GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — develop ( eba62c...1be0d2 )
by Lonnie
08:09
created
application/helpers/markdown_helper.php 2 patches
Doc Comments   +18 added lines patch added patch discarded remove patch
@@ -1122,6 +1122,10 @@  discard block
 block discarded – undo
1122 1122
 
1123 1123
     var $list_level = 0;
1124 1124
 
1125
+    /**
1126
+     * @param string $list_str
1127
+     * @param string $marker_any_re
1128
+     */
1125 1129
     function processListItems($list_str, $marker_any_re)
1126 1130
     {
1127 1131
         #
@@ -1234,6 +1238,9 @@  discard block
 block discarded – undo
1234 1238
         return "\n\n" . $this->hashBlock($codeblock) . "\n\n";
1235 1239
     }
1236 1240
 
1241
+    /**
1242
+     * @param string $code
1243
+     */
1237 1244
     function makeCodeSpan($code)
1238 1245
     {
1239 1246
         #
@@ -1751,6 +1758,9 @@  discard block
 block discarded – undo
1751 1758
     # hanlde UTF-8 if the default function does not exist.
1752 1759
     var $utf8_strlen = 'mb_strlen';
1753 1760
 
1761
+    /**
1762
+     * @param string $text
1763
+     */
1754 1764
     function detab($text)
1755 1765
     {
1756 1766
         #
@@ -2340,6 +2350,11 @@  discard block
 block discarded – undo
2340 2350
         return array($parsed, $text);
2341 2351
     }
2342 2352
 
2353
+    /**
2354
+     * @param string $text
2355
+     * @param string $hash_method
2356
+     * @param boolean $md_attr
2357
+     */
2343 2358
     function _hashHTMLBlocks_inHTML($text, $hash_method, $md_attr)
2344 2359
     {
2345 2360
         #
@@ -3078,6 +3093,9 @@  discard block
 block discarded – undo
3078 3093
         return $this->hashBlock($result) . "\n\n";
3079 3094
     }
3080 3095
 
3096
+    /**
3097
+     * @return string
3098
+     */
3081 3099
     function processDefListItems($list_str)
3082 3100
     {
3083 3101
         #
Please login to merge, or discard this patch.
Indentation   +2725 added lines, -2725 removed lines patch added patch discarded remove patch
@@ -56,15 +56,15 @@  discard block
 block discarded – undo
56 56
 #
57 57
 # Initialize the parser and return the result of its transform method.
58 58
 #
59
-    # Setup static parser variable.
60
-    static $parser;
61
-    if (!isset($parser)) {
62
-        $parser_class = MARKDOWN_PARSER_CLASS;
63
-        $parser       = new $parser_class;
64
-    }
65
-
66
-    # Transform text using parser.
67
-    return $parser->transform($text);
59
+	# Setup static parser variable.
60
+	static $parser;
61
+	if (!isset($parser)) {
62
+		$parser_class = MARKDOWN_PARSER_CLASS;
63
+		$parser       = new $parser_class;
64
+	}
65
+
66
+	# Transform text using parser.
67
+	return $parser->transform($text);
68 68
 }
69 69
 
70 70
 ### WordPress Plugin Interface ###
@@ -80,120 +80,120 @@  discard block
 block discarded – undo
80 80
 */
81 81
 
82 82
 if (isset($wp_version)) {
83
-    # More details about how it works here:
84
-    # <http://michelf.ca/weblog/2005/wordpress-text-flow-vs-markdown/>
85
-
86
-    # Post content and excerpts
87
-    # - Remove WordPress paragraph generator.
88
-    # - Run Markdown on excerpt, then remove all tags.
89
-    # - Add paragraph tag around the excerpt, but remove it for the excerpt rss.
90
-    if (MARKDOWN_WP_POSTS) {
91
-        remove_filter('the_content', 'wpautop');
92
-        remove_filter('the_content_rss', 'wpautop');
93
-        remove_filter('the_excerpt', 'wpautop');
94
-        add_filter('the_content', 'mdwp_MarkdownPost', 6);
95
-        add_filter('the_content_rss', 'mdwp_MarkdownPost', 6);
96
-        add_filter('get_the_excerpt', 'mdwp_MarkdownPost', 6);
97
-        add_filter('get_the_excerpt', 'trim', 7);
98
-        add_filter('the_excerpt', 'mdwp_add_p');
99
-        add_filter('the_excerpt_rss', 'mdwp_strip_p');
100
-
101
-        remove_filter('content_save_pre', 'balanceTags', 50);
102
-        remove_filter('excerpt_save_pre', 'balanceTags', 50);
103
-        add_filter('the_content', 'balanceTags', 50);
104
-        add_filter('get_the_excerpt', 'balanceTags', 9);
105
-    }
106
-
107
-    # Add a footnote id prefix to posts when inside a loop.
108
-    function mdwp_MarkdownPost($text)
109
-    {
110
-        static $parser;
111
-        if (!$parser) {
112
-            $parser_class = MARKDOWN_PARSER_CLASS;
113
-            $parser       = new $parser_class;
114
-        }
115
-        if (is_single() || is_page() || is_feed()) {
116
-            $parser->fn_id_prefix = "";
117
-        } else {
118
-            $parser->fn_id_prefix = get_the_ID() . ".";
119
-        }
120
-        return $parser->transform($text);
121
-    }
122
-
123
-    # Comments
124
-    # - Remove WordPress paragraph generator.
125
-    # - Remove WordPress auto-link generator.
126
-    # - Scramble important tags before passing them to the kses filter.
127
-    # - Run Markdown on excerpt then remove paragraph tags.
128
-    if (MARKDOWN_WP_COMMENTS) {
129
-        remove_filter('comment_text', 'wpautop', 30);
130
-        remove_filter('comment_text', 'make_clickable');
131
-        add_filter('pre_comment_content', 'Markdown', 6);
132
-        add_filter('pre_comment_content', 'mdwp_hide_tags', 8);
133
-        add_filter('pre_comment_content', 'mdwp_show_tags', 12);
134
-        add_filter('get_comment_text', 'Markdown', 6);
135
-        add_filter('get_comment_excerpt', 'Markdown', 6);
136
-        add_filter('get_comment_excerpt', 'mdwp_strip_p', 7);
137
-
138
-        global $mdwp_hidden_tags, $mdwp_placeholders;
139
-        $mdwp_hidden_tags  = explode(
140
-            ' ',
141
-            '<p> </p> <pre> </pre> <ol> </ol> <ul> </ul> <li> </li>'
142
-        );
143
-        $mdwp_placeholders = explode(
144
-            ' ',
145
-            str_rot13(
146
-                'pEj07ZbbBZ U1kqgh4w4p pre2zmeN6K QTi31t9pre ol0MP1jzJR ' .
147
-                'ML5IjmbRol ulANi1NsGY J7zRLJqPul liA8ctl16T K9nhooUHli'
148
-            )
149
-        );
150
-    }
151
-
152
-    function mdwp_add_p($text)
153
-    {
154
-        if (!preg_match('{^$|^<(p|ul|ol|dl|pre|blockquote)>}i', $text)) {
155
-            $text = '<p>' . $text . '</p>';
156
-            $text = preg_replace('{\n{2,}}', "</p>\n\n<p>", $text);
157
-        }
158
-        return $text;
159
-    }
160
-
161
-    function mdwp_strip_p($t) { return preg_replace('{</?p>}i', '', $t); }
162
-
163
-    function mdwp_hide_tags($text)
164
-    {
165
-        global $mdwp_hidden_tags, $mdwp_placeholders;
166
-        return str_replace($mdwp_hidden_tags, $mdwp_placeholders, $text);
167
-    }
168
-
169
-    function mdwp_show_tags($text)
170
-    {
171
-        global $mdwp_hidden_tags, $mdwp_placeholders;
172
-        return str_replace($mdwp_placeholders, $mdwp_hidden_tags, $text);
173
-    }
83
+	# More details about how it works here:
84
+	# <http://michelf.ca/weblog/2005/wordpress-text-flow-vs-markdown/>
85
+
86
+	# Post content and excerpts
87
+	# - Remove WordPress paragraph generator.
88
+	# - Run Markdown on excerpt, then remove all tags.
89
+	# - Add paragraph tag around the excerpt, but remove it for the excerpt rss.
90
+	if (MARKDOWN_WP_POSTS) {
91
+		remove_filter('the_content', 'wpautop');
92
+		remove_filter('the_content_rss', 'wpautop');
93
+		remove_filter('the_excerpt', 'wpautop');
94
+		add_filter('the_content', 'mdwp_MarkdownPost', 6);
95
+		add_filter('the_content_rss', 'mdwp_MarkdownPost', 6);
96
+		add_filter('get_the_excerpt', 'mdwp_MarkdownPost', 6);
97
+		add_filter('get_the_excerpt', 'trim', 7);
98
+		add_filter('the_excerpt', 'mdwp_add_p');
99
+		add_filter('the_excerpt_rss', 'mdwp_strip_p');
100
+
101
+		remove_filter('content_save_pre', 'balanceTags', 50);
102
+		remove_filter('excerpt_save_pre', 'balanceTags', 50);
103
+		add_filter('the_content', 'balanceTags', 50);
104
+		add_filter('get_the_excerpt', 'balanceTags', 9);
105
+	}
106
+
107
+	# Add a footnote id prefix to posts when inside a loop.
108
+	function mdwp_MarkdownPost($text)
109
+	{
110
+		static $parser;
111
+		if (!$parser) {
112
+			$parser_class = MARKDOWN_PARSER_CLASS;
113
+			$parser       = new $parser_class;
114
+		}
115
+		if (is_single() || is_page() || is_feed()) {
116
+			$parser->fn_id_prefix = "";
117
+		} else {
118
+			$parser->fn_id_prefix = get_the_ID() . ".";
119
+		}
120
+		return $parser->transform($text);
121
+	}
122
+
123
+	# Comments
124
+	# - Remove WordPress paragraph generator.
125
+	# - Remove WordPress auto-link generator.
126
+	# - Scramble important tags before passing them to the kses filter.
127
+	# - Run Markdown on excerpt then remove paragraph tags.
128
+	if (MARKDOWN_WP_COMMENTS) {
129
+		remove_filter('comment_text', 'wpautop', 30);
130
+		remove_filter('comment_text', 'make_clickable');
131
+		add_filter('pre_comment_content', 'Markdown', 6);
132
+		add_filter('pre_comment_content', 'mdwp_hide_tags', 8);
133
+		add_filter('pre_comment_content', 'mdwp_show_tags', 12);
134
+		add_filter('get_comment_text', 'Markdown', 6);
135
+		add_filter('get_comment_excerpt', 'Markdown', 6);
136
+		add_filter('get_comment_excerpt', 'mdwp_strip_p', 7);
137
+
138
+		global $mdwp_hidden_tags, $mdwp_placeholders;
139
+		$mdwp_hidden_tags  = explode(
140
+			' ',
141
+			'<p> </p> <pre> </pre> <ol> </ol> <ul> </ul> <li> </li>'
142
+		);
143
+		$mdwp_placeholders = explode(
144
+			' ',
145
+			str_rot13(
146
+				'pEj07ZbbBZ U1kqgh4w4p pre2zmeN6K QTi31t9pre ol0MP1jzJR ' .
147
+				'ML5IjmbRol ulANi1NsGY J7zRLJqPul liA8ctl16T K9nhooUHli'
148
+			)
149
+		);
150
+	}
151
+
152
+	function mdwp_add_p($text)
153
+	{
154
+		if (!preg_match('{^$|^<(p|ul|ol|dl|pre|blockquote)>}i', $text)) {
155
+			$text = '<p>' . $text . '</p>';
156
+			$text = preg_replace('{\n{2,}}', "</p>\n\n<p>", $text);
157
+		}
158
+		return $text;
159
+	}
160
+
161
+	function mdwp_strip_p($t) { return preg_replace('{</?p>}i', '', $t); }
162
+
163
+	function mdwp_hide_tags($text)
164
+	{
165
+		global $mdwp_hidden_tags, $mdwp_placeholders;
166
+		return str_replace($mdwp_hidden_tags, $mdwp_placeholders, $text);
167
+	}
168
+
169
+	function mdwp_show_tags($text)
170
+	{
171
+		global $mdwp_hidden_tags, $mdwp_placeholders;
172
+		return str_replace($mdwp_placeholders, $mdwp_hidden_tags, $text);
173
+	}
174 174
 }
175 175
 
176 176
 ### bBlog Plugin Info ###
177 177
 
178 178
 function identify_modifier_markdown()
179 179
 {
180
-    return array(
181
-        'name'        => 'markdown',
182
-        'type'        => 'modifier',
183
-        'nicename'    => 'PHP Markdown Extra',
184
-        'description' => 'A text-to-HTML conversion tool for web writers',
185
-        'authors'     => 'Michel Fortin and John Gruber',
186
-        'licence'     => 'GPL',
187
-        'version'     => MARKDOWNEXTRA_VERSION,
188
-        'help'        => '<a href="http://daringfireball.net/projects/markdown/syntax">Markdown syntax</a> allows you to write using an easy-to-read, easy-to-write plain text format. Based on the original Perl version by <a href="http://daringfireball.net/">John Gruber</a>. <a href="http://michelf.ca/projects/php-markdown/">More...</a>',
189
-    );
180
+	return array(
181
+		'name'        => 'markdown',
182
+		'type'        => 'modifier',
183
+		'nicename'    => 'PHP Markdown Extra',
184
+		'description' => 'A text-to-HTML conversion tool for web writers',
185
+		'authors'     => 'Michel Fortin and John Gruber',
186
+		'licence'     => 'GPL',
187
+		'version'     => MARKDOWNEXTRA_VERSION,
188
+		'help'        => '<a href="http://daringfireball.net/projects/markdown/syntax">Markdown syntax</a> allows you to write using an easy-to-read, easy-to-write plain text format. Based on the original Perl version by <a href="http://daringfireball.net/">John Gruber</a>. <a href="http://michelf.ca/projects/php-markdown/">More...</a>',
189
+	);
190 190
 }
191 191
 
192 192
 ### Smarty Modifier Interface ###
193 193
 
194 194
 function smarty_modifier_markdown($text)
195 195
 {
196
-    return Markdown($text);
196
+	return Markdown($text);
197 197
 }
198 198
 
199 199
 ### Textile Compatibility Mode ###
@@ -201,32 +201,32 @@  discard block
 block discarded – undo
201 201
 # Rename this file to "classTextile.php" and it can replace Textile everywhere.
202 202
 
203 203
 if (strcasecmp(substr(__FILE__, - 16), "classTextile.php") == 0) {
204
-    # Try to include PHP SmartyPants. Should be in the same directory.
205
-    @include_once 'smartypants.php';
206
-
207
-    # Fake Textile class. It calls Markdown instead.
208
-    class Textile
209
-    {
210
-        function TextileThis($text, $lite = '', $encode = '')
211
-        {
212
-            if ($lite == '' && $encode == '') {
213
-                $text = Markdown($text);
214
-            }
215
-            if (function_exists('SmartyPants')) {
216
-                $text = SmartyPants($text);
217
-            }
218
-            return $text;
219
-        }
220
-
221
-        # Fake restricted version: restrictions are not supported for now.
222
-        function TextileRestricted($text, $lite = '', $noimage = '')
223
-        {
224
-            return $this->TextileThis($text, $lite);
225
-        }
226
-
227
-        # Workaround to ensure compatibility with TextPattern 4.0.3.
228
-        function blockLite($text) { return $text; }
229
-    }
204
+	# Try to include PHP SmartyPants. Should be in the same directory.
205
+	@include_once 'smartypants.php';
206
+
207
+	# Fake Textile class. It calls Markdown instead.
208
+	class Textile
209
+	{
210
+		function TextileThis($text, $lite = '', $encode = '')
211
+		{
212
+			if ($lite == '' && $encode == '') {
213
+				$text = Markdown($text);
214
+			}
215
+			if (function_exists('SmartyPants')) {
216
+				$text = SmartyPants($text);
217
+			}
218
+			return $text;
219
+		}
220
+
221
+		# Fake restricted version: restrictions are not supported for now.
222
+		function TextileRestricted($text, $lite = '', $noimage = '')
223
+		{
224
+			return $this->TextileThis($text, $lite);
225
+		}
226
+
227
+		# Workaround to ensure compatibility with TextPattern 4.0.3.
228
+		function blockLite($text) { return $text; }
229
+	}
230 230
 }
231 231
 
232 232
 #
@@ -236,156 +236,156 @@  discard block
 block discarded – undo
236 236
 class Markdown_Parser
237 237
 {
238 238
 
239
-    ### Configuration Variables ###
239
+	### Configuration Variables ###
240 240
 
241
-    # Change to ">" for HTML output.
242
-    var $empty_element_suffix = MARKDOWN_EMPTY_ELEMENT_SUFFIX;
241
+	# Change to ">" for HTML output.
242
+	var $empty_element_suffix = MARKDOWN_EMPTY_ELEMENT_SUFFIX;
243 243
 
244
-    var $tab_width = MARKDOWN_TAB_WIDTH;
244
+	var $tab_width = MARKDOWN_TAB_WIDTH;
245 245
 
246
-    # Change to `true` to disallow markup or entities.
247
-    var $no_markup = false;
246
+	# Change to `true` to disallow markup or entities.
247
+	var $no_markup = false;
248 248
 
249
-    var $no_entities = false;
249
+	var $no_entities = false;
250 250
 
251
-    # Predefined urls and titles for reference links and images.
252
-    var $predef_urls = array();
251
+	# Predefined urls and titles for reference links and images.
252
+	var $predef_urls = array();
253 253
 
254
-    var $predef_titles = array();
254
+	var $predef_titles = array();
255 255
 
256
-    ### Parser Implementation ###
256
+	### Parser Implementation ###
257 257
 
258
-    # Regex to match balanced [brackets].
259
-    # Needed to insert a maximum bracked depth while converting to PHP.
260
-    var $nested_brackets_depth = 6;
258
+	# Regex to match balanced [brackets].
259
+	# Needed to insert a maximum bracked depth while converting to PHP.
260
+	var $nested_brackets_depth = 6;
261 261
 
262
-    var $nested_brackets_re;
262
+	var $nested_brackets_re;
263 263
 
264
-    var $nested_url_parenthesis_depth = 4;
264
+	var $nested_url_parenthesis_depth = 4;
265 265
 
266
-    var $nested_url_parenthesis_re;
266
+	var $nested_url_parenthesis_re;
267 267
 
268
-    # Table of hash values for escaped characters:
269
-    var $escape_chars = '\`*_{}[]()>#+-.!';
268
+	# Table of hash values for escaped characters:
269
+	var $escape_chars = '\`*_{}[]()>#+-.!';
270 270
 
271
-    var $escape_chars_re;
271
+	var $escape_chars_re;
272 272
 
273
-    function __construct()
274
-    {
275
-        #
276
-        # Constructor function. Initialize appropriate member variables.
277
-        #
278
-        $this->_initDetab();
279
-        $this->prepareItalicsAndBold();
273
+	function __construct()
274
+	{
275
+		#
276
+		# Constructor function. Initialize appropriate member variables.
277
+		#
278
+		$this->_initDetab();
279
+		$this->prepareItalicsAndBold();
280 280
 
281
-        $this->nested_brackets_re =
282
-            str_repeat('(?>[^\[\]]+|\[', $this->nested_brackets_depth) .
283
-            str_repeat('\])*', $this->nested_brackets_depth);
281
+		$this->nested_brackets_re =
282
+			str_repeat('(?>[^\[\]]+|\[', $this->nested_brackets_depth) .
283
+			str_repeat('\])*', $this->nested_brackets_depth);
284 284
 
285
-        $this->nested_url_parenthesis_re =
286
-            str_repeat('(?>[^()\s]+|\(', $this->nested_url_parenthesis_depth) .
287
-            str_repeat('(?>\)))*', $this->nested_url_parenthesis_depth);
285
+		$this->nested_url_parenthesis_re =
286
+			str_repeat('(?>[^()\s]+|\(', $this->nested_url_parenthesis_depth) .
287
+			str_repeat('(?>\)))*', $this->nested_url_parenthesis_depth);
288 288
 
289
-        $this->escape_chars_re = '[' . preg_quote($this->escape_chars) . ']';
289
+		$this->escape_chars_re = '[' . preg_quote($this->escape_chars) . ']';
290 290
 
291
-        # Sort document, block, and span gamut in ascendent priority order.
292
-        asort($this->document_gamut);
293
-        asort($this->block_gamut);
294
-        asort($this->span_gamut);
295
-    }
291
+		# Sort document, block, and span gamut in ascendent priority order.
292
+		asort($this->document_gamut);
293
+		asort($this->block_gamut);
294
+		asort($this->span_gamut);
295
+	}
296 296
 
297
-    # Internal hashes used during transformation.
298
-    var $urls = array();
297
+	# Internal hashes used during transformation.
298
+	var $urls = array();
299 299
 
300
-    var $titles = array();
300
+	var $titles = array();
301 301
 
302
-    var $html_hashes = array();
302
+	var $html_hashes = array();
303 303
 
304
-    # Status flag to avoid invalid nesting.
305
-    var $in_anchor = false;
304
+	# Status flag to avoid invalid nesting.
305
+	var $in_anchor = false;
306 306
 
307
-    function setup()
308
-    {
309
-        #
310
-        # Called before the transformation process starts to setup parser
311
-        # states.
312
-        #
313
-        # Clear global hashes.
314
-        $this->urls        = $this->predef_urls;
315
-        $this->titles      = $this->predef_titles;
316
-        $this->html_hashes = array();
307
+	function setup()
308
+	{
309
+		#
310
+		# Called before the transformation process starts to setup parser
311
+		# states.
312
+		#
313
+		# Clear global hashes.
314
+		$this->urls        = $this->predef_urls;
315
+		$this->titles      = $this->predef_titles;
316
+		$this->html_hashes = array();
317 317
 
318
-        $this->in_anchor = false;
319
-    }
318
+		$this->in_anchor = false;
319
+	}
320 320
 
321
-    function teardown()
322
-    {
323
-        #
324
-        # Called after the transformation process to clear any variable
325
-        # which may be taking up memory unnecessarly.
326
-        #
327
-        $this->urls        = array();
328
-        $this->titles      = array();
329
-        $this->html_hashes = array();
330
-    }
321
+	function teardown()
322
+	{
323
+		#
324
+		# Called after the transformation process to clear any variable
325
+		# which may be taking up memory unnecessarly.
326
+		#
327
+		$this->urls        = array();
328
+		$this->titles      = array();
329
+		$this->html_hashes = array();
330
+	}
331 331
 
332
-    function transform($text)
333
-    {
334
-        #
335
-        # Main function. Performs some preprocessing on the input text
336
-        # and pass it through the document gamut.
337
-        #
338
-        $this->setup();
332
+	function transform($text)
333
+	{
334
+		#
335
+		# Main function. Performs some preprocessing on the input text
336
+		# and pass it through the document gamut.
337
+		#
338
+		$this->setup();
339 339
 
340
-        # Remove UTF-8 BOM and marker character in input, if present.
341
-        $text = preg_replace('{^\xEF\xBB\xBF|\x1A}', '', $text);
340
+		# Remove UTF-8 BOM and marker character in input, if present.
341
+		$text = preg_replace('{^\xEF\xBB\xBF|\x1A}', '', $text);
342 342
 
343
-        # Standardize line endings:
344
-        #   DOS to Unix and Mac to Unix
345
-        $text = preg_replace('{\r\n?}', "\n", $text);
343
+		# Standardize line endings:
344
+		#   DOS to Unix and Mac to Unix
345
+		$text = preg_replace('{\r\n?}', "\n", $text);
346 346
 
347
-        # Make sure $text ends with a couple of newlines:
348
-        $text .= "\n\n";
349
-
350
-        # Convert all tabs to spaces.
351
-        $text = $this->detab($text);
352
-
353
-        # Turn block-level HTML blocks into hash entries
354
-        $text = $this->hashHTMLBlocks($text);
355
-
356
-        # Strip any lines consisting only of spaces and tabs.
357
-        # This makes subsequent regexen easier to write, because we can
358
-        # match consecutive blank lines with /\n+/ instead of something
359
-        # contorted like /[ ]*\n+/ .
360
-        $text = preg_replace('/^[ ]+$/m', '', $text);
361
-
362
-        # Run document gamut methods.
363
-        foreach ($this->document_gamut as $method => $priority) {
364
-            $text = $this->$method($text);
365
-        }
366
-
367
-        $this->teardown();
368
-
369
-        return $text . "\n";
370
-    }
371
-
372
-    var $document_gamut = array(
373
-        # Strip link definitions, store in hashes.
374
-        "stripLinkDefinitions" => 20,
375
-        "runBasicBlockGamut"   => 30,
376
-    );
377
-
378
-    function stripLinkDefinitions($text)
379
-    {
380
-        #
381
-        # Strips link definitions from text, stores the URLs and titles in
382
-        # hash references.
383
-        #
384
-        $less_than_tab = $this->tab_width - 1;
385
-
386
-        # Link defs are in the form: ^[id]: url "optional title"
387
-        $text = preg_replace_callback(
388
-            '{
347
+		# Make sure $text ends with a couple of newlines:
348
+		$text .= "\n\n";
349
+
350
+		# Convert all tabs to spaces.
351
+		$text = $this->detab($text);
352
+
353
+		# Turn block-level HTML blocks into hash entries
354
+		$text = $this->hashHTMLBlocks($text);
355
+
356
+		# Strip any lines consisting only of spaces and tabs.
357
+		# This makes subsequent regexen easier to write, because we can
358
+		# match consecutive blank lines with /\n+/ instead of something
359
+		# contorted like /[ ]*\n+/ .
360
+		$text = preg_replace('/^[ ]+$/m', '', $text);
361
+
362
+		# Run document gamut methods.
363
+		foreach ($this->document_gamut as $method => $priority) {
364
+			$text = $this->$method($text);
365
+		}
366
+
367
+		$this->teardown();
368
+
369
+		return $text . "\n";
370
+	}
371
+
372
+	var $document_gamut = array(
373
+		# Strip link definitions, store in hashes.
374
+		"stripLinkDefinitions" => 20,
375
+		"runBasicBlockGamut"   => 30,
376
+	);
377
+
378
+	function stripLinkDefinitions($text)
379
+	{
380
+		#
381
+		# Strips link definitions from text, stores the URLs and titles in
382
+		# hash references.
383
+		#
384
+		$less_than_tab = $this->tab_width - 1;
385
+
386
+		# Link defs are in the form: ^[id]: url "optional title"
387
+		$text = preg_replace_callback(
388
+			'{
389 389
 							^[ ]{0,' . $less_than_tab . '}\[(.+)\][ ]?:	# id = $1
390 390
 							  [ ]*
391 391
 							  \n?				# maybe *one* newline
@@ -407,51 +407,51 @@  discard block
 block discarded – undo
407 407
 							)?	# title is optional
408 408
 							(?:\n+|\Z)
409 409
 			}xm',
410
-            array(&$this, '_stripLinkDefinitions_callback'),
411
-            $text
412
-        );
413
-        return $text;
414
-    }
415
-
416
-    function _stripLinkDefinitions_callback($matches)
417
-    {
418
-        $link_id                = strtolower($matches[1]);
419
-        $url                    = $matches[2] == '' ? $matches[3] : $matches[2];
420
-        $this->urls[$link_id]   = $url;
421
-        $this->titles[$link_id] =& $matches[4];
422
-        return ''; # String that will replace the block
423
-    }
424
-
425
-    function hashHTMLBlocks($text)
426
-    {
427
-        if ($this->no_markup) {
428
-            return $text;
429
-        }
430
-
431
-        $less_than_tab = $this->tab_width - 1;
432
-
433
-        # Hashify HTML blocks:
434
-        # We only want to do this for block-level HTML tags, such as headers,
435
-        # lists, and tables. That's because we still want to wrap <p>s around
436
-        # "paragraphs" that are wrapped in non-block-level tags, such as anchors,
437
-        # phrase emphasis, and spans. The list of tags we're looking for is
438
-        # hard-coded:
439
-        #
440
-        # *  List "a" is made of tags which can be both inline or block-level.
441
-        #    These will be treated block-level when the start tag is alone on
442
-        #    its line, otherwise they're not matched here and will be taken as
443
-        #    inline later.
444
-        # *  List "b" is made of tags which are always block-level;
445
-        #
446
-        $block_tags_a_re = 'ins|del';
447
-        $block_tags_b_re = 'p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|address|' .
448
-                           'script|noscript|form|fieldset|iframe|math|svg|' .
449
-                           'article|section|nav|aside|hgroup|header|footer|' .
450
-                           'figure';
451
-
452
-        # Regular expression for the content of a block tag.
453
-        $nested_tags_level = 4;
454
-        $attr              = '
410
+			array(&$this, '_stripLinkDefinitions_callback'),
411
+			$text
412
+		);
413
+		return $text;
414
+	}
415
+
416
+	function _stripLinkDefinitions_callback($matches)
417
+	{
418
+		$link_id                = strtolower($matches[1]);
419
+		$url                    = $matches[2] == '' ? $matches[3] : $matches[2];
420
+		$this->urls[$link_id]   = $url;
421
+		$this->titles[$link_id] =& $matches[4];
422
+		return ''; # String that will replace the block
423
+	}
424
+
425
+	function hashHTMLBlocks($text)
426
+	{
427
+		if ($this->no_markup) {
428
+			return $text;
429
+		}
430
+
431
+		$less_than_tab = $this->tab_width - 1;
432
+
433
+		# Hashify HTML blocks:
434
+		# We only want to do this for block-level HTML tags, such as headers,
435
+		# lists, and tables. That's because we still want to wrap <p>s around
436
+		# "paragraphs" that are wrapped in non-block-level tags, such as anchors,
437
+		# phrase emphasis, and spans. The list of tags we're looking for is
438
+		# hard-coded:
439
+		#
440
+		# *  List "a" is made of tags which can be both inline or block-level.
441
+		#    These will be treated block-level when the start tag is alone on
442
+		#    its line, otherwise they're not matched here and will be taken as
443
+		#    inline later.
444
+		# *  List "b" is made of tags which are always block-level;
445
+		#
446
+		$block_tags_a_re = 'ins|del';
447
+		$block_tags_b_re = 'p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|address|' .
448
+						   'script|noscript|form|fieldset|iframe|math|svg|' .
449
+						   'article|section|nav|aside|hgroup|header|footer|' .
450
+						   'figure';
451
+
452
+		# Regular expression for the content of a block tag.
453
+		$nested_tags_level = 4;
454
+		$attr              = '
455 455
 			(?>				# optional tag attributes
456 456
 			  \s			# starts with whitespace
457 457
 			  (?>
@@ -465,9 +465,9 @@  discard block
 block discarded – undo
465 465
 			  )*
466 466
 			)?
467 467
 			';
468
-        $content           =
469
-            str_repeat(
470
-                '
468
+		$content           =
469
+			str_repeat(
470
+				'
471 471
 				(?>
472 472
 				  [^<]+			# content without tag
473 473
 				|
@@ -477,34 +477,34 @@  discard block
 block discarded – undo
477 477
 					  />
478 478
 					|
479 479
 					  >',
480
-                $nested_tags_level
481
-            ) . # end of opening tag
482
-            '.*?' . # last level nested tag content
483
-            str_repeat(
484
-                '
480
+				$nested_tags_level
481
+			) . # end of opening tag
482
+			'.*?' . # last level nested tag content
483
+			str_repeat(
484
+				'
485 485
 					  </\2\s*>	# closing nested tag
486 486
 					)
487 487
 				  |
488 488
 					<(?!/\2\s*>	# other tags with a different name
489 489
 				  )
490 490
 				)*',
491
-                $nested_tags_level
492
-            );
493
-        $content2          = str_replace('\2', '\3', $content);
494
-
495
-        # First, look for nested blocks, e.g.:
496
-        # 	<div>
497
-        # 		<div>
498
-        # 		tags for inner block must be indented.
499
-        # 		</div>
500
-        # 	</div>
501
-        #
502
-        # The outermost tags must start at the left margin for this to match, and
503
-        # the inner nested divs must be indented.
504
-        # We need to do this before the next, more liberal match, because the next
505
-        # match will start at the first `<div>` and stop at the first `</div>`.
506
-        $text = preg_replace_callback(
507
-            '{(?>
491
+				$nested_tags_level
492
+			);
493
+		$content2          = str_replace('\2', '\3', $content);
494
+
495
+		# First, look for nested blocks, e.g.:
496
+		# 	<div>
497
+		# 		<div>
498
+		# 		tags for inner block must be indented.
499
+		# 		</div>
500
+		# 	</div>
501
+		#
502
+		# The outermost tags must start at the left margin for this to match, and
503
+		# the inner nested divs must be indented.
504
+		# We need to do this before the next, more liberal match, because the next
505
+		# match will start at the first `<div>` and stop at the first `</div>`.
506
+		$text = preg_replace_callback(
507
+			'{(?>
508 508
 			(?>
509 509
 				(?<=\n\n)		# Starting after a blank line
510 510
 				|				# or
@@ -565,100 +565,100 @@  discard block
 block discarded – undo
565 565
 
566 566
 			)
567 567
 			)}Sxmi',
568
-            array(&$this, '_hashHTMLBlocks_callback'),
569
-            $text
570
-        );
571
-
572
-        return $text;
573
-    }
574
-
575
-    function _hashHTMLBlocks_callback($matches)
576
-    {
577
-        $text = $matches[1];
578
-        $key  = $this->hashBlock($text);
579
-        return "\n\n$key\n\n";
580
-    }
581
-
582
-    function hashPart($text, $boundary = 'X')
583
-    {
584
-        #
585
-        # Called whenever a tag must be hashed when a function insert an atomic
586
-        # element in the text stream. Passing $text to through this function gives
587
-        # a unique text-token which will be reverted back when calling unhash.
588
-        #
589
-        # The $boundary argument specify what character should be used to surround
590
-        # the token. By convension, "B" is used for block elements that needs not
591
-        # to be wrapped into paragraph tags at the end, ":" is used for elements
592
-        # that are word separators and "X" is used in the general case.
593
-        #
594
-        # Swap back any tag hash found in $text so we do not have to `unhash`
595
-        # multiple times at the end.
596
-        $text = $this->unhash($text);
597
-
598
-        # Then hash the block.
599
-        static $i = 0;
600
-        $key                     = "$boundary\x1A" . ++ $i . $boundary;
601
-        $this->html_hashes[$key] = $text;
602
-        return $key; # String that will replace the tag.
603
-    }
604
-
605
-    function hashBlock($text)
606
-    {
607
-        #
608
-        # Shortcut function for hashPart with block-level boundaries.
609
-        #
610
-        return $this->hashPart($text, 'B');
611
-    }
612
-
613
-    var $block_gamut = array(
614
-        #
615
-        # These are all the transformations that form block-level
616
-        # tags like paragraphs, headers, and list items.
617
-        #
618
-        "doHeaders"         => 10,
619
-        "doHorizontalRules" => 20,
620
-        "doLists"           => 40,
621
-        "doCodeBlocks"      => 50,
622
-        "doBlockQuotes"     => 60,
623
-    );
624
-
625
-    function runBlockGamut($text)
626
-    {
627
-        #
628
-        # Run block gamut tranformations.
629
-        #
630
-        # We need to escape raw HTML in Markdown source before doing anything
631
-        # else. This need to be done for each block, and not only at the
632
-        # begining in the Markdown function since hashed blocks can be part of
633
-        # list items and could have been indented. Indented blocks would have
634
-        # been seen as a code block in a previous pass of hashHTMLBlocks.
635
-        $text = $this->hashHTMLBlocks($text);
636
-
637
-        return $this->runBasicBlockGamut($text);
638
-    }
639
-
640
-    function runBasicBlockGamut($text)
641
-    {
642
-        #
643
-        # Run block gamut tranformations, without hashing HTML blocks. This is
644
-        # useful when HTML blocks are known to be already hashed, like in the first
645
-        # whole-document pass.
646
-        #
647
-        foreach ($this->block_gamut as $method => $priority) {
648
-            $text = $this->$method($text);
649
-        }
650
-
651
-        # Finally form paragraph and restore hashed blocks.
652
-        $text = $this->formParagraphs($text);
653
-
654
-        return $text;
655
-    }
656
-
657
-    function doHorizontalRules($text)
658
-    {
659
-        # Do Horizontal Rules:
660
-        return preg_replace(
661
-            '{
568
+			array(&$this, '_hashHTMLBlocks_callback'),
569
+			$text
570
+		);
571
+
572
+		return $text;
573
+	}
574
+
575
+	function _hashHTMLBlocks_callback($matches)
576
+	{
577
+		$text = $matches[1];
578
+		$key  = $this->hashBlock($text);
579
+		return "\n\n$key\n\n";
580
+	}
581
+
582
+	function hashPart($text, $boundary = 'X')
583
+	{
584
+		#
585
+		# Called whenever a tag must be hashed when a function insert an atomic
586
+		# element in the text stream. Passing $text to through this function gives
587
+		# a unique text-token which will be reverted back when calling unhash.
588
+		#
589
+		# The $boundary argument specify what character should be used to surround
590
+		# the token. By convension, "B" is used for block elements that needs not
591
+		# to be wrapped into paragraph tags at the end, ":" is used for elements
592
+		# that are word separators and "X" is used in the general case.
593
+		#
594
+		# Swap back any tag hash found in $text so we do not have to `unhash`
595
+		# multiple times at the end.
596
+		$text = $this->unhash($text);
597
+
598
+		# Then hash the block.
599
+		static $i = 0;
600
+		$key                     = "$boundary\x1A" . ++ $i . $boundary;
601
+		$this->html_hashes[$key] = $text;
602
+		return $key; # String that will replace the tag.
603
+	}
604
+
605
+	function hashBlock($text)
606
+	{
607
+		#
608
+		# Shortcut function for hashPart with block-level boundaries.
609
+		#
610
+		return $this->hashPart($text, 'B');
611
+	}
612
+
613
+	var $block_gamut = array(
614
+		#
615
+		# These are all the transformations that form block-level
616
+		# tags like paragraphs, headers, and list items.
617
+		#
618
+		"doHeaders"         => 10,
619
+		"doHorizontalRules" => 20,
620
+		"doLists"           => 40,
621
+		"doCodeBlocks"      => 50,
622
+		"doBlockQuotes"     => 60,
623
+	);
624
+
625
+	function runBlockGamut($text)
626
+	{
627
+		#
628
+		# Run block gamut tranformations.
629
+		#
630
+		# We need to escape raw HTML in Markdown source before doing anything
631
+		# else. This need to be done for each block, and not only at the
632
+		# begining in the Markdown function since hashed blocks can be part of
633
+		# list items and could have been indented. Indented blocks would have
634
+		# been seen as a code block in a previous pass of hashHTMLBlocks.
635
+		$text = $this->hashHTMLBlocks($text);
636
+
637
+		return $this->runBasicBlockGamut($text);
638
+	}
639
+
640
+	function runBasicBlockGamut($text)
641
+	{
642
+		#
643
+		# Run block gamut tranformations, without hashing HTML blocks. This is
644
+		# useful when HTML blocks are known to be already hashed, like in the first
645
+		# whole-document pass.
646
+		#
647
+		foreach ($this->block_gamut as $method => $priority) {
648
+			$text = $this->$method($text);
649
+		}
650
+
651
+		# Finally form paragraph and restore hashed blocks.
652
+		$text = $this->formParagraphs($text);
653
+
654
+		return $text;
655
+	}
656
+
657
+	function doHorizontalRules($text)
658
+	{
659
+		# Do Horizontal Rules:
660
+		return preg_replace(
661
+			'{
662 662
 				^[ ]{0,3}	# Leading space
663 663
 				([-*_])		# $1: First marker
664 664
 				(?>			# Repeated marker group
@@ -668,74 +668,74 @@  discard block
 block discarded – undo
668 668
 				[ ]*		# Tailing spaces
669 669
 				$			# End of line.
670 670
 			}mx',
671
-            "\n" . $this->hashBlock("<hr$this->empty_element_suffix") . "\n",
672
-            $text
673
-        );
674
-    }
675
-
676
-    var $span_gamut = array(
677
-        #
678
-        # These are all the transformations that occur *within* block-level
679
-        # tags like paragraphs, headers, and list items.
680
-        #
681
-        # Process character escapes, code spans, and inline HTML
682
-        # in one shot.
683
-        "parseSpan"           => - 30,
684
-        # Process anchor and image tags. Images must come first,
685
-        # because ![foo][f] looks like an anchor.
686
-        "doImages"            => 10,
687
-        "doAnchors"           => 20,
688
-        # Make links out of things like `<http://example.com/>`
689
-        # Must come after doAnchors, because you can use < and >
690
-        # delimiters in inline links like [this](<url>).
691
-        "doAutoLinks"         => 30,
692
-        "encodeAmpsAndAngles" => 40,
693
-        "doItalicsAndBold"    => 50,
694
-        "doHardBreaks"        => 60,
695
-    );
696
-
697
-    function runSpanGamut($text)
698
-    {
699
-        #
700
-        # Run span gamut tranformations.
701
-        #
702
-        foreach ($this->span_gamut as $method => $priority) {
703
-            $text = $this->$method($text);
704
-        }
705
-
706
-        return $text;
707
-    }
708
-
709
-    function doHardBreaks($text)
710
-    {
711
-        # Do hard breaks:
712
-        return preg_replace_callback(
713
-            '/ {2,}\n/',
714
-            array(&$this, '_doHardBreaks_callback'),
715
-            $text
716
-        );
717
-    }
718
-
719
-    function _doHardBreaks_callback($matches)
720
-    {
721
-        return $this->hashPart("<br$this->empty_element_suffix\n");
722
-    }
723
-
724
-    function doAnchors($text)
725
-    {
726
-        #
727
-        # Turn Markdown link shortcuts into XHTML <a> tags.
728
-        #
729
-        if ($this->in_anchor) {
730
-            return $text;
731
-        }
732
-        $this->in_anchor = true;
733
-
734
-        #
735
-        # First, handle reference-style links: [link text] [id]
736
-        #
737
-        $text = preg_replace_callback(
738
-            '{
671
+			"\n" . $this->hashBlock("<hr$this->empty_element_suffix") . "\n",
672
+			$text
673
+		);
674
+	}
675
+
676
+	var $span_gamut = array(
677
+		#
678
+		# These are all the transformations that occur *within* block-level
679
+		# tags like paragraphs, headers, and list items.
680
+		#
681
+		# Process character escapes, code spans, and inline HTML
682
+		# in one shot.
683
+		"parseSpan"           => - 30,
684
+		# Process anchor and image tags. Images must come first,
685
+		# because ![foo][f] looks like an anchor.
686
+		"doImages"            => 10,
687
+		"doAnchors"           => 20,
688
+		# Make links out of things like `<http://example.com/>`
689
+		# Must come after doAnchors, because you can use < and >
690
+		# delimiters in inline links like [this](<url>).
691
+		"doAutoLinks"         => 30,
692
+		"encodeAmpsAndAngles" => 40,
693
+		"doItalicsAndBold"    => 50,
694
+		"doHardBreaks"        => 60,
695
+	);
696
+
697
+	function runSpanGamut($text)
698
+	{
699
+		#
700
+		# Run span gamut tranformations.
701
+		#
702
+		foreach ($this->span_gamut as $method => $priority) {
703
+			$text = $this->$method($text);
704
+		}
705
+
706
+		return $text;
707
+	}
708
+
709
+	function doHardBreaks($text)
710
+	{
711
+		# Do hard breaks:
712
+		return preg_replace_callback(
713
+			'/ {2,}\n/',
714
+			array(&$this, '_doHardBreaks_callback'),
715
+			$text
716
+		);
717
+	}
718
+
719
+	function _doHardBreaks_callback($matches)
720
+	{
721
+		return $this->hashPart("<br$this->empty_element_suffix\n");
722
+	}
723
+
724
+	function doAnchors($text)
725
+	{
726
+		#
727
+		# Turn Markdown link shortcuts into XHTML <a> tags.
728
+		#
729
+		if ($this->in_anchor) {
730
+			return $text;
731
+		}
732
+		$this->in_anchor = true;
733
+
734
+		#
735
+		# First, handle reference-style links: [link text] [id]
736
+		#
737
+		$text = preg_replace_callback(
738
+			'{
739 739
 			(					# wrap whole match in $1
740 740
 			  \[
741 741
 				(' . $this->nested_brackets_re . ')	# link text = $2
@@ -749,15 +749,15 @@  discard block
 block discarded – undo
749 749
 			  \]
750 750
 			)
751 751
 			}xs',
752
-            array(&$this, '_doAnchors_reference_callback'),
753
-            $text
754
-        );
755
-
756
-        #
757
-        # Next, inline-style links: [link text](url "optional title")
758
-        #
759
-        $text = preg_replace_callback(
760
-            '{
752
+			array(&$this, '_doAnchors_reference_callback'),
753
+			$text
754
+		);
755
+
756
+		#
757
+		# Next, inline-style links: [link text](url "optional title")
758
+		#
759
+		$text = preg_replace_callback(
760
+			'{
761 761
 			(				# wrap whole match in $1
762 762
 			  \[
763 763
 				(' . $this->nested_brackets_re . ')	# link text = $2
@@ -779,97 +779,97 @@  discard block
 block discarded – undo
779 779
 			  \)
780 780
 			)
781 781
 			}xs',
782
-            array(&$this, '_doAnchors_inline_callback'),
783
-            $text
784
-        );
785
-
786
-        #
787
-        # Last, handle reference-style shortcuts: [link text]
788
-        # These must come last in case you've also got [link text][1]
789
-        # or [link text](/foo)
790
-        #
791
-        $text = preg_replace_callback(
792
-            '{
782
+			array(&$this, '_doAnchors_inline_callback'),
783
+			$text
784
+		);
785
+
786
+		#
787
+		# Last, handle reference-style shortcuts: [link text]
788
+		# These must come last in case you've also got [link text][1]
789
+		# or [link text](/foo)
790
+		#
791
+		$text = preg_replace_callback(
792
+			'{
793 793
 			(					# wrap whole match in $1
794 794
 			  \[
795 795
 				([^\[\]]+)		# link text = $2; can\'t contain [ or ]
796 796
 			  \]
797 797
 			)
798 798
 			}xs',
799
-            array(&$this, '_doAnchors_reference_callback'),
800
-            $text
801
-        );
802
-
803
-        $this->in_anchor = false;
804
-        return $text;
805
-    }
806
-
807
-    function _doAnchors_reference_callback($matches)
808
-    {
809
-        $whole_match = $matches[1];
810
-        $link_text   = $matches[2];
811
-        $link_id     =& $matches[3];
812
-
813
-        if ($link_id == "") {
814
-            # for shortcut links like [this][] or [this].
815
-            $link_id = $link_text;
816
-        }
817
-
818
-        # lower-case and turn embedded newlines into spaces
819
-        $link_id = strtolower($link_id);
820
-        $link_id = preg_replace('{[ ]?\n}', ' ', $link_id);
821
-
822
-        if (isset($this->urls[$link_id])) {
823
-            $url = $this->urls[$link_id];
824
-            $url = $this->encodeAttribute($url);
825
-
826
-            $result = "<a href=\"$url\"";
827
-            if (isset($this->titles[$link_id])) {
828
-                $title = $this->titles[$link_id];
829
-                $title = $this->encodeAttribute($title);
830
-                $result .= " title=\"$title\"";
831
-            }
832
-
833
-            $link_text = $this->runSpanGamut($link_text);
834
-            $result .= ">$link_text</a>";
835
-            $result = $this->hashPart($result);
836
-        } else {
837
-            $result = $whole_match;
838
-        }
839
-        return $result;
840
-    }
841
-
842
-    function _doAnchors_inline_callback($matches)
843
-    {
844
-        $whole_match = $matches[1];
845
-        $link_text   = $this->runSpanGamut($matches[2]);
846
-        $url         = $matches[3] == '' ? $matches[4] : $matches[3];
847
-        $title       =& $matches[7];
848
-
849
-        $url = $this->encodeAttribute($url);
850
-
851
-        $result = "<a href=\"$url\"";
852
-        if (isset($title)) {
853
-            $title = $this->encodeAttribute($title);
854
-            $result .= " title=\"$title\"";
855
-        }
856
-
857
-        $link_text = $this->runSpanGamut($link_text);
858
-        $result .= ">$link_text</a>";
859
-
860
-        return $this->hashPart($result);
861
-    }
862
-
863
-    function doImages($text)
864
-    {
865
-        #
866
-        # Turn Markdown image shortcuts into <img> tags.
867
-        #
868
-        #
869
-        # First, handle reference-style labeled images: ![alt text][id]
870
-        #
871
-        $text = preg_replace_callback(
872
-            '{
799
+			array(&$this, '_doAnchors_reference_callback'),
800
+			$text
801
+		);
802
+
803
+		$this->in_anchor = false;
804
+		return $text;
805
+	}
806
+
807
+	function _doAnchors_reference_callback($matches)
808
+	{
809
+		$whole_match = $matches[1];
810
+		$link_text   = $matches[2];
811
+		$link_id     =& $matches[3];
812
+
813
+		if ($link_id == "") {
814
+			# for shortcut links like [this][] or [this].
815
+			$link_id = $link_text;
816
+		}
817
+
818
+		# lower-case and turn embedded newlines into spaces
819
+		$link_id = strtolower($link_id);
820
+		$link_id = preg_replace('{[ ]?\n}', ' ', $link_id);
821
+
822
+		if (isset($this->urls[$link_id])) {
823
+			$url = $this->urls[$link_id];
824
+			$url = $this->encodeAttribute($url);
825
+
826
+			$result = "<a href=\"$url\"";
827
+			if (isset($this->titles[$link_id])) {
828
+				$title = $this->titles[$link_id];
829
+				$title = $this->encodeAttribute($title);
830
+				$result .= " title=\"$title\"";
831
+			}
832
+
833
+			$link_text = $this->runSpanGamut($link_text);
834
+			$result .= ">$link_text</a>";
835
+			$result = $this->hashPart($result);
836
+		} else {
837
+			$result = $whole_match;
838
+		}
839
+		return $result;
840
+	}
841
+
842
+	function _doAnchors_inline_callback($matches)
843
+	{
844
+		$whole_match = $matches[1];
845
+		$link_text   = $this->runSpanGamut($matches[2]);
846
+		$url         = $matches[3] == '' ? $matches[4] : $matches[3];
847
+		$title       =& $matches[7];
848
+
849
+		$url = $this->encodeAttribute($url);
850
+
851
+		$result = "<a href=\"$url\"";
852
+		if (isset($title)) {
853
+			$title = $this->encodeAttribute($title);
854
+			$result .= " title=\"$title\"";
855
+		}
856
+
857
+		$link_text = $this->runSpanGamut($link_text);
858
+		$result .= ">$link_text</a>";
859
+
860
+		return $this->hashPart($result);
861
+	}
862
+
863
+	function doImages($text)
864
+	{
865
+		#
866
+		# Turn Markdown image shortcuts into <img> tags.
867
+		#
868
+		#
869
+		# First, handle reference-style labeled images: ![alt text][id]
870
+		#
871
+		$text = preg_replace_callback(
872
+			'{
873 873
 			(				# wrap whole match in $1
874 874
 			  !\[
875 875
 				(' . $this->nested_brackets_re . ')		# alt text = $2
@@ -884,16 +884,16 @@  discard block
 block discarded – undo
884 884
 
885 885
 			)
886 886
 			}xs',
887
-            array(&$this, '_doImages_reference_callback'),
888
-            $text
889
-        );
890
-
891
-        #
892
-        # Next, handle inline images:  ![alt text](url "optional title")
893
-        # Don't forget: encode * and _
894
-        #
895
-        $text = preg_replace_callback(
896
-            '{
887
+			array(&$this, '_doImages_reference_callback'),
888
+			$text
889
+		);
890
+
891
+		#
892
+		# Next, handle inline images:  ![alt text](url "optional title")
893
+		# Don't forget: encode * and _
894
+		#
895
+		$text = preg_replace_callback(
896
+			'{
897 897
 			(				# wrap whole match in $1
898 898
 			  !\[
899 899
 				(' . $this->nested_brackets_re . ')		# alt text = $2
@@ -916,85 +916,85 @@  discard block
 block discarded – undo
916 916
 			  \)
917 917
 			)
918 918
 			}xs',
919
-            array(&$this, '_doImages_inline_callback'),
920
-            $text
921
-        );
922
-
923
-        return $text;
924
-    }
925
-
926
-    function _doImages_reference_callback($matches)
927
-    {
928
-        $whole_match = $matches[1];
929
-        $alt_text    = $matches[2];
930
-        $link_id     = strtolower($matches[3]);
931
-
932
-        if ($link_id == "") {
933
-            $link_id = strtolower($alt_text); # for shortcut links like ![this][].
934
-        }
935
-
936
-        $alt_text = $this->encodeAttribute($alt_text);
937
-        if (isset($this->urls[$link_id])) {
938
-            $url    = $this->encodeAttribute($this->urls[$link_id]);
939
-            $result = "<img src=\"$url\" alt=\"$alt_text\"";
940
-            if (isset($this->titles[$link_id])) {
941
-                $title = $this->titles[$link_id];
942
-                $title = $this->encodeAttribute($title);
943
-                $result .= " title=\"$title\"";
944
-            }
945
-            $result .= $this->empty_element_suffix;
946
-            $result = $this->hashPart($result);
947
-        } else {
948
-            # If there's no such link ID, leave intact:
949
-            $result = $whole_match;
950
-        }
951
-
952
-        return $result;
953
-    }
954
-
955
-    function _doImages_inline_callback($matches)
956
-    {
957
-        $whole_match = $matches[1];
958
-        $alt_text    = $matches[2];
959
-        $url         = $matches[3] == '' ? $matches[4] : $matches[3];
960
-        $title       =& $matches[7];
961
-
962
-        $alt_text = $this->encodeAttribute($alt_text);
963
-        $url      = $this->encodeAttribute($url);
964
-        $result   = "<img src=\"$url\" alt=\"$alt_text\"";
965
-        if (isset($title)) {
966
-            $title = $this->encodeAttribute($title);
967
-            $result .= " title=\"$title\""; # $title already quoted
968
-        }
969
-        $result .= $this->empty_element_suffix;
970
-
971
-        return $this->hashPart($result);
972
-    }
973
-
974
-    function doHeaders($text)
975
-    {
976
-        # Setext-style headers:
977
-        #	  Header 1
978
-        #	  ========
979
-        #
980
-        #	  Header 2
981
-        #	  --------
982
-        #
983
-        $text = preg_replace_callback(
984
-            '{ ^(.+?)[ ]*\n(=+|-+)[ ]*\n+ }mx',
985
-            array(&$this, '_doHeaders_callback_setext'),
986
-            $text
987
-        );
988
-
989
-        # atx-style headers:
990
-        #	# Header 1
991
-        #	## Header 2
992
-        #	## Header 2 with closing hashes ##
993
-        #	...
994
-        #	###### Header 6
995
-        #
996
-        $text = preg_replace_callback(
997
-            '{
919
+			array(&$this, '_doImages_inline_callback'),
920
+			$text
921
+		);
922
+
923
+		return $text;
924
+	}
925
+
926
+	function _doImages_reference_callback($matches)
927
+	{
928
+		$whole_match = $matches[1];
929
+		$alt_text    = $matches[2];
930
+		$link_id     = strtolower($matches[3]);
931
+
932
+		if ($link_id == "") {
933
+			$link_id = strtolower($alt_text); # for shortcut links like ![this][].
934
+		}
935
+
936
+		$alt_text = $this->encodeAttribute($alt_text);
937
+		if (isset($this->urls[$link_id])) {
938
+			$url    = $this->encodeAttribute($this->urls[$link_id]);
939
+			$result = "<img src=\"$url\" alt=\"$alt_text\"";
940
+			if (isset($this->titles[$link_id])) {
941
+				$title = $this->titles[$link_id];
942
+				$title = $this->encodeAttribute($title);
943
+				$result .= " title=\"$title\"";
944
+			}
945
+			$result .= $this->empty_element_suffix;
946
+			$result = $this->hashPart($result);
947
+		} else {
948
+			# If there's no such link ID, leave intact:
949
+			$result = $whole_match;
950
+		}
951
+
952
+		return $result;
953
+	}
954
+
955
+	function _doImages_inline_callback($matches)
956
+	{
957
+		$whole_match = $matches[1];
958
+		$alt_text    = $matches[2];
959
+		$url         = $matches[3] == '' ? $matches[4] : $matches[3];
960
+		$title       =& $matches[7];
961
+
962
+		$alt_text = $this->encodeAttribute($alt_text);
963
+		$url      = $this->encodeAttribute($url);
964
+		$result   = "<img src=\"$url\" alt=\"$alt_text\"";
965
+		if (isset($title)) {
966
+			$title = $this->encodeAttribute($title);
967
+			$result .= " title=\"$title\""; # $title already quoted
968
+		}
969
+		$result .= $this->empty_element_suffix;
970
+
971
+		return $this->hashPart($result);
972
+	}
973
+
974
+	function doHeaders($text)
975
+	{
976
+		# Setext-style headers:
977
+		#	  Header 1
978
+		#	  ========
979
+		#
980
+		#	  Header 2
981
+		#	  --------
982
+		#
983
+		$text = preg_replace_callback(
984
+			'{ ^(.+?)[ ]*\n(=+|-+)[ ]*\n+ }mx',
985
+			array(&$this, '_doHeaders_callback_setext'),
986
+			$text
987
+		);
988
+
989
+		# atx-style headers:
990
+		#	# Header 1
991
+		#	## Header 2
992
+		#	## Header 2 with closing hashes ##
993
+		#	...
994
+		#	###### Header 6
995
+		#
996
+		$text = preg_replace_callback(
997
+			'{
998 998
 				^(\#{1,6})	# $1 = string of #\'s
999 999
 				[ ]*
1000 1000
 				(.+?)		# $2 = Header text
@@ -1002,52 +1002,52 @@  discard block
 block discarded – undo
1002 1002
 				\#*			# optional closing #\'s (not counted)
1003 1003
 				\n+
1004 1004
 			}xm',
1005
-            array(&$this, '_doHeaders_callback_atx'),
1006
-            $text
1007
-        );
1008
-
1009
-        return $text;
1010
-    }
1011
-
1012
-    function _doHeaders_callback_setext($matches)
1013
-    {
1014
-        # Terrible hack to check we haven't found an empty list item.
1015
-        if ($matches[2] == '-' && preg_match('{^-(?: |$)}', $matches[1])) {
1016
-            return $matches[0];
1017
-        }
1018
-
1019
-        $level = $matches[2]{0} == '=' ? 1 : 2;
1020
-        $block = "<h$level>" . $this->runSpanGamut($matches[1]) . "</h$level>";
1021
-        return "\n" . $this->hashBlock($block) . "\n\n";
1022
-    }
1023
-
1024
-    function _doHeaders_callback_atx($matches)
1025
-    {
1026
-        $level = strlen($matches[1]);
1027
-        $block = "<h$level>" . $this->runSpanGamut($matches[2]) . "</h$level>";
1028
-        return "\n" . $this->hashBlock($block) . "\n\n";
1029
-    }
1030
-
1031
-    function doLists($text)
1032
-    {
1033
-        #
1034
-        # Form HTML ordered (numbered) and unordered (bulleted) lists.
1035
-        #
1036
-        $less_than_tab = $this->tab_width - 1;
1037
-
1038
-        # Re-usable patterns to match list item bullets and number markers:
1039
-        $marker_ul_re  = '[*+-]';
1040
-        $marker_ol_re  = '\d+[\.]';
1041
-        $marker_any_re = "(?:$marker_ul_re|$marker_ol_re)";
1042
-
1043
-        $markers_relist = array(
1044
-            $marker_ul_re => $marker_ol_re,
1045
-            $marker_ol_re => $marker_ul_re,
1046
-        );
1047
-
1048
-        foreach ($markers_relist as $marker_re => $other_marker_re) {
1049
-            # Re-usable pattern to match any entirel ul or ol list:
1050
-            $whole_list_re = '
1005
+			array(&$this, '_doHeaders_callback_atx'),
1006
+			$text
1007
+		);
1008
+
1009
+		return $text;
1010
+	}
1011
+
1012
+	function _doHeaders_callback_setext($matches)
1013
+	{
1014
+		# Terrible hack to check we haven't found an empty list item.
1015
+		if ($matches[2] == '-' && preg_match('{^-(?: |$)}', $matches[1])) {
1016
+			return $matches[0];
1017
+		}
1018
+
1019
+		$level = $matches[2]{0} == '=' ? 1 : 2;
1020
+		$block = "<h$level>" . $this->runSpanGamut($matches[1]) . "</h$level>";
1021
+		return "\n" . $this->hashBlock($block) . "\n\n";
1022
+	}
1023
+
1024
+	function _doHeaders_callback_atx($matches)
1025
+	{
1026
+		$level = strlen($matches[1]);
1027
+		$block = "<h$level>" . $this->runSpanGamut($matches[2]) . "</h$level>";
1028
+		return "\n" . $this->hashBlock($block) . "\n\n";
1029
+	}
1030
+
1031
+	function doLists($text)
1032
+	{
1033
+		#
1034
+		# Form HTML ordered (numbered) and unordered (bulleted) lists.
1035
+		#
1036
+		$less_than_tab = $this->tab_width - 1;
1037
+
1038
+		# Re-usable patterns to match list item bullets and number markers:
1039
+		$marker_ul_re  = '[*+-]';
1040
+		$marker_ol_re  = '\d+[\.]';
1041
+		$marker_any_re = "(?:$marker_ul_re|$marker_ol_re)";
1042
+
1043
+		$markers_relist = array(
1044
+			$marker_ul_re => $marker_ol_re,
1045
+			$marker_ol_re => $marker_ul_re,
1046
+		);
1047
+
1048
+		foreach ($markers_relist as $marker_re => $other_marker_re) {
1049
+			# Re-usable pattern to match any entirel ul or ol list:
1050
+			$whole_list_re = '
1051 1051
 				(								# $1 = whole list
1052 1052
 				  (								# $2
1053 1053
 					([ ]{0,' . $less_than_tab . '})	# $3 = number of spaces
@@ -1074,88 +1074,88 @@  discard block
 block discarded – undo
1074 1074
 				)
1075 1075
 			'; // mx
1076 1076
 
1077
-            # We use a different prefix before nested lists than top-level lists.
1078
-            # See extended comment in _ProcessListItems().
1077
+			# We use a different prefix before nested lists than top-level lists.
1078
+			# See extended comment in _ProcessListItems().
1079 1079
 
1080
-            if ($this->list_level) {
1081
-                $text = preg_replace_callback(
1082
-                    '{
1080
+			if ($this->list_level) {
1081
+				$text = preg_replace_callback(
1082
+					'{
1083 1083
 						^
1084 1084
 						' . $whole_list_re . '
1085 1085
 					}mx',
1086
-                    array(&$this, '_doLists_callback'),
1087
-                    $text
1088
-                );
1089
-            } else {
1090
-                $text = preg_replace_callback(
1091
-                    '{
1086
+					array(&$this, '_doLists_callback'),
1087
+					$text
1088
+				);
1089
+			} else {
1090
+				$text = preg_replace_callback(
1091
+					'{
1092 1092
 						(?:(?<=\n)\n|\A\n?) # Must eat the newline
1093 1093
 						' . $whole_list_re . '
1094 1094
 					}mx',
1095
-                    array(&$this, '_doLists_callback'),
1096
-                    $text
1097
-                );
1098
-            }
1099
-        }
1100
-
1101
-        return $text;
1102
-    }
1103
-
1104
-    function _doLists_callback($matches)
1105
-    {
1106
-        # Re-usable patterns to match list item bullets and number markers:
1107
-        $marker_ul_re  = '[*+-]';
1108
-        $marker_ol_re  = '\d+[\.]';
1109
-        $marker_any_re = "(?:$marker_ul_re|$marker_ol_re)";
1110
-
1111
-        $list      = $matches[1];
1112
-        $list_type = preg_match("/$marker_ul_re/", $matches[4]) ? "ul" : "ol";
1113
-
1114
-        $marker_any_re = ($list_type == "ul" ? $marker_ul_re : $marker_ol_re);
1115
-
1116
-        $list .= "\n";
1117
-        $result = $this->processListItems($list, $marker_any_re);
1118
-
1119
-        $result = $this->hashBlock("<$list_type>\n" . $result . "</$list_type>");
1120
-        return "\n" . $result . "\n\n";
1121
-    }
1122
-
1123
-    var $list_level = 0;
1124
-
1125
-    function processListItems($list_str, $marker_any_re)
1126
-    {
1127
-        #
1128
-        #	Process the contents of a single ordered or unordered list, splitting it
1129
-        #	into individual list items.
1130
-        #
1131
-        # The $this->list_level global keeps track of when we're inside a list.
1132
-        # Each time we enter a list, we increment it; when we leave a list,
1133
-        # we decrement. If it's zero, we're not in a list anymore.
1134
-        #
1135
-        # We do this because when we're not inside a list, we want to treat
1136
-        # something like this:
1137
-        #
1138
-        #		I recommend upgrading to version
1139
-        #		8. Oops, now this line is treated
1140
-        #		as a sub-list.
1141
-        #
1142
-        # As a single paragraph, despite the fact that the second line starts
1143
-        # with a digit-period-space sequence.
1144
-        #
1145
-        # Whereas when we're inside a list (or sub-list), that line will be
1146
-        # treated as the start of a sub-list. What a kludge, huh? This is
1147
-        # an aspect of Markdown's syntax that's hard to parse perfectly
1148
-        # without resorting to mind-reading. Perhaps the solution is to
1149
-        # change the syntax rules such that sub-lists must start with a
1150
-        # starting cardinal number; e.g. "1." or "a.".
1151
-
1152
-        $this->list_level ++;
1153
-
1154
-        # trim trailing blank lines:
1155
-        $list_str = preg_replace("/\n{2,}\\z/", "\n", $list_str);
1156
-
1157
-        $list_str = preg_replace_callback(
1158
-            '{
1095
+					array(&$this, '_doLists_callback'),
1096
+					$text
1097
+				);
1098
+			}
1099
+		}
1100
+
1101
+		return $text;
1102
+	}
1103
+
1104
+	function _doLists_callback($matches)
1105
+	{
1106
+		# Re-usable patterns to match list item bullets and number markers:
1107
+		$marker_ul_re  = '[*+-]';
1108
+		$marker_ol_re  = '\d+[\.]';
1109
+		$marker_any_re = "(?:$marker_ul_re|$marker_ol_re)";
1110
+
1111
+		$list      = $matches[1];
1112
+		$list_type = preg_match("/$marker_ul_re/", $matches[4]) ? "ul" : "ol";
1113
+
1114
+		$marker_any_re = ($list_type == "ul" ? $marker_ul_re : $marker_ol_re);
1115
+
1116
+		$list .= "\n";
1117
+		$result = $this->processListItems($list, $marker_any_re);
1118
+
1119
+		$result = $this->hashBlock("<$list_type>\n" . $result . "</$list_type>");
1120
+		return "\n" . $result . "\n\n";
1121
+	}
1122
+
1123
+	var $list_level = 0;
1124
+
1125
+	function processListItems($list_str, $marker_any_re)
1126
+	{
1127
+		#
1128
+		#	Process the contents of a single ordered or unordered list, splitting it
1129
+		#	into individual list items.
1130
+		#
1131
+		# The $this->list_level global keeps track of when we're inside a list.
1132
+		# Each time we enter a list, we increment it; when we leave a list,
1133
+		# we decrement. If it's zero, we're not in a list anymore.
1134
+		#
1135
+		# We do this because when we're not inside a list, we want to treat
1136
+		# something like this:
1137
+		#
1138
+		#		I recommend upgrading to version
1139
+		#		8. Oops, now this line is treated
1140
+		#		as a sub-list.
1141
+		#
1142
+		# As a single paragraph, despite the fact that the second line starts
1143
+		# with a digit-period-space sequence.
1144
+		#
1145
+		# Whereas when we're inside a list (or sub-list), that line will be
1146
+		# treated as the start of a sub-list. What a kludge, huh? This is
1147
+		# an aspect of Markdown's syntax that's hard to parse perfectly
1148
+		# without resorting to mind-reading. Perhaps the solution is to
1149
+		# change the syntax rules such that sub-lists must start with a
1150
+		# starting cardinal number; e.g. "1." or "a.".
1151
+
1152
+		$this->list_level ++;
1153
+
1154
+		# trim trailing blank lines:
1155
+		$list_str = preg_replace("/\n{2,}\\z/", "\n", $list_str);
1156
+
1157
+		$list_str = preg_replace_callback(
1158
+			'{
1159 1159
 			(\n)?							# leading line = $1
1160 1160
 			(^[ ]*)							# leading whitespace = $2
1161 1161
 			(' . $marker_any_re . '				# list marker and space = $3
@@ -1165,45 +1165,45 @@  discard block
 block discarded – undo
1165 1165
 			(?:(\n+(?=\n))|\n)				# tailing blank line = $5
1166 1166
 			(?= \n* (\z | \2 (' . $marker_any_re . ') (?:[ ]+|(?=\n))))
1167 1167
 			}xm',
1168
-            array(&$this, '_processListItems_callback'),
1169
-            $list_str
1170
-        );
1171
-
1172
-        $this->list_level --;
1173
-        return $list_str;
1174
-    }
1175
-
1176
-    function _processListItems_callback($matches)
1177
-    {
1178
-        $item               = $matches[4];
1179
-        $leading_line       =& $matches[1];
1180
-        $leading_space      =& $matches[2];
1181
-        $marker_space       = $matches[3];
1182
-        $tailing_blank_line =& $matches[5];
1183
-
1184
-        if ($leading_line || $tailing_blank_line ||
1185
-            preg_match('/\n{2,}/', $item)
1186
-        ) {
1187
-            # Replace marker with the appropriate whitespace indentation
1188
-            $item = $leading_space . str_repeat(' ', strlen($marker_space)) . $item;
1189
-            $item = $this->runBlockGamut($this->outdent($item) . "\n");
1190
-        } else {
1191
-            # Recursion for sub-lists:
1192
-            $item = $this->doLists($this->outdent($item));
1193
-            $item = preg_replace('/\n+$/', '', $item);
1194
-            $item = $this->runSpanGamut($item);
1195
-        }
1196
-
1197
-        return "<li>" . $item . "</li>\n";
1198
-    }
1199
-
1200
-    function doCodeBlocks($text)
1201
-    {
1202
-        #
1203
-        #	Process Markdown `<pre><code>` blocks.
1204
-        #
1205
-        $text = preg_replace_callback(
1206
-            '{
1168
+			array(&$this, '_processListItems_callback'),
1169
+			$list_str
1170
+		);
1171
+
1172
+		$this->list_level --;
1173
+		return $list_str;
1174
+	}
1175
+
1176
+	function _processListItems_callback($matches)
1177
+	{
1178
+		$item               = $matches[4];
1179
+		$leading_line       =& $matches[1];
1180
+		$leading_space      =& $matches[2];
1181
+		$marker_space       = $matches[3];
1182
+		$tailing_blank_line =& $matches[5];
1183
+
1184
+		if ($leading_line || $tailing_blank_line ||
1185
+			preg_match('/\n{2,}/', $item)
1186
+		) {
1187
+			# Replace marker with the appropriate whitespace indentation
1188
+			$item = $leading_space . str_repeat(' ', strlen($marker_space)) . $item;
1189
+			$item = $this->runBlockGamut($this->outdent($item) . "\n");
1190
+		} else {
1191
+			# Recursion for sub-lists:
1192
+			$item = $this->doLists($this->outdent($item));
1193
+			$item = preg_replace('/\n+$/', '', $item);
1194
+			$item = $this->runSpanGamut($item);
1195
+		}
1196
+
1197
+		return "<li>" . $item . "</li>\n";
1198
+	}
1199
+
1200
+	function doCodeBlocks($text)
1201
+	{
1202
+		#
1203
+		#	Process Markdown `<pre><code>` blocks.
1204
+		#
1205
+		$text = preg_replace_callback(
1206
+			'{
1207 1207
 				(?:\n\n|\A\n?)
1208 1208
 				(	            # $1 = the code block -- one or more lines, starting with a space/tab
1209 1209
 				  (?>
@@ -1213,206 +1213,206 @@  discard block
 block discarded – undo
1213 1213
 				)
1214 1214
 				((?=^[ ]{0,' . $this->tab_width . '}\S)|\Z)	# Lookahead for non-space at line-start, or end of doc
1215 1215
 			}xm',
1216
-            array(&$this, '_doCodeBlocks_callback'),
1217
-            $text
1218
-        );
1219
-
1220
-        return $text;
1221
-    }
1222
-
1223
-    function _doCodeBlocks_callback($matches)
1224
-    {
1225
-        $codeblock = $matches[1];
1226
-
1227
-        $codeblock = $this->outdent($codeblock);
1228
-        $codeblock = htmlspecialchars($codeblock, ENT_NOQUOTES);
1229
-
1230
-        # trim leading newlines and trailing newlines
1231
-        $codeblock = preg_replace('/\A\n+|\n+\z/', '', $codeblock);
1232
-
1233
-        $codeblock = "<pre><code>$codeblock\n</code></pre>";
1234
-        return "\n\n" . $this->hashBlock($codeblock) . "\n\n";
1235
-    }
1236
-
1237
-    function makeCodeSpan($code)
1238
-    {
1239
-        #
1240
-        # Create a code span markup for $code. Called from handleSpanToken.
1241
-        #
1242
-        $code = htmlspecialchars(trim($code), ENT_NOQUOTES);
1243
-        return $this->hashPart("<code>$code</code>");
1244
-    }
1245
-
1246
-    var $em_relist = array(
1247
-        ''  => '(?:(?<!\*)\*(?!\*)|(?<!_)_(?!_))(?=\S|$)(?![\.,:;]\s)',
1248
-        '*' => '(?<=\S|^)(?<!\*)\*(?!\*)',
1249
-        '_' => '(?<=\S|^)(?<!_)_(?!_)',
1250
-    );
1251
-
1252
-    var $strong_relist = array(
1253
-        ''   => '(?:(?<!\*)\*\*(?!\*)|(?<!_)__(?!_))(?=\S|$)(?![\.,:;]\s)',
1254
-        '**' => '(?<=\S|^)(?<!\*)\*\*(?!\*)',
1255
-        '__' => '(?<=\S|^)(?<!_)__(?!_)',
1256
-    );
1257
-
1258
-    var $em_strong_relist = array(
1259
-        ''    => '(?:(?<!\*)\*\*\*(?!\*)|(?<!_)___(?!_))(?=\S|$)(?![\.,:;]\s)',
1260
-        '***' => '(?<=\S|^)(?<!\*)\*\*\*(?!\*)',
1261
-        '___' => '(?<=\S|^)(?<!_)___(?!_)',
1262
-    );
1263
-
1264
-    var $em_strong_prepared_relist;
1265
-
1266
-    function prepareItalicsAndBold()
1267
-    {
1268
-        #
1269
-        # Prepare regular expressions for searching emphasis tokens in any
1270
-        # context.
1271
-        #
1272
-        foreach ($this->em_relist as $em => $em_re) {
1273
-            foreach ($this->strong_relist as $strong => $strong_re) {
1274
-                # Construct list of allowed token expressions.
1275
-                $token_relist = array();
1276
-                if (isset($this->em_strong_relist["$em$strong"])) {
1277
-                    $token_relist[] = $this->em_strong_relist["$em$strong"];
1278
-                }
1279
-                $token_relist[] = $em_re;
1280
-                $token_relist[] = $strong_re;
1281
-
1282
-                # Construct master expression from list.
1283
-                $token_re                                      = '{(' . implode('|', $token_relist) . ')}';
1284
-                $this->em_strong_prepared_relist["$em$strong"] = $token_re;
1285
-            }
1286
-        }
1287
-    }
1288
-
1289
-    function doItalicsAndBold($text)
1290
-    {
1291
-        $token_stack  = array('');
1292
-        $text_stack   = array('');
1293
-        $em           = '';
1294
-        $strong       = '';
1295
-        $tree_char_em = false;
1296
-
1297
-        while (1) {
1298
-            #
1299
-            # Get prepared regular expression for seraching emphasis tokens
1300
-            # in current context.
1301
-            #
1302
-            $token_re = $this->em_strong_prepared_relist["$em$strong"];
1303
-
1304
-            #
1305
-            # Each loop iteration search for the next emphasis token.
1306
-            # Each token is then passed to handleSpanToken.
1307
-            #
1308
-            $parts = preg_split($token_re, $text, 2, PREG_SPLIT_DELIM_CAPTURE);
1309
-            $text_stack[0] .= $parts[0];
1310
-            $token =& $parts[1];
1311
-            $text  =& $parts[2];
1312
-
1313
-            if (empty($token)) {
1314
-                # Reached end of text span: empty stack without emitting.
1315
-                # any more emphasis.
1316
-                while ($token_stack[0]) {
1317
-                    $text_stack[1] .= array_shift($token_stack);
1318
-                    $text_stack[0] .= array_shift($text_stack);
1319
-                }
1320
-                break;
1321
-            }
1322
-
1323
-            $token_len = strlen($token);
1324
-            if ($tree_char_em) {
1325
-                # Reached closing marker while inside a three-char emphasis.
1326
-                if ($token_len == 3) {
1327
-                    # Three-char closing marker, close em and strong.
1328
-                    array_shift($token_stack);
1329
-                    $span = array_shift($text_stack);
1330
-                    $span = $this->runSpanGamut($span);
1331
-                    $span = "<strong><em>$span</em></strong>";
1332
-                    $text_stack[0] .= $this->hashPart($span);
1333
-                    $em     = '';
1334
-                    $strong = '';
1335
-                } else {
1336
-                    # Other closing marker: close one em or strong and
1337
-                    # change current token state to match the other
1338
-                    $token_stack[0] = str_repeat($token{0}, 3 - $token_len);
1339
-                    $tag            = $token_len == 2 ? "strong" : "em";
1340
-                    $span           = $text_stack[0];
1341
-                    $span           = $this->runSpanGamut($span);
1342
-                    $span           = "<$tag>$span</$tag>";
1343
-                    $text_stack[0]  = $this->hashPart($span);
1344
-                    $$tag           = ''; # $$tag stands for $em or $strong
1345
-                }
1346
-                $tree_char_em = false;
1347
-            } else if ($token_len == 3) {
1348
-                if ($em) {
1349
-                    # Reached closing marker for both em and strong.
1350
-                    # Closing strong marker:
1351
-                    for ($i = 0; $i < 2; ++ $i) {
1352
-                        $shifted_token = array_shift($token_stack);
1353
-                        $tag           = strlen($shifted_token) == 2 ? "strong" : "em";
1354
-                        $span          = array_shift($text_stack);
1355
-                        $span          = $this->runSpanGamut($span);
1356
-                        $span          = "<$tag>$span</$tag>";
1357
-                        $text_stack[0] .= $this->hashPart($span);
1358
-                        $$tag = ''; # $$tag stands for $em or $strong
1359
-                    }
1360
-                } else {
1361
-                    # Reached opening three-char emphasis marker. Push on token
1362
-                    # stack; will be handled by the special condition above.
1363
-                    $em     = $token{0};
1364
-                    $strong = "$em$em";
1365
-                    array_unshift($token_stack, $token);
1366
-                    array_unshift($text_stack, '');
1367
-                    $tree_char_em = true;
1368
-                }
1369
-            } else if ($token_len == 2) {
1370
-                if ($strong) {
1371
-                    # Unwind any dangling emphasis marker:
1372
-                    if (strlen($token_stack[0]) == 1) {
1373
-                        $text_stack[1] .= array_shift($token_stack);
1374
-                        $text_stack[0] .= array_shift($text_stack);
1375
-                    }
1376
-                    # Closing strong marker:
1377
-                    array_shift($token_stack);
1378
-                    $span = array_shift($text_stack);
1379
-                    $span = $this->runSpanGamut($span);
1380
-                    $span = "<strong>$span</strong>";
1381
-                    $text_stack[0] .= $this->hashPart($span);
1382
-                    $strong = '';
1383
-                } else {
1384
-                    array_unshift($token_stack, $token);
1385
-                    array_unshift($text_stack, '');
1386
-                    $strong = $token;
1387
-                }
1388
-            } else {
1389
-                # Here $token_len == 1
1390
-                if ($em) {
1391
-                    if (strlen($token_stack[0]) == 1) {
1392
-                        # Closing emphasis marker:
1393
-                        array_shift($token_stack);
1394
-                        $span = array_shift($text_stack);
1395
-                        $span = $this->runSpanGamut($span);
1396
-                        $span = "<em>$span</em>";
1397
-                        $text_stack[0] .= $this->hashPart($span);
1398
-                        $em = '';
1399
-                    } else {
1400
-                        $text_stack[0] .= $token;
1401
-                    }
1402
-                } else {
1403
-                    array_unshift($token_stack, $token);
1404
-                    array_unshift($text_stack, '');
1405
-                    $em = $token;
1406
-                }
1407
-            }
1408
-        }
1409
-        return $text_stack[0];
1410
-    }
1411
-
1412
-    function doBlockQuotes($text)
1413
-    {
1414
-        $text = preg_replace_callback(
1415
-            '/
1216
+			array(&$this, '_doCodeBlocks_callback'),
1217
+			$text
1218
+		);
1219
+
1220
+		return $text;
1221
+	}
1222
+
1223
+	function _doCodeBlocks_callback($matches)
1224
+	{
1225
+		$codeblock = $matches[1];
1226
+
1227
+		$codeblock = $this->outdent($codeblock);
1228
+		$codeblock = htmlspecialchars($codeblock, ENT_NOQUOTES);
1229
+
1230
+		# trim leading newlines and trailing newlines
1231
+		$codeblock = preg_replace('/\A\n+|\n+\z/', '', $codeblock);
1232
+
1233
+		$codeblock = "<pre><code>$codeblock\n</code></pre>";
1234
+		return "\n\n" . $this->hashBlock($codeblock) . "\n\n";
1235
+	}
1236
+
1237
+	function makeCodeSpan($code)
1238
+	{
1239
+		#
1240
+		# Create a code span markup for $code. Called from handleSpanToken.
1241
+		#
1242
+		$code = htmlspecialchars(trim($code), ENT_NOQUOTES);
1243
+		return $this->hashPart("<code>$code</code>");
1244
+	}
1245
+
1246
+	var $em_relist = array(
1247
+		''  => '(?:(?<!\*)\*(?!\*)|(?<!_)_(?!_))(?=\S|$)(?![\.,:;]\s)',
1248
+		'*' => '(?<=\S|^)(?<!\*)\*(?!\*)',
1249
+		'_' => '(?<=\S|^)(?<!_)_(?!_)',
1250
+	);
1251
+
1252
+	var $strong_relist = array(
1253
+		''   => '(?:(?<!\*)\*\*(?!\*)|(?<!_)__(?!_))(?=\S|$)(?![\.,:;]\s)',
1254
+		'**' => '(?<=\S|^)(?<!\*)\*\*(?!\*)',
1255
+		'__' => '(?<=\S|^)(?<!_)__(?!_)',
1256
+	);
1257
+
1258
+	var $em_strong_relist = array(
1259
+		''    => '(?:(?<!\*)\*\*\*(?!\*)|(?<!_)___(?!_))(?=\S|$)(?![\.,:;]\s)',
1260
+		'***' => '(?<=\S|^)(?<!\*)\*\*\*(?!\*)',
1261
+		'___' => '(?<=\S|^)(?<!_)___(?!_)',
1262
+	);
1263
+
1264
+	var $em_strong_prepared_relist;
1265
+
1266
+	function prepareItalicsAndBold()
1267
+	{
1268
+		#
1269
+		# Prepare regular expressions for searching emphasis tokens in any
1270
+		# context.
1271
+		#
1272
+		foreach ($this->em_relist as $em => $em_re) {
1273
+			foreach ($this->strong_relist as $strong => $strong_re) {
1274
+				# Construct list of allowed token expressions.
1275
+				$token_relist = array();
1276
+				if (isset($this->em_strong_relist["$em$strong"])) {
1277
+					$token_relist[] = $this->em_strong_relist["$em$strong"];
1278
+				}
1279
+				$token_relist[] = $em_re;
1280
+				$token_relist[] = $strong_re;
1281
+
1282
+				# Construct master expression from list.
1283
+				$token_re                                      = '{(' . implode('|', $token_relist) . ')}';
1284
+				$this->em_strong_prepared_relist["$em$strong"] = $token_re;
1285
+			}
1286
+		}
1287
+	}
1288
+
1289
+	function doItalicsAndBold($text)
1290
+	{
1291
+		$token_stack  = array('');
1292
+		$text_stack   = array('');
1293
+		$em           = '';
1294
+		$strong       = '';
1295
+		$tree_char_em = false;
1296
+
1297
+		while (1) {
1298
+			#
1299
+			# Get prepared regular expression for seraching emphasis tokens
1300
+			# in current context.
1301
+			#
1302
+			$token_re = $this->em_strong_prepared_relist["$em$strong"];
1303
+
1304
+			#
1305
+			# Each loop iteration search for the next emphasis token.
1306
+			# Each token is then passed to handleSpanToken.
1307
+			#
1308
+			$parts = preg_split($token_re, $text, 2, PREG_SPLIT_DELIM_CAPTURE);
1309
+			$text_stack[0] .= $parts[0];
1310
+			$token =& $parts[1];
1311
+			$text  =& $parts[2];
1312
+
1313
+			if (empty($token)) {
1314
+				# Reached end of text span: empty stack without emitting.
1315
+				# any more emphasis.
1316
+				while ($token_stack[0]) {
1317
+					$text_stack[1] .= array_shift($token_stack);
1318
+					$text_stack[0] .= array_shift($text_stack);
1319
+				}
1320
+				break;
1321
+			}
1322
+
1323
+			$token_len = strlen($token);
1324
+			if ($tree_char_em) {
1325
+				# Reached closing marker while inside a three-char emphasis.
1326
+				if ($token_len == 3) {
1327
+					# Three-char closing marker, close em and strong.
1328
+					array_shift($token_stack);
1329
+					$span = array_shift($text_stack);
1330
+					$span = $this->runSpanGamut($span);
1331
+					$span = "<strong><em>$span</em></strong>";
1332
+					$text_stack[0] .= $this->hashPart($span);
1333
+					$em     = '';
1334
+					$strong = '';
1335
+				} else {
1336
+					# Other closing marker: close one em or strong and
1337
+					# change current token state to match the other
1338
+					$token_stack[0] = str_repeat($token{0}, 3 - $token_len);
1339
+					$tag            = $token_len == 2 ? "strong" : "em";
1340
+					$span           = $text_stack[0];
1341
+					$span           = $this->runSpanGamut($span);
1342
+					$span           = "<$tag>$span</$tag>";
1343
+					$text_stack[0]  = $this->hashPart($span);
1344
+					$$tag           = ''; # $$tag stands for $em or $strong
1345
+				}
1346
+				$tree_char_em = false;
1347
+			} else if ($token_len == 3) {
1348
+				if ($em) {
1349
+					# Reached closing marker for both em and strong.
1350
+					# Closing strong marker:
1351
+					for ($i = 0; $i < 2; ++ $i) {
1352
+						$shifted_token = array_shift($token_stack);
1353
+						$tag           = strlen($shifted_token) == 2 ? "strong" : "em";
1354
+						$span          = array_shift($text_stack);
1355
+						$span          = $this->runSpanGamut($span);
1356
+						$span          = "<$tag>$span</$tag>";
1357
+						$text_stack[0] .= $this->hashPart($span);
1358
+						$$tag = ''; # $$tag stands for $em or $strong
1359
+					}
1360
+				} else {
1361
+					# Reached opening three-char emphasis marker. Push on token
1362
+					# stack; will be handled by the special condition above.
1363
+					$em     = $token{0};
1364
+					$strong = "$em$em";
1365
+					array_unshift($token_stack, $token);
1366
+					array_unshift($text_stack, '');
1367
+					$tree_char_em = true;
1368
+				}
1369
+			} else if ($token_len == 2) {
1370
+				if ($strong) {
1371
+					# Unwind any dangling emphasis marker:
1372
+					if (strlen($token_stack[0]) == 1) {
1373
+						$text_stack[1] .= array_shift($token_stack);
1374
+						$text_stack[0] .= array_shift($text_stack);
1375
+					}
1376
+					# Closing strong marker:
1377
+					array_shift($token_stack);
1378
+					$span = array_shift($text_stack);
1379
+					$span = $this->runSpanGamut($span);
1380
+					$span = "<strong>$span</strong>";
1381
+					$text_stack[0] .= $this->hashPart($span);
1382
+					$strong = '';
1383
+				} else {
1384
+					array_unshift($token_stack, $token);
1385
+					array_unshift($text_stack, '');
1386
+					$strong = $token;
1387
+				}
1388
+			} else {
1389
+				# Here $token_len == 1
1390
+				if ($em) {
1391
+					if (strlen($token_stack[0]) == 1) {
1392
+						# Closing emphasis marker:
1393
+						array_shift($token_stack);
1394
+						$span = array_shift($text_stack);
1395
+						$span = $this->runSpanGamut($span);
1396
+						$span = "<em>$span</em>";
1397
+						$text_stack[0] .= $this->hashPart($span);
1398
+						$em = '';
1399
+					} else {
1400
+						$text_stack[0] .= $token;
1401
+					}
1402
+				} else {
1403
+					array_unshift($token_stack, $token);
1404
+					array_unshift($text_stack, '');
1405
+					$em = $token;
1406
+				}
1407
+			}
1408
+		}
1409
+		return $text_stack[0];
1410
+	}
1411
+
1412
+	function doBlockQuotes($text)
1413
+	{
1414
+		$text = preg_replace_callback(
1415
+			'/
1416 1416
 			  (								# Wrap whole match in $1
1417 1417
 				(?>
1418 1418
 				  ^[ ]*>[ ]?			# ">" at the start of a line
@@ -1422,66 +1422,66 @@  discard block
 block discarded – undo
1422 1422
 				)+
1423 1423
 			  )
1424 1424
 			/xm',
1425
-            array(&$this, '_doBlockQuotes_callback'),
1426
-            $text
1427
-        );
1428
-
1429
-        return $text;
1430
-    }
1431
-
1432
-    function _doBlockQuotes_callback($matches)
1433
-    {
1434
-        $bq = $matches[1];
1435
-        # trim one level of quoting - trim whitespace-only lines
1436
-        $bq = preg_replace('/^[ ]*>[ ]?|^[ ]+$/m', '', $bq);
1437
-        $bq = $this->runBlockGamut($bq); # recurse
1438
-
1439
-        $bq = preg_replace('/^/m', "  ", $bq);
1440
-        # These leading spaces cause problem with <pre> content,
1441
-        # so we need to fix that:
1442
-        $bq = preg_replace_callback(
1443
-            '{(\s*<pre>.+?</pre>)}sx',
1444
-            array(&$this, '_doBlockQuotes_callback2'),
1445
-            $bq
1446
-        );
1447
-
1448
-        return "\n" . $this->hashBlock("<blockquote>\n$bq\n</blockquote>") . "\n\n";
1449
-    }
1450
-
1451
-    function _doBlockQuotes_callback2($matches)
1452
-    {
1453
-        $pre = $matches[1];
1454
-        $pre = preg_replace('/^  /m', '', $pre);
1455
-        return $pre;
1456
-    }
1457
-
1458
-    function formParagraphs($text)
1459
-    {
1460
-        #
1461
-        #	Params:
1462
-        #		$text - string to process with html <p> tags
1463
-        #
1464
-        # Strip leading and trailing lines:
1465
-        $text = preg_replace('/\A\n+|\n+\z/', '', $text);
1466
-
1467
-        $grafs = preg_split('/\n{2,}/', $text, - 1, PREG_SPLIT_NO_EMPTY);
1468
-
1469
-        #
1470
-        # Wrap <p> tags and unhashify HTML blocks
1471
-        #
1472
-        foreach ($grafs as $key => $value) {
1473
-            if (!preg_match('/^B\x1A[0-9]+B$/', $value)) {
1474
-                # Is a paragraph.
1475
-                $value = $this->runSpanGamut($value);
1476
-                $value = preg_replace('/^([ ]*)/', "<p>", $value);
1477
-                $value .= "</p>";
1478
-                $grafs[$key] = $this->unhash($value);
1479
-            } else {
1480
-                # Is a block.
1481
-                # Modify elements of @grafs in-place...
1482
-                $graf  = $value;
1483
-                $block = $this->html_hashes[$graf];
1484
-                $graf  = $block;
1425
+			array(&$this, '_doBlockQuotes_callback'),
1426
+			$text
1427
+		);
1428
+
1429
+		return $text;
1430
+	}
1431
+
1432
+	function _doBlockQuotes_callback($matches)
1433
+	{
1434
+		$bq = $matches[1];
1435
+		# trim one level of quoting - trim whitespace-only lines
1436
+		$bq = preg_replace('/^[ ]*>[ ]?|^[ ]+$/m', '', $bq);
1437
+		$bq = $this->runBlockGamut($bq); # recurse
1438
+
1439
+		$bq = preg_replace('/^/m', "  ", $bq);
1440
+		# These leading spaces cause problem with <pre> content,
1441
+		# so we need to fix that:
1442
+		$bq = preg_replace_callback(
1443
+			'{(\s*<pre>.+?</pre>)}sx',
1444
+			array(&$this, '_doBlockQuotes_callback2'),
1445
+			$bq
1446
+		);
1447
+
1448
+		return "\n" . $this->hashBlock("<blockquote>\n$bq\n</blockquote>") . "\n\n";
1449
+	}
1450
+
1451
+	function _doBlockQuotes_callback2($matches)
1452
+	{
1453
+		$pre = $matches[1];
1454
+		$pre = preg_replace('/^  /m', '', $pre);
1455
+		return $pre;
1456
+	}
1457
+
1458
+	function formParagraphs($text)
1459
+	{
1460
+		#
1461
+		#	Params:
1462
+		#		$text - string to process with html <p> tags
1463
+		#
1464
+		# Strip leading and trailing lines:
1465
+		$text = preg_replace('/\A\n+|\n+\z/', '', $text);
1466
+
1467
+		$grafs = preg_split('/\n{2,}/', $text, - 1, PREG_SPLIT_NO_EMPTY);
1468
+
1469
+		#
1470
+		# Wrap <p> tags and unhashify HTML blocks
1471
+		#
1472
+		foreach ($grafs as $key => $value) {
1473
+			if (!preg_match('/^B\x1A[0-9]+B$/', $value)) {
1474
+				# Is a paragraph.
1475
+				$value = $this->runSpanGamut($value);
1476
+				$value = preg_replace('/^([ ]*)/', "<p>", $value);
1477
+				$value .= "</p>";
1478
+				$grafs[$key] = $this->unhash($value);
1479
+			} else {
1480
+				# Is a block.
1481
+				# Modify elements of @grafs in-place...
1482
+				$graf  = $value;
1483
+				$block = $this->html_hashes[$graf];
1484
+				$graf  = $block;
1485 1485
 //				if (preg_match('{
1486 1486
 //					\A
1487 1487
 //					(							# $1 = <div> tag
@@ -1517,59 +1517,59 @@  discard block
 block discarded – undo
1517 1517
 //
1518 1518
 //					$graf = $div_open . "\n" . $div_content . "\n" . $div_close;
1519 1519
 //				}
1520
-                $grafs[$key] = $graf;
1521
-            }
1522
-        }
1523
-
1524
-        return implode("\n\n", $grafs);
1525
-    }
1526
-
1527
-    function encodeAttribute($text)
1528
-    {
1529
-        #
1530
-        # Encode text for a double-quoted HTML attribute. This function
1531
-        # is *not* suitable for attributes enclosed in single quotes.
1532
-        #
1533
-        $text = $this->encodeAmpsAndAngles($text);
1534
-        $text = str_replace('"', '&quot;', $text);
1535
-        return $text;
1536
-    }
1537
-
1538
-    function encodeAmpsAndAngles($text)
1539
-    {
1540
-        #
1541
-        # Smart processing for ampersands and angle brackets that need to
1542
-        # be encoded. Valid character entities are left alone unless the
1543
-        # no-entities mode is set.
1544
-        #
1545
-        if ($this->no_entities) {
1546
-            $text = str_replace('&', '&amp;', $text);
1547
-        } else {
1548
-            # Ampersand-encoding based entirely on Nat Irons's Amputator
1549
-            # MT plugin: <http://bumppo.net/projects/amputator/>
1550
-            $text = preg_replace(
1551
-                '/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/',
1552
-                '&amp;',
1553
-                $text
1554
-            );;
1555
-        }
1556
-        # Encode remaining <'s
1557
-        $text = str_replace('<', '&lt;', $text);
1558
-
1559
-        return $text;
1560
-    }
1561
-
1562
-    function doAutoLinks($text)
1563
-    {
1564
-        $text = preg_replace_callback(
1565
-            '{<((https?|ftp|dict):[^\'">\s]+)>}i',
1566
-            array(&$this, '_doAutoLinks_url_callback'),
1567
-            $text
1568
-        );
1569
-
1570
-        # Email addresses: <[email protected]>
1571
-        $text = preg_replace_callback(
1572
-            '{
1520
+				$grafs[$key] = $graf;
1521
+			}
1522
+		}
1523
+
1524
+		return implode("\n\n", $grafs);
1525
+	}
1526
+
1527
+	function encodeAttribute($text)
1528
+	{
1529
+		#
1530
+		# Encode text for a double-quoted HTML attribute. This function
1531
+		# is *not* suitable for attributes enclosed in single quotes.
1532
+		#
1533
+		$text = $this->encodeAmpsAndAngles($text);
1534
+		$text = str_replace('"', '&quot;', $text);
1535
+		return $text;
1536
+	}
1537
+
1538
+	function encodeAmpsAndAngles($text)
1539
+	{
1540
+		#
1541
+		# Smart processing for ampersands and angle brackets that need to
1542
+		# be encoded. Valid character entities are left alone unless the
1543
+		# no-entities mode is set.
1544
+		#
1545
+		if ($this->no_entities) {
1546
+			$text = str_replace('&', '&amp;', $text);
1547
+		} else {
1548
+			# Ampersand-encoding based entirely on Nat Irons's Amputator
1549
+			# MT plugin: <http://bumppo.net/projects/amputator/>
1550
+			$text = preg_replace(
1551
+				'/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/',
1552
+				'&amp;',
1553
+				$text
1554
+			);;
1555
+		}
1556
+		# Encode remaining <'s
1557
+		$text = str_replace('<', '&lt;', $text);
1558
+
1559
+		return $text;
1560
+	}
1561
+
1562
+	function doAutoLinks($text)
1563
+	{
1564
+		$text = preg_replace_callback(
1565
+			'{<((https?|ftp|dict):[^\'">\s]+)>}i',
1566
+			array(&$this, '_doAutoLinks_url_callback'),
1567
+			$text
1568
+		);
1569
+
1570
+		# Email addresses: <[email protected]>
1571
+		$text = preg_replace_callback(
1572
+			'{
1573 1573
 			<
1574 1574
 			(?:mailto:)?
1575 1575
 			(
@@ -1587,81 +1587,81 @@  discard block
 block discarded – undo
1587 1587
 			)
1588 1588
 			>
1589 1589
 			}xi',
1590
-            array(&$this, '_doAutoLinks_email_callback'),
1591
-            $text
1592
-        );
1593
-
1594
-        return $text;
1595
-    }
1596
-
1597
-    function _doAutoLinks_url_callback($matches)
1598
-    {
1599
-        $url  = $this->encodeAttribute($matches[1]);
1600
-        $link = "<a href=\"$url\">$url</a>";
1601
-        return $this->hashPart($link);
1602
-    }
1603
-
1604
-    function _doAutoLinks_email_callback($matches)
1605
-    {
1606
-        $address = $matches[1];
1607
-        $link    = $this->encodeEmailAddress($address);
1608
-        return $this->hashPart($link);
1609
-    }
1610
-
1611
-    function encodeEmailAddress($addr)
1612
-    {
1613
-        #
1614
-        #	Input: an email address, e.g. "[email protected]"
1615
-        #
1616
-        #	Output: the email address as a mailto link, with each character
1617
-        #		of the address encoded as either a decimal or hex entity, in
1618
-        #		the hopes of foiling most address harvesting spam bots. E.g.:
1619
-        #
1620
-        #	  <p><a href="&#109;&#x61;&#105;&#x6c;&#116;&#x6f;&#58;&#x66;o&#111;
1621
-        #        &#x40;&#101;&#x78;&#97;&#x6d;&#112;&#x6c;&#101;&#46;&#x63;&#111;
1622
-        #        &#x6d;">&#x66;o&#111;&#x40;&#101;&#x78;&#97;&#x6d;&#112;&#x6c;
1623
-        #        &#101;&#46;&#x63;&#111;&#x6d;</a></p>
1624
-        #
1625
-        #	Based by a filter by Matthew Wickline, posted to BBEdit-Talk.
1626
-        #   With some optimizations by Milian Wolff.
1627
-        #
1628
-        $addr  = "mailto:" . $addr;
1629
-        $chars = preg_split('/(?<!^)(?!$)/', $addr);
1630
-        $seed  = (int)abs(crc32($addr) / strlen($addr)); # Deterministic seed.
1631
-
1632
-        foreach ($chars as $key => $char) {
1633
-            $ord = ord($char);
1634
-            # Ignore non-ascii chars.
1635
-            if ($ord < 128) {
1636
-                $r = ($seed * (1 + $key)) % 100; # Pseudo-random function.
1637
-                # roughly 10% raw, 45% hex, 45% dec
1638
-                # '@' *must* be encoded. I insist.
1639
-                if ($r > 90 && $char != '@') /* do nothing */ {
1640
-                    ;
1641
-                } else if ($r < 45) {
1642
-                    $chars[$key] = '&#x' . dechex($ord) . ';';
1643
-                } else {
1644
-                    $chars[$key] = '&#' . $ord . ';';
1645
-                }
1646
-            }
1647
-        }
1648
-
1649
-        $addr = implode('', $chars);
1650
-        $text = implode('', array_slice($chars, 7)); # text without `mailto:`
1651
-        $addr = "<a href=\"$addr\">$text</a>";
1652
-
1653
-        return $addr;
1654
-    }
1655
-
1656
-    function parseSpan($str)
1657
-    {
1658
-        #
1659
-        # Take the string $str and parse it into tokens, hashing embeded HTML,
1660
-        # escaped characters and handling code spans.
1661
-        #
1662
-        $output = '';
1663
-
1664
-        $span_re = '{
1590
+			array(&$this, '_doAutoLinks_email_callback'),
1591
+			$text
1592
+		);
1593
+
1594
+		return $text;
1595
+	}
1596
+
1597
+	function _doAutoLinks_url_callback($matches)
1598
+	{
1599
+		$url  = $this->encodeAttribute($matches[1]);
1600
+		$link = "<a href=\"$url\">$url</a>";
1601
+		return $this->hashPart($link);
1602
+	}
1603
+
1604
+	function _doAutoLinks_email_callback($matches)
1605
+	{
1606
+		$address = $matches[1];
1607
+		$link    = $this->encodeEmailAddress($address);
1608
+		return $this->hashPart($link);
1609
+	}
1610
+
1611
+	function encodeEmailAddress($addr)
1612
+	{
1613
+		#
1614
+		#	Input: an email address, e.g. "[email protected]"
1615
+		#
1616
+		#	Output: the email address as a mailto link, with each character
1617
+		#		of the address encoded as either a decimal or hex entity, in
1618
+		#		the hopes of foiling most address harvesting spam bots. E.g.:
1619
+		#
1620
+		#	  <p><a href="&#109;&#x61;&#105;&#x6c;&#116;&#x6f;&#58;&#x66;o&#111;
1621
+		#        &#x40;&#101;&#x78;&#97;&#x6d;&#112;&#x6c;&#101;&#46;&#x63;&#111;
1622
+		#        &#x6d;">&#x66;o&#111;&#x40;&#101;&#x78;&#97;&#x6d;&#112;&#x6c;
1623
+		#        &#101;&#46;&#x63;&#111;&#x6d;</a></p>
1624
+		#
1625
+		#	Based by a filter by Matthew Wickline, posted to BBEdit-Talk.
1626
+		#   With some optimizations by Milian Wolff.
1627
+		#
1628
+		$addr  = "mailto:" . $addr;
1629
+		$chars = preg_split('/(?<!^)(?!$)/', $addr);
1630
+		$seed  = (int)abs(crc32($addr) / strlen($addr)); # Deterministic seed.
1631
+
1632
+		foreach ($chars as $key => $char) {
1633
+			$ord = ord($char);
1634
+			# Ignore non-ascii chars.
1635
+			if ($ord < 128) {
1636
+				$r = ($seed * (1 + $key)) % 100; # Pseudo-random function.
1637
+				# roughly 10% raw, 45% hex, 45% dec
1638
+				# '@' *must* be encoded. I insist.
1639
+				if ($r > 90 && $char != '@') /* do nothing */ {
1640
+					;
1641
+				} else if ($r < 45) {
1642
+					$chars[$key] = '&#x' . dechex($ord) . ';';
1643
+				} else {
1644
+					$chars[$key] = '&#' . $ord . ';';
1645
+				}
1646
+			}
1647
+		}
1648
+
1649
+		$addr = implode('', $chars);
1650
+		$text = implode('', array_slice($chars, 7)); # text without `mailto:`
1651
+		$addr = "<a href=\"$addr\">$text</a>";
1652
+
1653
+		return $addr;
1654
+	}
1655
+
1656
+	function parseSpan($str)
1657
+	{
1658
+		#
1659
+		# Take the string $str and parse it into tokens, hashing embeded HTML,
1660
+		# escaped characters and handling code spans.
1661
+		#
1662
+		$output = '';
1663
+
1664
+		$span_re = '{
1665 1665
 				(
1666 1666
 					\\\\' . $this->escape_chars_re . '
1667 1667
 				|
@@ -1687,142 +1687,142 @@  discard block
 block discarded – undo
1687 1687
 				)
1688 1688
 				}xs';
1689 1689
 
1690
-        while (1) {
1691
-            #
1692
-            # Each loop iteration seach for either the next tag, the next
1693
-            # openning code span marker, or the next escaped character.
1694
-            # Each token is then passed to handleSpanToken.
1695
-            #
1696
-            $parts = preg_split($span_re, $str, 2, PREG_SPLIT_DELIM_CAPTURE);
1697
-
1698
-            # Create token from text preceding tag.
1699
-            if ($parts[0] != "") {
1700
-                $output .= $parts[0];
1701
-            }
1702
-
1703
-            # Check if we reach the end.
1704
-            if (isset($parts[1])) {
1705
-                $output .= $this->handleSpanToken($parts[1], $parts[2]);
1706
-                $str = $parts[2];
1707
-            } else {
1708
-                break;
1709
-            }
1710
-        }
1711
-
1712
-        return $output;
1713
-    }
1714
-
1715
-    function handleSpanToken($token, &$str)
1716
-    {
1717
-        #
1718
-        # Handle $token provided by parseSpan by determining its nature and
1719
-        # returning the corresponding value that should replace it.
1720
-        #
1721
-        switch ($token{0}) {
1722
-            case "\\":
1723
-                return $this->hashPart("&#" . ord($token{1}) . ";");
1724
-            case "`":
1725
-                # Search for end marker in remaining text.
1726
-                if (preg_match(
1727
-                    '/^(.*?[^`])' . preg_quote($token) . '(?!`)(.*)$/sm',
1728
-                    $str,
1729
-                    $matches
1730
-                )) {
1731
-                    $str      = $matches[2];
1732
-                    $codespan = $this->makeCodeSpan($matches[1]);
1733
-                    return $this->hashPart($codespan);
1734
-                }
1735
-                return $token; // return as text since no ending marker found.
1736
-            default:
1737
-                return $this->hashPart($token);
1738
-        }
1739
-    }
1740
-
1741
-    function outdent($text)
1742
-    {
1743
-        #
1744
-        # Remove one level of line-leading tabs or spaces
1745
-        #
1746
-        return preg_replace('/^(\t|[ ]{1,' . $this->tab_width . '})/m', '', $text);
1747
-    }
1748
-
1749
-
1750
-    # String length function for detab. `_initDetab` will create a function to
1751
-    # hanlde UTF-8 if the default function does not exist.
1752
-    var $utf8_strlen = 'mb_strlen';
1753
-
1754
-    function detab($text)
1755
-    {
1756
-        #
1757
-        # Replace tabs with the appropriate amount of space.
1758
-        #
1759
-        # For each line we separate the line in blocks delemited by
1760
-        # tab characters. Then we reconstruct every line by adding the
1761
-        # appropriate number of space between each blocks.
1762
-
1763
-        $text = preg_replace_callback(
1764
-            '/^.*\t.*$/m',
1765
-            array(&$this, '_detab_callback'),
1766
-            $text
1767
-        );
1768
-
1769
-        return $text;
1770
-    }
1771
-
1772
-    function _detab_callback($matches)
1773
-    {
1774
-        $line   = $matches[0];
1775
-        $strlen = $this->utf8_strlen; # strlen function for UTF-8.
1776
-
1777
-        # Split in blocks.
1778
-        $blocks = explode("\t", $line);
1779
-        # Add each blocks to the line.
1780
-        $line = $blocks[0];
1781
-        unset($blocks[0]); # Do not add first block twice.
1782
-        foreach ($blocks as $block) {
1783
-            # Calculate amount of space, insert spaces, insert block.
1784
-            $amount = $this->tab_width -
1785
-                      $strlen($line, 'UTF-8') % $this->tab_width;
1786
-            $line .= str_repeat(" ", $amount) . $block;
1787
-        }
1788
-        return $line;
1789
-    }
1790
-
1791
-    function _initDetab()
1792
-    {
1793
-        #
1794
-        # Check for the availability of the function in the `utf8_strlen` property
1795
-        # (initially `mb_strlen`). If the function is not available, create a
1796
-        # function that will loosely count the number of UTF-8 characters with a
1797
-        # regular expression.
1798
-        #
1799
-        if (function_exists($this->utf8_strlen)) {
1800
-            return;
1801
-        }
1802
-        $this->utf8_strlen = create_function(
1803
-            '$text',
1804
-            'return preg_match_all(
1690
+		while (1) {
1691
+			#
1692
+			# Each loop iteration seach for either the next tag, the next
1693
+			# openning code span marker, or the next escaped character.
1694
+			# Each token is then passed to handleSpanToken.
1695
+			#
1696
+			$parts = preg_split($span_re, $str, 2, PREG_SPLIT_DELIM_CAPTURE);
1697
+
1698
+			# Create token from text preceding tag.
1699
+			if ($parts[0] != "") {
1700
+				$output .= $parts[0];
1701
+			}
1702
+
1703
+			# Check if we reach the end.
1704
+			if (isset($parts[1])) {
1705
+				$output .= $this->handleSpanToken($parts[1], $parts[2]);
1706
+				$str = $parts[2];
1707
+			} else {
1708
+				break;
1709
+			}
1710
+		}
1711
+
1712
+		return $output;
1713
+	}
1714
+
1715
+	function handleSpanToken($token, &$str)
1716
+	{
1717
+		#
1718
+		# Handle $token provided by parseSpan by determining its nature and
1719
+		# returning the corresponding value that should replace it.
1720
+		#
1721
+		switch ($token{0}) {
1722
+			case "\\":
1723
+				return $this->hashPart("&#" . ord($token{1}) . ";");
1724
+			case "`":
1725
+				# Search for end marker in remaining text.
1726
+				if (preg_match(
1727
+					'/^(.*?[^`])' . preg_quote($token) . '(?!`)(.*)$/sm',
1728
+					$str,
1729
+					$matches
1730
+				)) {
1731
+					$str      = $matches[2];
1732
+					$codespan = $this->makeCodeSpan($matches[1]);
1733
+					return $this->hashPart($codespan);
1734
+				}
1735
+				return $token; // return as text since no ending marker found.
1736
+			default:
1737
+				return $this->hashPart($token);
1738
+		}
1739
+	}
1740
+
1741
+	function outdent($text)
1742
+	{
1743
+		#
1744
+		# Remove one level of line-leading tabs or spaces
1745
+		#
1746
+		return preg_replace('/^(\t|[ ]{1,' . $this->tab_width . '})/m', '', $text);
1747
+	}
1748
+
1749
+
1750
+	# String length function for detab. `_initDetab` will create a function to
1751
+	# hanlde UTF-8 if the default function does not exist.
1752
+	var $utf8_strlen = 'mb_strlen';
1753
+
1754
+	function detab($text)
1755
+	{
1756
+		#
1757
+		# Replace tabs with the appropriate amount of space.
1758
+		#
1759
+		# For each line we separate the line in blocks delemited by
1760
+		# tab characters. Then we reconstruct every line by adding the
1761
+		# appropriate number of space between each blocks.
1762
+
1763
+		$text = preg_replace_callback(
1764
+			'/^.*\t.*$/m',
1765
+			array(&$this, '_detab_callback'),
1766
+			$text
1767
+		);
1768
+
1769
+		return $text;
1770
+	}
1771
+
1772
+	function _detab_callback($matches)
1773
+	{
1774
+		$line   = $matches[0];
1775
+		$strlen = $this->utf8_strlen; # strlen function for UTF-8.
1776
+
1777
+		# Split in blocks.
1778
+		$blocks = explode("\t", $line);
1779
+		# Add each blocks to the line.
1780
+		$line = $blocks[0];
1781
+		unset($blocks[0]); # Do not add first block twice.
1782
+		foreach ($blocks as $block) {
1783
+			# Calculate amount of space, insert spaces, insert block.
1784
+			$amount = $this->tab_width -
1785
+					  $strlen($line, 'UTF-8') % $this->tab_width;
1786
+			$line .= str_repeat(" ", $amount) . $block;
1787
+		}
1788
+		return $line;
1789
+	}
1790
+
1791
+	function _initDetab()
1792
+	{
1793
+		#
1794
+		# Check for the availability of the function in the `utf8_strlen` property
1795
+		# (initially `mb_strlen`). If the function is not available, create a
1796
+		# function that will loosely count the number of UTF-8 characters with a
1797
+		# regular expression.
1798
+		#
1799
+		if (function_exists($this->utf8_strlen)) {
1800
+			return;
1801
+		}
1802
+		$this->utf8_strlen = create_function(
1803
+			'$text',
1804
+			'return preg_match_all(
1805 1805
 			"/[\\\\x00-\\\\xBF]|[\\\\xC0-\\\\xFF][\\\\x80-\\\\xBF]*/",
1806 1806
 			$text, $m);'
1807
-        );
1808
-    }
1809
-
1810
-    function unhash($text)
1811
-    {
1812
-        #
1813
-        # Swap back in all the tags hashed by _HashHTMLBlocks.
1814
-        #
1815
-        return preg_replace_callback(
1816
-            '/(.)\x1A[0-9]+\1/',
1817
-            array(&$this, '_unhash_callback'),
1818
-            $text
1819
-        );
1820
-    }
1821
-
1822
-    function _unhash_callback($matches)
1823
-    {
1824
-        return $this->html_hashes[$matches[0]];
1825
-    }
1807
+		);
1808
+	}
1809
+
1810
+	function unhash($text)
1811
+	{
1812
+		#
1813
+		# Swap back in all the tags hashed by _HashHTMLBlocks.
1814
+		#
1815
+		return preg_replace_callback(
1816
+			'/(.)\x1A[0-9]+\1/',
1817
+			array(&$this, '_unhash_callback'),
1818
+			$text
1819
+		);
1820
+	}
1821
+
1822
+	function _unhash_callback($matches)
1823
+	{
1824
+		return $this->html_hashes[$matches[0]];
1825
+	}
1826 1826
 }
1827 1827
 
1828 1828
 #
@@ -1832,178 +1832,178 @@  discard block
 block discarded – undo
1832 1832
 class MarkdownExtra_Parser extends Markdown_Parser
1833 1833
 {
1834 1834
 
1835
-    ### Configuration Variables ###
1836
-
1837
-    # Prefix for footnote ids.
1838
-    var $fn_id_prefix = "";
1839
-
1840
-    # Optional title attribute for footnote links and backlinks.
1841
-    var $fn_link_title = MARKDOWN_FN_LINK_TITLE;
1842
-
1843
-    var $fn_backlink_title = MARKDOWN_FN_BACKLINK_TITLE;
1844
-
1845
-    # Optional class attribute for footnote links and backlinks.
1846
-    var $fn_link_class = MARKDOWN_FN_LINK_CLASS;
1847
-
1848
-    var $fn_backlink_class = MARKDOWN_FN_BACKLINK_CLASS;
1849
-
1850
-    # Optional class prefix for fenced code block.
1851
-    var $code_class_prefix = MARKDOWN_CODE_CLASS_PREFIX;
1835
+	### Configuration Variables ###
1836
+
1837
+	# Prefix for footnote ids.
1838
+	var $fn_id_prefix = "";
1839
+
1840
+	# Optional title attribute for footnote links and backlinks.
1841
+	var $fn_link_title = MARKDOWN_FN_LINK_TITLE;
1842
+
1843
+	var $fn_backlink_title = MARKDOWN_FN_BACKLINK_TITLE;
1844
+
1845
+	# Optional class attribute for footnote links and backlinks.
1846
+	var $fn_link_class = MARKDOWN_FN_LINK_CLASS;
1847
+
1848
+	var $fn_backlink_class = MARKDOWN_FN_BACKLINK_CLASS;
1849
+
1850
+	# Optional class prefix for fenced code block.
1851
+	var $code_class_prefix = MARKDOWN_CODE_CLASS_PREFIX;
1852 1852
 
1853
-    # Class attribute for code blocks goes on the `code` tag;
1854
-    # setting this to true will put attributes on the `pre` tag instead.
1855
-    var $code_attr_on_pre = MARKDOWN_CODE_ATTR_ON_PRE;
1853
+	# Class attribute for code blocks goes on the `code` tag;
1854
+	# setting this to true will put attributes on the `pre` tag instead.
1855
+	var $code_attr_on_pre = MARKDOWN_CODE_ATTR_ON_PRE;
1856 1856
 
1857
-    # Predefined abbreviations.
1858
-    var $predef_abbr = array();
1859
-
1860
-    ### Parser Implementation ###
1861
-
1862
-    function __construct()
1863
-    {
1864
-        #
1865
-        # Constructor function. Initialize the parser object.
1866
-        #
1867
-        # Add extra escapable characters before parent constructor
1868
-        # initialize the table.
1869
-        $this->escape_chars .= ':|';
1870
-
1871
-        # Insert extra document, block, and span transformations.
1872
-        # Parent constructor will do the sorting.
1873
-        $this->document_gamut += array(
1874
-            "doFencedCodeBlocks" => 5,
1875
-            "stripFootnotes"     => 15,
1876
-            "stripAbbreviations" => 25,
1877
-            "appendFootnotes"    => 50,
1878
-        );
1879
-        $this->block_gamut += array(
1880
-            "doFencedCodeBlocks" => 5,
1881
-            "doTables"           => 15,
1882
-            "doDefLists"         => 45,
1883
-        );
1884
-        $this->span_gamut += array(
1885
-            "doFootnotes"     => 5,
1886
-            "doAbbreviations" => 70,
1887
-        );
1888
-
1889
-        parent::__construct();
1890
-    }
1891
-
1892
-    # Extra variables used during extra transformations.
1893
-    var $footnotes = array();
1894
-
1895
-    var $footnotes_ordered = array();
1896
-
1897
-    var $footnotes_ref_count = array();
1898
-
1899
-    var $footnotes_numbers = array();
1900
-
1901
-    var $abbr_desciptions = array();
1902
-
1903
-    var $abbr_word_re = '';
1904
-
1905
-    # Give the current footnote number.
1906
-    var $footnote_counter = 1;
1907
-
1908
-    function setup()
1909
-    {
1910
-        #
1911
-        # Setting up Extra-specific variables.
1912
-        #
1913
-        parent::setup();
1914
-
1915
-        $this->footnotes           = array();
1916
-        $this->footnotes_ordered   = array();
1917
-        $this->footnotes_ref_count = array();
1918
-        $this->footnotes_numbers   = array();
1919
-        $this->abbr_desciptions    = array();
1920
-        $this->abbr_word_re        = '';
1921
-        $this->footnote_counter    = 1;
1922
-
1923
-        foreach ($this->predef_abbr as $abbr_word => $abbr_desc) {
1924
-            if ($this->abbr_word_re) {
1925
-                $this->abbr_word_re .= '|';
1926
-            }
1927
-            $this->abbr_word_re .= preg_quote($abbr_word);
1928
-            $this->abbr_desciptions[$abbr_word] = trim($abbr_desc);
1929
-        }
1930
-    }
1931
-
1932
-    function teardown()
1933
-    {
1934
-        #
1935
-        # Clearing Extra-specific variables.
1936
-        #
1937
-        $this->footnotes           = array();
1938
-        $this->footnotes_ordered   = array();
1939
-        $this->footnotes_ref_count = array();
1940
-        $this->footnotes_numbers   = array();
1941
-        $this->abbr_desciptions    = array();
1942
-        $this->abbr_word_re        = '';
1943
-
1944
-        parent::teardown();
1945
-    }
1946
-
1947
-
1948
-    ### Extra Attribute Parser ###
1949
-
1950
-    # Expression to use to catch attributes (includes the braces)
1951
-    var $id_class_attr_catch_re = '\{((?:[ ]*[#.][-_:a-zA-Z0-9]+){1,})[ ]*\}';
1952
-
1953
-    # Expression to use when parsing in a context when no capture is desired
1954
-    var $id_class_attr_nocatch_re = '\{(?:[ ]*[#.][-_:a-zA-Z0-9]+){1,}[ ]*\}';
1955
-
1956
-    function doExtraAttributes($tag_name, $attr)
1957
-    {
1958
-        #
1959
-        # Parse attributes caught by the $this->id_class_attr_catch_re expression
1960
-        # and return the HTML-formatted list of attributes.
1961
-        #
1962
-        # Currently supported attributes are .class and #id.
1963
-        #
1964
-        if (empty($attr)) {
1965
-            return "";
1966
-        }
1967
-
1968
-        # Split on components
1969
-        preg_match_all('/[#.][-_:a-zA-Z0-9]+/', $attr, $matches);
1970
-        $elements = $matches[0];
1971
-
1972
-        # handle classes and ids (only first id taken into account)
1973
-        $classes = array();
1974
-        $id      = false;
1975
-        foreach ($elements as $element) {
1976
-            if ($element{0} == '.') {
1977
-                $classes[] = substr($element, 1);
1978
-            } else if ($element{0} == '#') {
1979
-                if ($id === false) {
1980
-                    $id = substr($element, 1);
1981
-                }
1982
-            }
1983
-        }
1984
-
1985
-        # compose attributes as string
1986
-        $attr_str = "";
1987
-        if (!empty($id)) {
1988
-            $attr_str .= ' id="' . $id . '"';
1989
-        }
1990
-        if (!empty($classes)) {
1991
-            $attr_str .= ' class="' . implode(" ", $classes) . '"';
1992
-        }
1993
-        return $attr_str;
1994
-    }
1995
-
1996
-    function stripLinkDefinitions($text)
1997
-    {
1998
-        #
1999
-        # Strips link definitions from text, stores the URLs and titles in
2000
-        # hash references.
2001
-        #
2002
-        $less_than_tab = $this->tab_width - 1;
2003
-
2004
-        # Link defs are in the form: ^[id]: url "optional title"
2005
-        $text = preg_replace_callback(
2006
-            '{
1857
+	# Predefined abbreviations.
1858
+	var $predef_abbr = array();
1859
+
1860
+	### Parser Implementation ###
1861
+
1862
+	function __construct()
1863
+	{
1864
+		#
1865
+		# Constructor function. Initialize the parser object.
1866
+		#
1867
+		# Add extra escapable characters before parent constructor
1868
+		# initialize the table.
1869
+		$this->escape_chars .= ':|';
1870
+
1871
+		# Insert extra document, block, and span transformations.
1872
+		# Parent constructor will do the sorting.
1873
+		$this->document_gamut += array(
1874
+			"doFencedCodeBlocks" => 5,
1875
+			"stripFootnotes"     => 15,
1876
+			"stripAbbreviations" => 25,
1877
+			"appendFootnotes"    => 50,
1878
+		);
1879
+		$this->block_gamut += array(
1880
+			"doFencedCodeBlocks" => 5,
1881
+			"doTables"           => 15,
1882
+			"doDefLists"         => 45,
1883
+		);
1884
+		$this->span_gamut += array(
1885
+			"doFootnotes"     => 5,
1886
+			"doAbbreviations" => 70,
1887
+		);
1888
+
1889
+		parent::__construct();
1890
+	}
1891
+
1892
+	# Extra variables used during extra transformations.
1893
+	var $footnotes = array();
1894
+
1895
+	var $footnotes_ordered = array();
1896
+
1897
+	var $footnotes_ref_count = array();
1898
+
1899
+	var $footnotes_numbers = array();
1900
+
1901
+	var $abbr_desciptions = array();
1902
+
1903
+	var $abbr_word_re = '';
1904
+
1905
+	# Give the current footnote number.
1906
+	var $footnote_counter = 1;
1907
+
1908
+	function setup()
1909
+	{
1910
+		#
1911
+		# Setting up Extra-specific variables.
1912
+		#
1913
+		parent::setup();
1914
+
1915
+		$this->footnotes           = array();
1916
+		$this->footnotes_ordered   = array();
1917
+		$this->footnotes_ref_count = array();
1918
+		$this->footnotes_numbers   = array();
1919
+		$this->abbr_desciptions    = array();
1920
+		$this->abbr_word_re        = '';
1921
+		$this->footnote_counter    = 1;
1922
+
1923
+		foreach ($this->predef_abbr as $abbr_word => $abbr_desc) {
1924
+			if ($this->abbr_word_re) {
1925
+				$this->abbr_word_re .= '|';
1926
+			}
1927
+			$this->abbr_word_re .= preg_quote($abbr_word);
1928
+			$this->abbr_desciptions[$abbr_word] = trim($abbr_desc);
1929
+		}
1930
+	}
1931
+
1932
+	function teardown()
1933
+	{
1934
+		#
1935
+		# Clearing Extra-specific variables.
1936
+		#
1937
+		$this->footnotes           = array();
1938
+		$this->footnotes_ordered   = array();
1939
+		$this->footnotes_ref_count = array();
1940
+		$this->footnotes_numbers   = array();
1941
+		$this->abbr_desciptions    = array();
1942
+		$this->abbr_word_re        = '';
1943
+
1944
+		parent::teardown();
1945
+	}
1946
+
1947
+
1948
+	### Extra Attribute Parser ###
1949
+
1950
+	# Expression to use to catch attributes (includes the braces)
1951
+	var $id_class_attr_catch_re = '\{((?:[ ]*[#.][-_:a-zA-Z0-9]+){1,})[ ]*\}';
1952
+
1953
+	# Expression to use when parsing in a context when no capture is desired
1954
+	var $id_class_attr_nocatch_re = '\{(?:[ ]*[#.][-_:a-zA-Z0-9]+){1,}[ ]*\}';
1955
+
1956
+	function doExtraAttributes($tag_name, $attr)
1957
+	{
1958
+		#
1959
+		# Parse attributes caught by the $this->id_class_attr_catch_re expression
1960
+		# and return the HTML-formatted list of attributes.
1961
+		#
1962
+		# Currently supported attributes are .class and #id.
1963
+		#
1964
+		if (empty($attr)) {
1965
+			return "";
1966
+		}
1967
+
1968
+		# Split on components
1969
+		preg_match_all('/[#.][-_:a-zA-Z0-9]+/', $attr, $matches);
1970
+		$elements = $matches[0];
1971
+
1972
+		# handle classes and ids (only first id taken into account)
1973
+		$classes = array();
1974
+		$id      = false;
1975
+		foreach ($elements as $element) {
1976
+			if ($element{0} == '.') {
1977
+				$classes[] = substr($element, 1);
1978
+			} else if ($element{0} == '#') {
1979
+				if ($id === false) {
1980
+					$id = substr($element, 1);
1981
+				}
1982
+			}
1983
+		}
1984
+
1985
+		# compose attributes as string
1986
+		$attr_str = "";
1987
+		if (!empty($id)) {
1988
+			$attr_str .= ' id="' . $id . '"';
1989
+		}
1990
+		if (!empty($classes)) {
1991
+			$attr_str .= ' class="' . implode(" ", $classes) . '"';
1992
+		}
1993
+		return $attr_str;
1994
+	}
1995
+
1996
+	function stripLinkDefinitions($text)
1997
+	{
1998
+		#
1999
+		# Strips link definitions from text, stores the URLs and titles in
2000
+		# hash references.
2001
+		#
2002
+		$less_than_tab = $this->tab_width - 1;
2003
+
2004
+		# Link defs are in the form: ^[id]: url "optional title"
2005
+		$text = preg_replace_callback(
2006
+			'{
2007 2007
 							^[ ]{0,' . $less_than_tab . '}\[(.+)\][ ]?:	# id = $1
2008 2008
 							  [ ]*
2009 2009
 							  \n?				# maybe *one* newline
@@ -2026,117 +2026,117 @@  discard block
 block discarded – undo
2026 2026
 					(?:[ ]* ' . $this->id_class_attr_catch_re . ' )?  # $5 = extra id & class attr
2027 2027
 							(?:\n+|\Z)
2028 2028
 			}xm',
2029
-            array(&$this, '_stripLinkDefinitions_callback'),
2030
-            $text
2031
-        );
2032
-        return $text;
2033
-    }
2034
-
2035
-    function _stripLinkDefinitions_callback($matches)
2036
-    {
2037
-        $link_id                  = strtolower($matches[1]);
2038
-        $url                      = $matches[2] == '' ? $matches[3] : $matches[2];
2039
-        $this->urls[$link_id]     = $url;
2040
-        $this->titles[$link_id]   =& $matches[4];
2041
-        $this->ref_attr[$link_id] = $this->doExtraAttributes("", $dummy =& $matches[5]);
2042
-        return ''; # String that will replace the block
2043
-    }
2044
-
2045
-
2046
-    ### HTML Block Parser ###
2047
-
2048
-    # Tags that are always treated as block tags:
2049
-    var $block_tags_re = 'p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|address|form|fieldset|iframe|hr|legend|article|section|nav|aside|hgroup|header|footer|figcaption';
2050
-
2051
-    # Tags treated as block tags only if the opening tag is alone on its line:
2052
-    var $context_block_tags_re = 'script|noscript|ins|del|iframe|object|source|track|param|math|svg|canvas|audio|video';
2053
-
2054
-    # Tags where markdown="1" default to span mode:
2055
-    var $contain_span_tags_re = 'p|h[1-6]|li|dd|dt|td|th|legend|address';
2056
-
2057
-    # Tags which must not have their contents modified, no matter where
2058
-    # they appear:
2059
-    var $clean_tags_re = 'script|math|svg';
2060
-
2061
-    # Tags that do not need to be closed.
2062
-    var $auto_close_tags_re = 'hr|img|param|source|track';
2063
-
2064
-    function hashHTMLBlocks($text)
2065
-    {
2066
-        #
2067
-        # Hashify HTML Blocks and "clean tags".
2068
-        #
2069
-        # We only want to do this for block-level HTML tags, such as headers,
2070
-        # lists, and tables. That's because we still want to wrap <p>s around
2071
-        # "paragraphs" that are wrapped in non-block-level tags, such as anchors,
2072
-        # phrase emphasis, and spans. The list of tags we're looking for is
2073
-        # hard-coded.
2074
-        #
2075
-        # This works by calling _HashHTMLBlocks_InMarkdown, which then calls
2076
-        # _HashHTMLBlocks_InHTML when it encounter block tags. When the markdown="1"
2077
-        # attribute is found within a tag, _HashHTMLBlocks_InHTML calls back
2078
-        #  _HashHTMLBlocks_InMarkdown to handle the Markdown syntax within the tag.
2079
-        # These two functions are calling each other. It's recursive!
2080
-        #
2081
-        if ($this->no_markup) {
2082
-            return $text;
2083
-        }
2084
-
2085
-        #
2086
-        # Call the HTML-in-Markdown hasher.
2087
-        #
2088
-        list($text,) = $this->_hashHTMLBlocks_inMarkdown($text);
2089
-
2090
-        return $text;
2091
-    }
2092
-
2093
-    function _hashHTMLBlocks_inMarkdown(
2094
-        $text,
2095
-        $indent = 0,
2096
-        $enclosing_tag_re = '',
2097
-        $span = false
2098
-    ) {
2099
-        #
2100
-        # Parse markdown text, calling _HashHTMLBlocks_InHTML for block tags.
2101
-        #
2102
-        # *   $indent is the number of space to be ignored when checking for code
2103
-        #     blocks. This is important because if we don't take the indent into
2104
-        #     account, something like this (which looks right) won't work as expected:
2105
-        #
2106
-        #     <div>
2107
-        #         <div markdown="1">
2108
-        #         Hello World.  <-- Is this a Markdown code block or text?
2109
-        #         </div>  <-- Is this a Markdown code block or a real tag?
2110
-        #     <div>
2111
-        #
2112
-        #     If you don't like this, just don't indent the tag on which
2113
-        #     you apply the markdown="1" attribute.
2114
-        #
2115
-        # *   If $enclosing_tag_re is not empty, stops at the first unmatched closing
2116
-        #     tag with that name. Nested tags supported.
2117
-        #
2118
-        # *   If $span is true, text inside must treated as span. So any double
2119
-        #     newline will be replaced by a single newline so that it does not create
2120
-        #     paragraphs.
2121
-        #
2122
-        # Returns an array of that form: ( processed text , remaining text )
2123
-        #
2124
-        if ($text === '') {
2125
-            return array('', '');
2126
-        }
2127
-
2128
-        # Regex to check for the presense of newlines around a block tag.
2129
-        $newline_before_re = '/(?:^\n?|\n\n)*$/';
2130
-        $newline_after_re  =
2131
-            '{
2029
+			array(&$this, '_stripLinkDefinitions_callback'),
2030
+			$text
2031
+		);
2032
+		return $text;
2033
+	}
2034
+
2035
+	function _stripLinkDefinitions_callback($matches)
2036
+	{
2037
+		$link_id                  = strtolower($matches[1]);
2038
+		$url                      = $matches[2] == '' ? $matches[3] : $matches[2];
2039
+		$this->urls[$link_id]     = $url;
2040
+		$this->titles[$link_id]   =& $matches[4];
2041
+		$this->ref_attr[$link_id] = $this->doExtraAttributes("", $dummy =& $matches[5]);
2042
+		return ''; # String that will replace the block
2043
+	}
2044
+
2045
+
2046
+	### HTML Block Parser ###
2047
+
2048
+	# Tags that are always treated as block tags:
2049
+	var $block_tags_re = 'p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|address|form|fieldset|iframe|hr|legend|article|section|nav|aside|hgroup|header|footer|figcaption';
2050
+
2051
+	# Tags treated as block tags only if the opening tag is alone on its line:
2052
+	var $context_block_tags_re = 'script|noscript|ins|del|iframe|object|source|track|param|math|svg|canvas|audio|video';
2053
+
2054
+	# Tags where markdown="1" default to span mode:
2055
+	var $contain_span_tags_re = 'p|h[1-6]|li|dd|dt|td|th|legend|address';
2056
+
2057
+	# Tags which must not have their contents modified, no matter where
2058
+	# they appear:
2059
+	var $clean_tags_re = 'script|math|svg';
2060
+
2061
+	# Tags that do not need to be closed.
2062
+	var $auto_close_tags_re = 'hr|img|param|source|track';
2063
+
2064
+	function hashHTMLBlocks($text)
2065
+	{
2066
+		#
2067
+		# Hashify HTML Blocks and "clean tags".
2068
+		#
2069
+		# We only want to do this for block-level HTML tags, such as headers,
2070
+		# lists, and tables. That's because we still want to wrap <p>s around
2071
+		# "paragraphs" that are wrapped in non-block-level tags, such as anchors,
2072
+		# phrase emphasis, and spans. The list of tags we're looking for is
2073
+		# hard-coded.
2074
+		#
2075
+		# This works by calling _HashHTMLBlocks_InMarkdown, which then calls
2076
+		# _HashHTMLBlocks_InHTML when it encounter block tags. When the markdown="1"
2077
+		# attribute is found within a tag, _HashHTMLBlocks_InHTML calls back
2078
+		#  _HashHTMLBlocks_InMarkdown to handle the Markdown syntax within the tag.
2079
+		# These two functions are calling each other. It's recursive!
2080
+		#
2081
+		if ($this->no_markup) {
2082
+			return $text;
2083
+		}
2084
+
2085
+		#
2086
+		# Call the HTML-in-Markdown hasher.
2087
+		#
2088
+		list($text,) = $this->_hashHTMLBlocks_inMarkdown($text);
2089
+
2090
+		return $text;
2091
+	}
2092
+
2093
+	function _hashHTMLBlocks_inMarkdown(
2094
+		$text,
2095
+		$indent = 0,
2096
+		$enclosing_tag_re = '',
2097
+		$span = false
2098
+	) {
2099
+		#
2100
+		# Parse markdown text, calling _HashHTMLBlocks_InHTML for block tags.
2101
+		#
2102
+		# *   $indent is the number of space to be ignored when checking for code
2103
+		#     blocks. This is important because if we don't take the indent into
2104
+		#     account, something like this (which looks right) won't work as expected:
2105
+		#
2106
+		#     <div>
2107
+		#         <div markdown="1">
2108
+		#         Hello World.  <-- Is this a Markdown code block or text?
2109
+		#         </div>  <-- Is this a Markdown code block or a real tag?
2110
+		#     <div>
2111
+		#
2112
+		#     If you don't like this, just don't indent the tag on which
2113
+		#     you apply the markdown="1" attribute.
2114
+		#
2115
+		# *   If $enclosing_tag_re is not empty, stops at the first unmatched closing
2116
+		#     tag with that name. Nested tags supported.
2117
+		#
2118
+		# *   If $span is true, text inside must treated as span. So any double
2119
+		#     newline will be replaced by a single newline so that it does not create
2120
+		#     paragraphs.
2121
+		#
2122
+		# Returns an array of that form: ( processed text , remaining text )
2123
+		#
2124
+		if ($text === '') {
2125
+			return array('', '');
2126
+		}
2127
+
2128
+		# Regex to check for the presense of newlines around a block tag.
2129
+		$newline_before_re = '/(?:^\n?|\n\n)*$/';
2130
+		$newline_after_re  =
2131
+			'{
2132 2132
 				^						# Start of text following the tag.
2133 2133
 				(?>[ ]*<!--.*?-->)?		# Optional comment.
2134 2134
 				[ ]*\n					# Must be followed by newline.
2135 2135
 			}xs';
2136 2136
 
2137
-        # Regex to match any tag.
2138
-        $block_tag_re =
2139
-            '{
2137
+		# Regex to match any tag.
2138
+		$block_tag_re =
2139
+			'{
2140 2140
 				(					# $2: Capture whole tag.
2141 2141
 					</?					# Any opening or closing tag.
2142 2142
 						(?>				# Tag name.
@@ -2187,177 +2187,177 @@  discard block
 block discarded – undo
2187 2187
 				)
2188 2188
 			}xs';
2189 2189
 
2190
-        $depth  = 0; # Current depth inside the tag tree.
2191
-        $parsed = ""; # Parsed text that will be returned.
2192
-
2193
-        #
2194
-        # Loop through every tag until we find the closing tag of the parent
2195
-        # or loop until reaching the end of text if no parent tag specified.
2196
-        #
2197
-        do {
2198
-            #
2199
-            # Split the text using the first $tag_match pattern found.
2200
-            # Text before  pattern will be first in the array, text after
2201
-            # pattern will be at the end, and between will be any catches made
2202
-            # by the pattern.
2203
-            #
2204
-            $parts = preg_split(
2205
-                $block_tag_re,
2206
-                $text,
2207
-                2,
2208
-                PREG_SPLIT_DELIM_CAPTURE
2209
-            );
2210
-
2211
-            # If in Markdown span mode, add a empty-string span-level hash
2212
-            # after each newline to prevent triggering any block element.
2213
-            if ($span) {
2214
-                $void     = $this->hashPart("", ':');
2215
-                $newline  = "$void\n";
2216
-                $parts[0] = $void . str_replace("\n", $newline, $parts[0]) . $void;
2217
-            }
2218
-
2219
-            $parsed .= $parts[0]; # Text before current tag.
2220
-
2221
-            # If end of $text has been reached. Stop loop.
2222
-            if (count($parts) < 3) {
2223
-                $text = "";
2224
-                break;
2225
-            }
2226
-
2227
-            $tag    = $parts[1]; # Tag to handle.
2228
-            $text   = $parts[2]; # Remaining text after current tag.
2229
-            $tag_re = preg_quote($tag); # For use in a regular expression.
2230
-
2231
-            #
2232
-            # Check for: Code span marker
2233
-            #
2234
-            if ($tag{0} == "`") {
2235
-                # Find corresponding end marker.
2236
-                $tag_re = preg_quote($tag);
2237
-                if (preg_match(
2238
-                    '{^(?>.+?|\n(?!\n))*?(?<!`)' . $tag_re . '(?!`)}',
2239
-                    $text,
2240
-                    $matches
2241
-                )) {
2242
-                    # End marker found: pass text unchanged until marker.
2243
-                    $parsed .= $tag . $matches[0];
2244
-                    $text = substr($text, strlen($matches[0]));
2245
-                } else {
2246
-                    # Unmatched marker: just skip it.
2247
-                    $parsed .= $tag;
2248
-                }
2249
-            }
2250
-            #
2251
-            # Check for: Fenced code block marker.
2252
-            #
2253
-            else if (preg_match('{^\n?([ ]{0,' . ($indent + 3) . '})(~+)}', $tag, $capture)) {
2254
-                # Fenced code block marker: find matching end marker.
2255
-                $fence_indent = strlen($capture[1]); # use captured indent in re
2256
-                $fence_re     = $capture[2]; # use captured fence in re
2257
-                if (preg_match(
2258
-                    '{^(?>.*\n)*?[ ]{' . ($fence_indent) . '}' . $fence_re . '[ ]*(?:\n|$)}',
2259
-                    $text,
2260
-                    $matches
2261
-                )) {
2262
-                    # End marker found: pass text unchanged until marker.
2263
-                    $parsed .= $tag . $matches[0];
2264
-                    $text = substr($text, strlen($matches[0]));
2265
-                } else {
2266
-                    # No end marker: just skip it.
2267
-                    $parsed .= $tag;
2268
-                }
2269
-            }
2270
-            #
2271
-            # Check for: Indented code block.
2272
-            #
2273
-            else if ($tag{0} == "\n" || $tag{0} == " ") {
2274
-                # Indented code block: pass it unchanged, will be handled
2275
-                # later.
2276
-                $parsed .= $tag;
2277
-            }
2278
-            #
2279
-            # Check for: Opening Block level tag or
2280
-            #            Opening Context Block tag (like ins and del)
2281
-            #               used as a block tag (tag is alone on it's line).
2282
-            #
2283
-            else if (preg_match('{^<(?:' . $this->block_tags_re . ')\b}', $tag) ||
2284
-                     (preg_match('{^<(?:' . $this->context_block_tags_re . ')\b}', $tag) &&
2285
-                      preg_match($newline_before_re, $parsed) &&
2286
-                      preg_match($newline_after_re, $text))
2287
-            ) {
2288
-                # Need to parse tag and following text using the HTML parser.
2289
-                list($block_text, $text) =
2290
-                    $this->_hashHTMLBlocks_inHTML($tag . $text, "hashBlock", true);
2291
-
2292
-                # Make sure it stays outside of any paragraph by adding newlines.
2293
-                $parsed .= "\n\n$block_text\n\n";
2294
-            }
2295
-            #
2296
-            # Check for: Clean tag (like script, math)
2297
-            #            HTML Comments, processing instructions.
2298
-            #
2299
-            else if (preg_match('{^<(?:' . $this->clean_tags_re . ')\b}', $tag) ||
2300
-                     $tag{1} == '!' || $tag{1} == '?'
2301
-            ) {
2302
-                # Need to parse tag and following text using the HTML parser.
2303
-                # (don't check for markdown attribute)
2304
-                list($block_text, $text) =
2305
-                    $this->_hashHTMLBlocks_inHTML($tag . $text, "hashClean", false);
2306
-
2307
-                $parsed .= $block_text;
2308
-            }
2309
-            #
2310
-            # Check for: Tag with same name as enclosing tag.
2311
-            #
2312
-            else if ($enclosing_tag_re !== '' &&
2313
-                     # Same name as enclosing tag.
2314
-                     preg_match('{^</?(?:' . $enclosing_tag_re . ')\b}', $tag)
2315
-            ) {
2316
-                #
2317
-                # Increase/decrease nested tag count.
2318
-                #
2319
-                if ($tag{1} == '/') {
2320
-                    $depth --;
2321
-                } else if ($tag{strlen($tag) - 2} != '/') {
2322
-                    $depth ++;
2323
-                }
2324
-
2325
-                if ($depth < 0) {
2326
-                    #
2327
-                    # Going out of parent element. Clean up and break so we
2328
-                    # return to the calling function.
2329
-                    #
2330
-                    $text = $tag . $text;
2331
-                    break;
2332
-                }
2333
-
2334
-                $parsed .= $tag;
2335
-            } else {
2336
-                $parsed .= $tag;
2337
-            }
2338
-        } while ($depth >= 0);
2339
-
2340
-        return array($parsed, $text);
2341
-    }
2342
-
2343
-    function _hashHTMLBlocks_inHTML($text, $hash_method, $md_attr)
2344
-    {
2345
-        #
2346
-        # Parse HTML, calling _HashHTMLBlocks_InMarkdown for block tags.
2347
-        #
2348
-        # *   Calls $hash_method to convert any blocks.
2349
-        # *   Stops when the first opening tag closes.
2350
-        # *   $md_attr indicate if the use of the `markdown="1"` attribute is allowed.
2351
-        #     (it is not inside clean tags)
2352
-        #
2353
-        # Returns an array of that form: ( processed text , remaining text )
2354
-        #
2355
-        if ($text === '') {
2356
-            return array('', '');
2357
-        }
2358
-
2359
-        # Regex to match `markdown` attribute inside of a tag.
2360
-        $markdown_attr_re = '
2190
+		$depth  = 0; # Current depth inside the tag tree.
2191
+		$parsed = ""; # Parsed text that will be returned.
2192
+
2193
+		#
2194
+		# Loop through every tag until we find the closing tag of the parent
2195
+		# or loop until reaching the end of text if no parent tag specified.
2196
+		#
2197
+		do {
2198
+			#
2199
+			# Split the text using the first $tag_match pattern found.
2200
+			# Text before  pattern will be first in the array, text after
2201
+			# pattern will be at the end, and between will be any catches made
2202
+			# by the pattern.
2203
+			#
2204
+			$parts = preg_split(
2205
+				$block_tag_re,
2206
+				$text,
2207
+				2,
2208
+				PREG_SPLIT_DELIM_CAPTURE
2209
+			);
2210
+
2211
+			# If in Markdown span mode, add a empty-string span-level hash
2212
+			# after each newline to prevent triggering any block element.
2213
+			if ($span) {
2214
+				$void     = $this->hashPart("", ':');
2215
+				$newline  = "$void\n";
2216
+				$parts[0] = $void . str_replace("\n", $newline, $parts[0]) . $void;
2217
+			}
2218
+
2219
+			$parsed .= $parts[0]; # Text before current tag.
2220
+
2221
+			# If end of $text has been reached. Stop loop.
2222
+			if (count($parts) < 3) {
2223
+				$text = "";
2224
+				break;
2225
+			}
2226
+
2227
+			$tag    = $parts[1]; # Tag to handle.
2228
+			$text   = $parts[2]; # Remaining text after current tag.
2229
+			$tag_re = preg_quote($tag); # For use in a regular expression.
2230
+
2231
+			#
2232
+			# Check for: Code span marker
2233
+			#
2234
+			if ($tag{0} == "`") {
2235
+				# Find corresponding end marker.
2236
+				$tag_re = preg_quote($tag);
2237
+				if (preg_match(
2238
+					'{^(?>.+?|\n(?!\n))*?(?<!`)' . $tag_re . '(?!`)}',
2239
+					$text,
2240
+					$matches
2241
+				)) {
2242
+					# End marker found: pass text unchanged until marker.
2243
+					$parsed .= $tag . $matches[0];
2244
+					$text = substr($text, strlen($matches[0]));
2245
+				} else {
2246
+					# Unmatched marker: just skip it.
2247
+					$parsed .= $tag;
2248
+				}
2249
+			}
2250
+			#
2251
+			# Check for: Fenced code block marker.
2252
+			#
2253
+			else if (preg_match('{^\n?([ ]{0,' . ($indent + 3) . '})(~+)}', $tag, $capture)) {
2254
+				# Fenced code block marker: find matching end marker.
2255
+				$fence_indent = strlen($capture[1]); # use captured indent in re
2256
+				$fence_re     = $capture[2]; # use captured fence in re
2257
+				if (preg_match(
2258
+					'{^(?>.*\n)*?[ ]{' . ($fence_indent) . '}' . $fence_re . '[ ]*(?:\n|$)}',
2259
+					$text,
2260
+					$matches
2261
+				)) {
2262
+					# End marker found: pass text unchanged until marker.
2263
+					$parsed .= $tag . $matches[0];
2264
+					$text = substr($text, strlen($matches[0]));
2265
+				} else {
2266
+					# No end marker: just skip it.
2267
+					$parsed .= $tag;
2268
+				}
2269
+			}
2270
+			#
2271
+			# Check for: Indented code block.
2272
+			#
2273
+			else if ($tag{0} == "\n" || $tag{0} == " ") {
2274
+				# Indented code block: pass it unchanged, will be handled
2275
+				# later.
2276
+				$parsed .= $tag;
2277
+			}
2278
+			#
2279
+			# Check for: Opening Block level tag or
2280
+			#            Opening Context Block tag (like ins and del)
2281
+			#               used as a block tag (tag is alone on it's line).
2282
+			#
2283
+			else if (preg_match('{^<(?:' . $this->block_tags_re . ')\b}', $tag) ||
2284
+					 (preg_match('{^<(?:' . $this->context_block_tags_re . ')\b}', $tag) &&
2285
+					  preg_match($newline_before_re, $parsed) &&
2286
+					  preg_match($newline_after_re, $text))
2287
+			) {
2288
+				# Need to parse tag and following text using the HTML parser.
2289
+				list($block_text, $text) =
2290
+					$this->_hashHTMLBlocks_inHTML($tag . $text, "hashBlock", true);
2291
+
2292
+				# Make sure it stays outside of any paragraph by adding newlines.
2293
+				$parsed .= "\n\n$block_text\n\n";
2294
+			}
2295
+			#
2296
+			# Check for: Clean tag (like script, math)
2297
+			#            HTML Comments, processing instructions.
2298
+			#
2299
+			else if (preg_match('{^<(?:' . $this->clean_tags_re . ')\b}', $tag) ||
2300
+					 $tag{1} == '!' || $tag{1} == '?'
2301
+			) {
2302
+				# Need to parse tag and following text using the HTML parser.
2303
+				# (don't check for markdown attribute)
2304
+				list($block_text, $text) =
2305
+					$this->_hashHTMLBlocks_inHTML($tag . $text, "hashClean", false);
2306
+
2307
+				$parsed .= $block_text;
2308
+			}
2309
+			#
2310
+			# Check for: Tag with same name as enclosing tag.
2311
+			#
2312
+			else if ($enclosing_tag_re !== '' &&
2313
+					 # Same name as enclosing tag.
2314
+					 preg_match('{^</?(?:' . $enclosing_tag_re . ')\b}', $tag)
2315
+			) {
2316
+				#
2317
+				# Increase/decrease nested tag count.
2318
+				#
2319
+				if ($tag{1} == '/') {
2320
+					$depth --;
2321
+				} else if ($tag{strlen($tag) - 2} != '/') {
2322
+					$depth ++;
2323
+				}
2324
+
2325
+				if ($depth < 0) {
2326
+					#
2327
+					# Going out of parent element. Clean up and break so we
2328
+					# return to the calling function.
2329
+					#
2330
+					$text = $tag . $text;
2331
+					break;
2332
+				}
2333
+
2334
+				$parsed .= $tag;
2335
+			} else {
2336
+				$parsed .= $tag;
2337
+			}
2338
+		} while ($depth >= 0);
2339
+
2340
+		return array($parsed, $text);
2341
+	}
2342
+
2343
+	function _hashHTMLBlocks_inHTML($text, $hash_method, $md_attr)
2344
+	{
2345
+		#
2346
+		# Parse HTML, calling _HashHTMLBlocks_InMarkdown for block tags.
2347
+		#
2348
+		# *   Calls $hash_method to convert any blocks.
2349
+		# *   Stops when the first opening tag closes.
2350
+		# *   $md_attr indicate if the use of the `markdown="1"` attribute is allowed.
2351
+		#     (it is not inside clean tags)
2352
+		#
2353
+		# Returns an array of that form: ( processed text , remaining text )
2354
+		#
2355
+		if ($text === '') {
2356
+			return array('', '');
2357
+		}
2358
+
2359
+		# Regex to match `markdown` attribute inside of a tag.
2360
+		$markdown_attr_re = '
2361 2361
 			{
2362 2362
 				\s*			# Eat whitespace before the `markdown` attribute
2363 2363
 				markdown
@@ -2372,8 +2372,8 @@  discard block
 block discarded – undo
2372 2372
 				()				# $4: make $3 always defined (avoid warnings)
2373 2373
 			}xs';
2374 2374
 
2375
-        # Regex to match any tag.
2376
-        $tag_re = '{
2375
+		# Regex to match any tag.
2376
+		$tag_re = '{
2377 2377
 				(					# $2: Capture whole tag.
2378 2378
 					</?					# Any opening or closing tag.
2379 2379
 						[\w:$]+			# Tag name.
@@ -2395,169 +2395,169 @@  discard block
 block discarded – undo
2395 2395
 				)
2396 2396
 			}xs';
2397 2397
 
2398
-        $original_text = $text; # Save original text in case of faliure.
2399
-
2400
-        $depth      = 0; # Current depth inside the tag tree.
2401
-        $block_text = ""; # Temporary text holder for current text.
2402
-        $parsed     = ""; # Parsed text that will be returned.
2403
-
2404
-        #
2405
-        # Get the name of the starting tag.
2406
-        # (This pattern makes $base_tag_name_re safe without quoting.)
2407
-        #
2408
-        if (preg_match('/^<([\w:$]*)\b/', $text, $matches)) {
2409
-            $base_tag_name_re = $matches[1];
2410
-        }
2411
-
2412
-        #
2413
-        # Loop through every tag until we find the corresponding closing tag.
2414
-        #
2415
-        do {
2416
-            #
2417
-            # Split the text using the first $tag_match pattern found.
2418
-            # Text before  pattern will be first in the array, text after
2419
-            # pattern will be at the end, and between will be any catches made
2420
-            # by the pattern.
2421
-            #
2422
-            $parts = preg_split($tag_re, $text, 2, PREG_SPLIT_DELIM_CAPTURE);
2423
-
2424
-            if (count($parts) < 3) {
2425
-                #
2426
-                # End of $text reached with unbalenced tag(s).
2427
-                # In that case, we return original text unchanged and pass the
2428
-                # first character as filtered to prevent an infinite loop in the
2429
-                # parent function.
2430
-                #
2431
-                return array($original_text{0}, substr($original_text, 1));
2432
-            }
2433
-
2434
-            $block_text .= $parts[0]; # Text before current tag.
2435
-            $tag  = $parts[1]; # Tag to handle.
2436
-            $text = $parts[2]; # Remaining text after current tag.
2437
-
2438
-            #
2439
-            # Check for: Auto-close tag (like <hr/>)
2440
-            #			 Comments and Processing Instructions.
2441
-            #
2442
-            if (preg_match('{^</?(?:' . $this->auto_close_tags_re . ')\b}', $tag) ||
2443
-                $tag{1} == '!' || $tag{1} == '?'
2444
-            ) {
2445
-                # Just add the tag to the block as if it was text.
2446
-                $block_text .= $tag;
2447
-            } else {
2448
-                #
2449
-                # Increase/decrease nested tag count. Only do so if
2450
-                # the tag's name match base tag's.
2451
-                #
2452
-                if (preg_match('{^</?' . $base_tag_name_re . '\b}', $tag)) {
2453
-                    if ($tag{1} == '/') {
2454
-                        $depth --;
2455
-                    } else if ($tag{strlen($tag) - 2} != '/') {
2456
-                        $depth ++;
2457
-                    }
2458
-                }
2459
-
2460
-                #
2461
-                # Check for `markdown="1"` attribute and handle it.
2462
-                #
2463
-                if ($md_attr &&
2464
-                    preg_match($markdown_attr_re, $tag, $attr_m) &&
2465
-                    preg_match('/^1|block|span$/', $attr_m[2] . $attr_m[3])
2466
-                ) {
2467
-                    # Remove `markdown` attribute from opening tag.
2468
-                    $tag = preg_replace($markdown_attr_re, '', $tag);
2469
-
2470
-                    # Check if text inside this tag must be parsed in span mode.
2471
-                    $this->mode = $attr_m[2] . $attr_m[3];
2472
-                    $span_mode  = $this->mode == 'span' || $this->mode != 'block' &&
2473
-                                                           preg_match(
2474
-                                                               '{^<(?:' . $this->contain_span_tags_re . ')\b}',
2475
-                                                               $tag
2476
-                                                           );
2477
-
2478
-                    # Calculate indent before tag.
2479
-                    if (preg_match('/(?:^|\n)( *?)(?! ).*?$/', $block_text, $matches)) {
2480
-                        $strlen = $this->utf8_strlen;
2481
-                        $indent = $strlen($matches[1], 'UTF-8');
2482
-                    } else {
2483
-                        $indent = 0;
2484
-                    }
2485
-
2486
-                    # End preceding block with this tag.
2487
-                    $block_text .= $tag;
2488
-                    $parsed .= $this->$hash_method($block_text);
2489
-
2490
-                    # Get enclosing tag name for the ParseMarkdown function.
2491
-                    # (This pattern makes $tag_name_re safe without quoting.)
2492
-                    preg_match('/^<([\w:$]*)\b/', $tag, $matches);
2493
-                    $tag_name_re = $matches[1];
2494
-
2495
-                    # Parse the content using the HTML-in-Markdown parser.
2496
-                    list ($block_text, $text)
2497
-                        = $this->_hashHTMLBlocks_inMarkdown(
2498
-                        $text,
2499
-                        $indent,
2500
-                        $tag_name_re,
2501
-                        $span_mode
2502
-                    );
2503
-
2504
-                    # Outdent markdown text.
2505
-                    if ($indent > 0) {
2506
-                        $block_text = preg_replace(
2507
-                            "/^[ ]{1,$indent}/m",
2508
-                            "",
2509
-                            $block_text
2510
-                        );
2511
-                    }
2512
-
2513
-                    # Append tag content to parsed text.
2514
-                    if (!$span_mode) {
2515
-                        $parsed .= "\n\n$block_text\n\n";
2516
-                    } else {
2517
-                        $parsed .= "$block_text";
2518
-                    }
2519
-
2520
-                    # Start over with a new block.
2521
-                    $block_text = "";
2522
-                } else {
2523
-                    $block_text .= $tag;
2524
-                }
2525
-            }
2526
-        } while ($depth > 0);
2527
-
2528
-        #
2529
-        # Hash last block text that wasn't processed inside the loop.
2530
-        #
2531
-        $parsed .= $this->$hash_method($block_text);
2532
-
2533
-        return array($parsed, $text);
2534
-    }
2535
-
2536
-    function hashClean($text)
2537
-    {
2538
-        #
2539
-        # Called whenever a tag must be hashed when a function inserts a "clean" tag
2540
-        # in $text, it passes through this function and is automaticaly escaped,
2541
-        # blocking invalid nested overlap.
2542
-        #
2543
-        return $this->hashPart($text, 'C');
2544
-    }
2545
-
2546
-    function doAnchors($text)
2547
-    {
2548
-        #
2549
-        # Turn Markdown link shortcuts into XHTML <a> tags.
2550
-        #
2551
-        if ($this->in_anchor) {
2552
-            return $text;
2553
-        }
2554
-        $this->in_anchor = true;
2555
-
2556
-        #
2557
-        # First, handle reference-style links: [link text] [id]
2558
-        #
2559
-        $text = preg_replace_callback(
2560
-            '{
2398
+		$original_text = $text; # Save original text in case of faliure.
2399
+
2400
+		$depth      = 0; # Current depth inside the tag tree.
2401
+		$block_text = ""; # Temporary text holder for current text.
2402
+		$parsed     = ""; # Parsed text that will be returned.
2403
+
2404
+		#
2405
+		# Get the name of the starting tag.
2406
+		# (This pattern makes $base_tag_name_re safe without quoting.)
2407
+		#
2408
+		if (preg_match('/^<([\w:$]*)\b/', $text, $matches)) {
2409
+			$base_tag_name_re = $matches[1];
2410
+		}
2411
+
2412
+		#
2413
+		# Loop through every tag until we find the corresponding closing tag.
2414
+		#
2415
+		do {
2416
+			#
2417
+			# Split the text using the first $tag_match pattern found.
2418
+			# Text before  pattern will be first in the array, text after
2419
+			# pattern will be at the end, and between will be any catches made
2420
+			# by the pattern.
2421
+			#
2422
+			$parts = preg_split($tag_re, $text, 2, PREG_SPLIT_DELIM_CAPTURE);
2423
+
2424
+			if (count($parts) < 3) {
2425
+				#
2426
+				# End of $text reached with unbalenced tag(s).
2427
+				# In that case, we return original text unchanged and pass the
2428
+				# first character as filtered to prevent an infinite loop in the
2429
+				# parent function.
2430
+				#
2431
+				return array($original_text{0}, substr($original_text, 1));
2432
+			}
2433
+
2434
+			$block_text .= $parts[0]; # Text before current tag.
2435
+			$tag  = $parts[1]; # Tag to handle.
2436
+			$text = $parts[2]; # Remaining text after current tag.
2437
+
2438
+			#
2439
+			# Check for: Auto-close tag (like <hr/>)
2440
+			#			 Comments and Processing Instructions.
2441
+			#
2442
+			if (preg_match('{^</?(?:' . $this->auto_close_tags_re . ')\b}', $tag) ||
2443
+				$tag{1} == '!' || $tag{1} == '?'
2444
+			) {
2445
+				# Just add the tag to the block as if it was text.
2446
+				$block_text .= $tag;
2447
+			} else {
2448
+				#
2449
+				# Increase/decrease nested tag count. Only do so if
2450
+				# the tag's name match base tag's.
2451
+				#
2452
+				if (preg_match('{^</?' . $base_tag_name_re . '\b}', $tag)) {
2453
+					if ($tag{1} == '/') {
2454
+						$depth --;
2455
+					} else if ($tag{strlen($tag) - 2} != '/') {
2456
+						$depth ++;
2457
+					}
2458
+				}
2459
+
2460
+				#
2461
+				# Check for `markdown="1"` attribute and handle it.
2462
+				#
2463
+				if ($md_attr &&
2464
+					preg_match($markdown_attr_re, $tag, $attr_m) &&
2465
+					preg_match('/^1|block|span$/', $attr_m[2] . $attr_m[3])
2466
+				) {
2467
+					# Remove `markdown` attribute from opening tag.
2468
+					$tag = preg_replace($markdown_attr_re, '', $tag);
2469
+
2470
+					# Check if text inside this tag must be parsed in span mode.
2471
+					$this->mode = $attr_m[2] . $attr_m[3];
2472
+					$span_mode  = $this->mode == 'span' || $this->mode != 'block' &&
2473
+														   preg_match(
2474
+															   '{^<(?:' . $this->contain_span_tags_re . ')\b}',
2475
+															   $tag
2476
+														   );
2477
+
2478
+					# Calculate indent before tag.
2479
+					if (preg_match('/(?:^|\n)( *?)(?! ).*?$/', $block_text, $matches)) {
2480
+						$strlen = $this->utf8_strlen;
2481
+						$indent = $strlen($matches[1], 'UTF-8');
2482
+					} else {
2483
+						$indent = 0;
2484
+					}
2485
+
2486
+					# End preceding block with this tag.
2487
+					$block_text .= $tag;
2488
+					$parsed .= $this->$hash_method($block_text);
2489
+
2490
+					# Get enclosing tag name for the ParseMarkdown function.
2491
+					# (This pattern makes $tag_name_re safe without quoting.)
2492
+					preg_match('/^<([\w:$]*)\b/', $tag, $matches);
2493
+					$tag_name_re = $matches[1];
2494
+
2495
+					# Parse the content using the HTML-in-Markdown parser.
2496
+					list ($block_text, $text)
2497
+						= $this->_hashHTMLBlocks_inMarkdown(
2498
+						$text,
2499
+						$indent,
2500
+						$tag_name_re,
2501
+						$span_mode
2502
+					);
2503
+
2504
+					# Outdent markdown text.
2505
+					if ($indent > 0) {
2506
+						$block_text = preg_replace(
2507
+							"/^[ ]{1,$indent}/m",
2508
+							"",
2509
+							$block_text
2510
+						);
2511
+					}
2512
+
2513
+					# Append tag content to parsed text.
2514
+					if (!$span_mode) {
2515
+						$parsed .= "\n\n$block_text\n\n";
2516
+					} else {
2517
+						$parsed .= "$block_text";
2518
+					}
2519
+
2520
+					# Start over with a new block.
2521
+					$block_text = "";
2522
+				} else {
2523
+					$block_text .= $tag;
2524
+				}
2525
+			}
2526
+		} while ($depth > 0);
2527
+
2528
+		#
2529
+		# Hash last block text that wasn't processed inside the loop.
2530
+		#
2531
+		$parsed .= $this->$hash_method($block_text);
2532
+
2533
+		return array($parsed, $text);
2534
+	}
2535
+
2536
+	function hashClean($text)
2537
+	{
2538
+		#
2539
+		# Called whenever a tag must be hashed when a function inserts a "clean" tag
2540
+		# in $text, it passes through this function and is automaticaly escaped,
2541
+		# blocking invalid nested overlap.
2542
+		#
2543
+		return $this->hashPart($text, 'C');
2544
+	}
2545
+
2546
+	function doAnchors($text)
2547
+	{
2548
+		#
2549
+		# Turn Markdown link shortcuts into XHTML <a> tags.
2550
+		#
2551
+		if ($this->in_anchor) {
2552
+			return $text;
2553
+		}
2554
+		$this->in_anchor = true;
2555
+
2556
+		#
2557
+		# First, handle reference-style links: [link text] [id]
2558
+		#
2559
+		$text = preg_replace_callback(
2560
+			'{
2561 2561
 			(					# wrap whole match in $1
2562 2562
 			  \[
2563 2563
 				(' . $this->nested_brackets_re . ')	# link text = $2
@@ -2571,15 +2571,15 @@  discard block
 block discarded – undo
2571 2571
 			  \]
2572 2572
 			)
2573 2573
 			}xs',
2574
-            array(&$this, '_doAnchors_reference_callback'),
2575
-            $text
2576
-        );
2577
-
2578
-        #
2579
-        # Next, inline-style links: [link text](url "optional title")
2580
-        #
2581
-        $text = preg_replace_callback(
2582
-            '{
2574
+			array(&$this, '_doAnchors_reference_callback'),
2575
+			$text
2576
+		);
2577
+
2578
+		#
2579
+		# Next, inline-style links: [link text](url "optional title")
2580
+		#
2581
+		$text = preg_replace_callback(
2582
+			'{
2583 2583
 			(				# wrap whole match in $1
2584 2584
 			  \[
2585 2585
 				(' . $this->nested_brackets_re . ')	# link text = $2
@@ -2602,102 +2602,102 @@  discard block
 block discarded – undo
2602 2602
 			  (?:[ ]? ' . $this->id_class_attr_catch_re . ' )?	 # $8 = id/class attributes
2603 2603
 			)
2604 2604
 			}xs',
2605
-            array(&$this, '_doAnchors_inline_callback'),
2606
-            $text
2607
-        );
2608
-
2609
-        #
2610
-        # Last, handle reference-style shortcuts: [link text]
2611
-        # These must come last in case you've also got [link text][1]
2612
-        # or [link text](/foo)
2613
-        #
2614
-        $text = preg_replace_callback(
2615
-            '{
2605
+			array(&$this, '_doAnchors_inline_callback'),
2606
+			$text
2607
+		);
2608
+
2609
+		#
2610
+		# Last, handle reference-style shortcuts: [link text]
2611
+		# These must come last in case you've also got [link text][1]
2612
+		# or [link text](/foo)
2613
+		#
2614
+		$text = preg_replace_callback(
2615
+			'{
2616 2616
 			(					# wrap whole match in $1
2617 2617
 			  \[
2618 2618
 				([^\[\]]+)		# link text = $2; can\'t contain [ or ]
2619 2619
 			  \]
2620 2620
 			)
2621 2621
 			}xs',
2622
-            array(&$this, '_doAnchors_reference_callback'),
2623
-            $text
2624
-        );
2625
-
2626
-        $this->in_anchor = false;
2627
-        return $text;
2628
-    }
2629
-
2630
-    function _doAnchors_reference_callback($matches)
2631
-    {
2632
-        $whole_match = $matches[1];
2633
-        $link_text   = $matches[2];
2634
-        $link_id     =& $matches[3];
2635
-
2636
-        if ($link_id == "") {
2637
-            # for shortcut links like [this][] or [this].
2638
-            $link_id = $link_text;
2639
-        }
2640
-
2641
-        # lower-case and turn embedded newlines into spaces
2642
-        $link_id = strtolower($link_id);
2643
-        $link_id = preg_replace('{[ ]?\n}', ' ', $link_id);
2644
-
2645
-        if (isset($this->urls[$link_id])) {
2646
-            $url = $this->urls[$link_id];
2647
-            $url = $this->encodeAttribute($url);
2648
-
2649
-            $result = "<a href=\"$url\"";
2650
-            if (isset($this->titles[$link_id])) {
2651
-                $title = $this->titles[$link_id];
2652
-                $title = $this->encodeAttribute($title);
2653
-                $result .= " title=\"$title\"";
2654
-            }
2655
-            if (isset($this->ref_attr[$link_id])) {
2656
-                $result .= $this->ref_attr[$link_id];
2657
-            }
2658
-
2659
-            $link_text = $this->runSpanGamut($link_text);
2660
-            $result .= ">$link_text</a>";
2661
-            $result = $this->hashPart($result);
2662
-        } else {
2663
-            $result = $whole_match;
2664
-        }
2665
-        return $result;
2666
-    }
2667
-
2668
-    function _doAnchors_inline_callback($matches)
2669
-    {
2670
-        $whole_match = $matches[1];
2671
-        $link_text   = $this->runSpanGamut($matches[2]);
2672
-        $url         = $matches[3] == '' ? $matches[4] : $matches[3];
2673
-        $title       =& $matches[7];
2674
-        $attr        = $this->doExtraAttributes("a", $dummy =& $matches[8]);
2675
-
2676
-        $url = $this->encodeAttribute($url);
2677
-
2678
-        $result = "<a href=\"$url\"";
2679
-        if (isset($title)) {
2680
-            $title = $this->encodeAttribute($title);
2681
-            $result .= " title=\"$title\"";
2682
-        }
2683
-        $result .= $attr;
2684
-
2685
-        $link_text = $this->runSpanGamut($link_text);
2686
-        $result .= ">$link_text</a>";
2687
-
2688
-        return $this->hashPart($result);
2689
-    }
2690
-
2691
-    function doImages($text)
2692
-    {
2693
-        #
2694
-        # Turn Markdown image shortcuts into <img> tags.
2695
-        #
2696
-        #
2697
-        # First, handle reference-style labeled images: ![alt text][id]
2698
-        #
2699
-        $text = preg_replace_callback(
2700
-            '{
2622
+			array(&$this, '_doAnchors_reference_callback'),
2623
+			$text
2624
+		);
2625
+
2626
+		$this->in_anchor = false;
2627
+		return $text;
2628
+	}
2629
+
2630
+	function _doAnchors_reference_callback($matches)
2631
+	{
2632
+		$whole_match = $matches[1];
2633
+		$link_text   = $matches[2];
2634
+		$link_id     =& $matches[3];
2635
+
2636
+		if ($link_id == "") {
2637
+			# for shortcut links like [this][] or [this].
2638
+			$link_id = $link_text;
2639
+		}
2640
+
2641
+		# lower-case and turn embedded newlines into spaces
2642
+		$link_id = strtolower($link_id);
2643
+		$link_id = preg_replace('{[ ]?\n}', ' ', $link_id);
2644
+
2645
+		if (isset($this->urls[$link_id])) {
2646
+			$url = $this->urls[$link_id];
2647
+			$url = $this->encodeAttribute($url);
2648
+
2649
+			$result = "<a href=\"$url\"";
2650
+			if (isset($this->titles[$link_id])) {
2651
+				$title = $this->titles[$link_id];
2652
+				$title = $this->encodeAttribute($title);
2653
+				$result .= " title=\"$title\"";
2654
+			}
2655
+			if (isset($this->ref_attr[$link_id])) {
2656
+				$result .= $this->ref_attr[$link_id];
2657
+			}
2658
+
2659
+			$link_text = $this->runSpanGamut($link_text);
2660
+			$result .= ">$link_text</a>";
2661
+			$result = $this->hashPart($result);
2662
+		} else {
2663
+			$result = $whole_match;
2664
+		}
2665
+		return $result;
2666
+	}
2667
+
2668
+	function _doAnchors_inline_callback($matches)
2669
+	{
2670
+		$whole_match = $matches[1];
2671
+		$link_text   = $this->runSpanGamut($matches[2]);
2672
+		$url         = $matches[3] == '' ? $matches[4] : $matches[3];
2673
+		$title       =& $matches[7];
2674
+		$attr        = $this->doExtraAttributes("a", $dummy =& $matches[8]);
2675
+
2676
+		$url = $this->encodeAttribute($url);
2677
+
2678
+		$result = "<a href=\"$url\"";
2679
+		if (isset($title)) {
2680
+			$title = $this->encodeAttribute($title);
2681
+			$result .= " title=\"$title\"";
2682
+		}
2683
+		$result .= $attr;
2684
+
2685
+		$link_text = $this->runSpanGamut($link_text);
2686
+		$result .= ">$link_text</a>";
2687
+
2688
+		return $this->hashPart($result);
2689
+	}
2690
+
2691
+	function doImages($text)
2692
+	{
2693
+		#
2694
+		# Turn Markdown image shortcuts into <img> tags.
2695
+		#
2696
+		#
2697
+		# First, handle reference-style labeled images: ![alt text][id]
2698
+		#
2699
+		$text = preg_replace_callback(
2700
+			'{
2701 2701
 			(				# wrap whole match in $1
2702 2702
 			  !\[
2703 2703
 				(' . $this->nested_brackets_re . ')		# alt text = $2
@@ -2712,16 +2712,16 @@  discard block
 block discarded – undo
2712 2712
 
2713 2713
 			)
2714 2714
 			}xs',
2715
-            array(&$this, '_doImages_reference_callback'),
2716
-            $text
2717
-        );
2718
-
2719
-        #
2720
-        # Next, handle inline images:  ![alt text](url "optional title")
2721
-        # Don't forget: encode * and _
2722
-        #
2723
-        $text = preg_replace_callback(
2724
-            '{
2715
+			array(&$this, '_doImages_reference_callback'),
2716
+			$text
2717
+		);
2718
+
2719
+		#
2720
+		# Next, handle inline images:  ![alt text](url "optional title")
2721
+		# Don't forget: encode * and _
2722
+		#
2723
+		$text = preg_replace_callback(
2724
+			'{
2725 2725
 			(				# wrap whole match in $1
2726 2726
 			  !\[
2727 2727
 				(' . $this->nested_brackets_re . ')		# alt text = $2
@@ -2745,97 +2745,97 @@  discard block
 block discarded – undo
2745 2745
 			  (?:[ ]? ' . $this->id_class_attr_catch_re . ' )?	 # $8 = id/class attributes
2746 2746
 			)
2747 2747
 			}xs',
2748
-            array(&$this, '_doImages_inline_callback'),
2749
-            $text
2750
-        );
2751
-
2752
-        return $text;
2753
-    }
2754
-
2755
-    function _doImages_reference_callback($matches)
2756
-    {
2757
-        $whole_match = $matches[1];
2758
-        $alt_text    = $matches[2];
2759
-        $link_id     = strtolower($matches[3]);
2760
-
2761
-        if ($link_id == "") {
2762
-            $link_id = strtolower($alt_text); # for shortcut links like ![this][].
2763
-        }
2764
-
2765
-        $alt_text = $this->encodeAttribute($alt_text);
2766
-        if (isset($this->urls[$link_id])) {
2767
-            $url    = $this->encodeAttribute($this->urls[$link_id]);
2768
-            $result = "<img src=\"$url\" alt=\"$alt_text\"";
2769
-            if (isset($this->titles[$link_id])) {
2770
-                $title = $this->titles[$link_id];
2771
-                $title = $this->encodeAttribute($title);
2772
-                $result .= " title=\"$title\"";
2773
-            }
2774
-            if (isset($this->ref_attr[$link_id])) {
2775
-                $result .= $this->ref_attr[$link_id];
2776
-            }
2777
-            $result .= $this->empty_element_suffix;
2778
-            $result = $this->hashPart($result);
2779
-        } else {
2780
-            # If there's no such link ID, leave intact:
2781
-            $result = $whole_match;
2782
-        }
2783
-
2784
-        return $result;
2785
-    }
2786
-
2787
-    function _doImages_inline_callback($matches)
2788
-    {
2789
-        $whole_match = $matches[1];
2790
-        $alt_text    = $matches[2];
2791
-        $url         = $matches[3] == '' ? $matches[4] : $matches[3];
2792
-        $title       =& $matches[7];
2793
-        $attr        = $this->doExtraAttributes("img", $dummy =& $matches[8]);
2794
-
2795
-        $alt_text = $this->encodeAttribute($alt_text);
2796
-        $url      = $this->encodeAttribute($url);
2797
-        $result   = "<img src=\"$url\" alt=\"$alt_text\"";
2798
-        if (isset($title)) {
2799
-            $title = $this->encodeAttribute($title);
2800
-            $result .= " title=\"$title\""; # $title already quoted
2801
-        }
2802
-        $result .= $attr;
2803
-        $result .= $this->empty_element_suffix;
2804
-
2805
-        return $this->hashPart($result);
2806
-    }
2807
-
2808
-    function doHeaders($text)
2809
-    {
2810
-        #
2811
-        # Redefined to add id and class attribute support.
2812
-        #
2813
-        # Setext-style headers:
2814
-        #	  Header 1  {#header1}
2815
-        #	  ========
2816
-        #
2817
-        #	  Header 2  {#header2 .class1 .class2}
2818
-        #	  --------
2819
-        #
2820
-        $text = preg_replace_callback(
2821
-            '{
2748
+			array(&$this, '_doImages_inline_callback'),
2749
+			$text
2750
+		);
2751
+
2752
+		return $text;
2753
+	}
2754
+
2755
+	function _doImages_reference_callback($matches)
2756
+	{
2757
+		$whole_match = $matches[1];
2758
+		$alt_text    = $matches[2];
2759
+		$link_id     = strtolower($matches[3]);
2760
+
2761
+		if ($link_id == "") {
2762
+			$link_id = strtolower($alt_text); # for shortcut links like ![this][].
2763
+		}
2764
+
2765
+		$alt_text = $this->encodeAttribute($alt_text);
2766
+		if (isset($this->urls[$link_id])) {
2767
+			$url    = $this->encodeAttribute($this->urls[$link_id]);
2768
+			$result = "<img src=\"$url\" alt=\"$alt_text\"";
2769
+			if (isset($this->titles[$link_id])) {
2770
+				$title = $this->titles[$link_id];
2771
+				$title = $this->encodeAttribute($title);
2772
+				$result .= " title=\"$title\"";
2773
+			}
2774
+			if (isset($this->ref_attr[$link_id])) {
2775
+				$result .= $this->ref_attr[$link_id];
2776
+			}
2777
+			$result .= $this->empty_element_suffix;
2778
+			$result = $this->hashPart($result);
2779
+		} else {
2780
+			# If there's no such link ID, leave intact:
2781
+			$result = $whole_match;
2782
+		}
2783
+
2784
+		return $result;
2785
+	}
2786
+
2787
+	function _doImages_inline_callback($matches)
2788
+	{
2789
+		$whole_match = $matches[1];
2790
+		$alt_text    = $matches[2];
2791
+		$url         = $matches[3] == '' ? $matches[4] : $matches[3];
2792
+		$title       =& $matches[7];
2793
+		$attr        = $this->doExtraAttributes("img", $dummy =& $matches[8]);
2794
+
2795
+		$alt_text = $this->encodeAttribute($alt_text);
2796
+		$url      = $this->encodeAttribute($url);
2797
+		$result   = "<img src=\"$url\" alt=\"$alt_text\"";
2798
+		if (isset($title)) {
2799
+			$title = $this->encodeAttribute($title);
2800
+			$result .= " title=\"$title\""; # $title already quoted
2801
+		}
2802
+		$result .= $attr;
2803
+		$result .= $this->empty_element_suffix;
2804
+
2805
+		return $this->hashPart($result);
2806
+	}
2807
+
2808
+	function doHeaders($text)
2809
+	{
2810
+		#
2811
+		# Redefined to add id and class attribute support.
2812
+		#
2813
+		# Setext-style headers:
2814
+		#	  Header 1  {#header1}
2815
+		#	  ========
2816
+		#
2817
+		#	  Header 2  {#header2 .class1 .class2}
2818
+		#	  --------
2819
+		#
2820
+		$text = preg_replace_callback(
2821
+			'{
2822 2822
 				(^.+?)								# $1: Header text
2823 2823
 				(?:[ ]+ ' . $this->id_class_attr_catch_re . ' )?	 # $3 = id/class attributes
2824 2824
 				[ ]*\n(=+|-+)[ ]*\n+				# $3: Header footer
2825 2825
 			}mx',
2826
-            array(&$this, '_doHeaders_callback_setext'),
2827
-            $text
2828
-        );
2829
-
2830
-        # atx-style headers:
2831
-        #	# Header 1        {#header1}
2832
-        #	## Header 2       {#header2}
2833
-        #	## Header 2 with closing hashes ##  {#header3.class1.class2}
2834
-        #	...
2835
-        #	###### Header 6   {.class2}
2836
-        #
2837
-        $text = preg_replace_callback(
2838
-            '{
2826
+			array(&$this, '_doHeaders_callback_setext'),
2827
+			$text
2828
+		);
2829
+
2830
+		# atx-style headers:
2831
+		#	# Header 1        {#header1}
2832
+		#	## Header 2       {#header2}
2833
+		#	## Header 2 with closing hashes ##  {#header3.class1.class2}
2834
+		#	...
2835
+		#	###### Header 6   {.class2}
2836
+		#
2837
+		$text = preg_replace_callback(
2838
+			'{
2839 2839
 				^(\#{1,6})	# $1 = string of #\'s
2840 2840
 				[ ]*
2841 2841
 				(.+?)		# $2 = Header text
@@ -2845,48 +2845,48 @@  discard block
 block discarded – undo
2845 2845
 				[ ]*
2846 2846
 				\n+
2847 2847
 			}xm',
2848
-            array(&$this, '_doHeaders_callback_atx'),
2849
-            $text
2850
-        );
2851
-
2852
-        return $text;
2853
-    }
2854
-
2855
-    function _doHeaders_callback_setext($matches)
2856
-    {
2857
-        if ($matches[3] == '-' && preg_match('{^- }', $matches[1])) {
2858
-            return $matches[0];
2859
-        }
2860
-        $level = $matches[3]{0} == '=' ? 1 : 2;
2861
-        $attr  = $this->doExtraAttributes("h$level", $dummy =& $matches[2]);
2862
-        $block = "<h$level$attr>" . $this->runSpanGamut($matches[1]) . "</h$level>";
2863
-        return "\n" . $this->hashBlock($block) . "\n\n";
2864
-    }
2865
-
2866
-    function _doHeaders_callback_atx($matches)
2867
-    {
2868
-        $level = strlen($matches[1]);
2869
-        $attr  = $this->doExtraAttributes("h$level", $dummy =& $matches[3]);
2870
-        $block = "<h$level$attr>" . $this->runSpanGamut($matches[2]) . "</h$level>";
2871
-        return "\n" . $this->hashBlock($block) . "\n\n";
2872
-    }
2873
-
2874
-    function doTables($text)
2875
-    {
2876
-        #
2877
-        # Form HTML tables.
2878
-        #
2879
-        $less_than_tab = $this->tab_width - 1;
2880
-        #
2881
-        # Find tables with leading pipe.
2882
-        #
2883
-        #	| Header 1 | Header 2
2884
-        #	| -------- | --------
2885
-        #	| Cell 1   | Cell 2
2886
-        #	| Cell 3   | Cell 4
2887
-        #
2888
-        $text = preg_replace_callback(
2889
-            '
2848
+			array(&$this, '_doHeaders_callback_atx'),
2849
+			$text
2850
+		);
2851
+
2852
+		return $text;
2853
+	}
2854
+
2855
+	function _doHeaders_callback_setext($matches)
2856
+	{
2857
+		if ($matches[3] == '-' && preg_match('{^- }', $matches[1])) {
2858
+			return $matches[0];
2859
+		}
2860
+		$level = $matches[3]{0} == '=' ? 1 : 2;
2861
+		$attr  = $this->doExtraAttributes("h$level", $dummy =& $matches[2]);
2862
+		$block = "<h$level$attr>" . $this->runSpanGamut($matches[1]) . "</h$level>";
2863
+		return "\n" . $this->hashBlock($block) . "\n\n";
2864
+	}
2865
+
2866
+	function _doHeaders_callback_atx($matches)
2867
+	{
2868
+		$level = strlen($matches[1]);
2869
+		$attr  = $this->doExtraAttributes("h$level", $dummy =& $matches[3]);
2870
+		$block = "<h$level$attr>" . $this->runSpanGamut($matches[2]) . "</h$level>";
2871
+		return "\n" . $this->hashBlock($block) . "\n\n";
2872
+	}
2873
+
2874
+	function doTables($text)
2875
+	{
2876
+		#
2877
+		# Form HTML tables.
2878
+		#
2879
+		$less_than_tab = $this->tab_width - 1;
2880
+		#
2881
+		# Find tables with leading pipe.
2882
+		#
2883
+		#	| Header 1 | Header 2
2884
+		#	| -------- | --------
2885
+		#	| Cell 1   | Cell 2
2886
+		#	| Cell 3   | Cell 4
2887
+		#
2888
+		$text = preg_replace_callback(
2889
+			'
2890 2890
 			{
2891 2891
 				^							# Start of a line
2892 2892
 				[ ]{0,' . $less_than_tab . '}	# Allowed whitespace.
@@ -2904,20 +2904,20 @@  discard block
 block discarded – undo
2904 2904
 				)
2905 2905
 				(?=\n|\Z)					# Stop at final double newline.
2906 2906
 			}xm',
2907
-            array(&$this, '_doTable_leadingPipe_callback'),
2908
-            $text
2909
-        );
2910
-
2911
-        #
2912
-        # Find tables without leading pipe.
2913
-        #
2914
-        #	Header 1 | Header 2
2915
-        #	-------- | --------
2916
-        #	Cell 1   | Cell 2
2917
-        #	Cell 3   | Cell 4
2918
-        #
2919
-        $text = preg_replace_callback(
2920
-            '
2907
+			array(&$this, '_doTable_leadingPipe_callback'),
2908
+			$text
2909
+		);
2910
+
2911
+		#
2912
+		# Find tables without leading pipe.
2913
+		#
2914
+		#	Header 1 | Header 2
2915
+		#	-------- | --------
2916
+		#	Cell 1   | Cell 2
2917
+		#	Cell 3   | Cell 4
2918
+		#
2919
+		$text = preg_replace_callback(
2920
+			'
2921 2921
 			{
2922 2922
 				^							# Start of a line
2923 2923
 				[ ]{0,' . $less_than_tab . '}	# Allowed whitespace.
@@ -2933,101 +2933,101 @@  discard block
 block discarded – undo
2933 2933
 				)
2934 2934
 				(?=\n|\Z)					# Stop at final double newline.
2935 2935
 			}xm',
2936
-            array(&$this, '_DoTable_callback'),
2937
-            $text
2938
-        );
2939
-
2940
-        return $text;
2941
-    }
2942
-
2943
-    function _doTable_leadingPipe_callback($matches)
2944
-    {
2945
-        $head      = $matches[1];
2946
-        $underline = $matches[2];
2947
-        $content   = $matches[3];
2948
-
2949
-        # Remove leading pipe for each row.
2950
-        $content = preg_replace('/^ *[|]/m', '', $content);
2951
-
2952
-        return $this->_doTable_callback(array($matches[0], $head, $underline, $content));
2953
-    }
2954
-
2955
-    function _doTable_callback($matches)
2956
-    {
2957
-        $head      = $matches[1];
2958
-        $underline = $matches[2];
2959
-        $content   = $matches[3];
2960
-
2961
-        # Remove any tailing pipes for each line.
2962
-        $head      = preg_replace('/[|] *$/m', '', $head);
2963
-        $underline = preg_replace('/[|] *$/m', '', $underline);
2964
-        $content   = preg_replace('/[|] *$/m', '', $content);
2965
-
2966
-        # Reading alignement from header underline.
2967
-        $separators = preg_split('/ *[|] */', $underline);
2968
-        foreach ($separators as $n => $s) {
2969
-            if (preg_match('/^ *-+: *$/', $s)) {
2970
-                $attr[$n] = ' align="right"';
2971
-            } else if (preg_match('/^ *:-+: *$/', $s)) {
2972
-                $attr[$n] = ' align="center"';
2973
-            } else if (preg_match('/^ *:-+ *$/', $s)) {
2974
-                $attr[$n] = ' align="left"';
2975
-            } else {
2976
-                $attr[$n] = '';
2977
-            }
2978
-        }
2979
-
2980
-        # Parsing span elements, including code spans, character escapes,
2981
-        # and inline HTML tags, so that pipes inside those gets ignored.
2982
-        $head      = $this->parseSpan($head);
2983
-        $headers   = preg_split('/ *[|] */', $head);
2984
-        $col_count = count($headers);
2985
-        $attr      = array_pad($attr, $col_count, '');
2986
-
2987
-        # Write column headers.
2988
-        $text = "<table>\n";
2989
-        $text .= "<thead>\n";
2990
-        $text .= "<tr>\n";
2991
-        foreach ($headers as $n => $header) {
2992
-            $text .= "  <th$attr[$n]>" . $this->runSpanGamut(trim($header)) . "</th>\n";
2993
-        }
2994
-        $text .= "</tr>\n";
2995
-        $text .= "</thead>\n";
2996
-
2997
-        # Split content by row.
2998
-        $rows = explode("\n", trim($content, "\n"));
2999
-
3000
-        $text .= "<tbody>\n";
3001
-        foreach ($rows as $row) {
3002
-            # Parsing span elements, including code spans, character escapes,
3003
-            # and inline HTML tags, so that pipes inside those gets ignored.
3004
-            $row = $this->parseSpan($row);
3005
-
3006
-            # Split row by cell.
3007
-            $row_cells = preg_split('/ *[|] */', $row, $col_count);
3008
-            $row_cells = array_pad($row_cells, $col_count, '');
3009
-
3010
-            $text .= "<tr>\n";
3011
-            foreach ($row_cells as $n => $cell) {
3012
-                $text .= "  <td$attr[$n]>" . $this->runSpanGamut(trim($cell)) . "</td>\n";
3013
-            }
3014
-            $text .= "</tr>\n";
3015
-        }
3016
-        $text .= "</tbody>\n";
3017
-        $text .= "</table>";
3018
-
3019
-        return $this->hashBlock($text) . "\n";
3020
-    }
3021
-
3022
-    function doDefLists($text)
3023
-    {
3024
-        #
3025
-        # Form HTML definition lists.
3026
-        #
3027
-        $less_than_tab = $this->tab_width - 1;
3028
-
3029
-        # Re-usable pattern to match any entire dl list:
3030
-        $whole_list_re = '(?>
2936
+			array(&$this, '_DoTable_callback'),
2937
+			$text
2938
+		);
2939
+
2940
+		return $text;
2941
+	}
2942
+
2943
+	function _doTable_leadingPipe_callback($matches)
2944
+	{
2945
+		$head      = $matches[1];
2946
+		$underline = $matches[2];
2947
+		$content   = $matches[3];
2948
+
2949
+		# Remove leading pipe for each row.
2950
+		$content = preg_replace('/^ *[|]/m', '', $content);
2951
+
2952
+		return $this->_doTable_callback(array($matches[0], $head, $underline, $content));
2953
+	}
2954
+
2955
+	function _doTable_callback($matches)
2956
+	{
2957
+		$head      = $matches[1];
2958
+		$underline = $matches[2];
2959
+		$content   = $matches[3];
2960
+
2961
+		# Remove any tailing pipes for each line.
2962
+		$head      = preg_replace('/[|] *$/m', '', $head);
2963
+		$underline = preg_replace('/[|] *$/m', '', $underline);
2964
+		$content   = preg_replace('/[|] *$/m', '', $content);
2965
+
2966
+		# Reading alignement from header underline.
2967
+		$separators = preg_split('/ *[|] */', $underline);
2968
+		foreach ($separators as $n => $s) {
2969
+			if (preg_match('/^ *-+: *$/', $s)) {
2970
+				$attr[$n] = ' align="right"';
2971
+			} else if (preg_match('/^ *:-+: *$/', $s)) {
2972
+				$attr[$n] = ' align="center"';
2973
+			} else if (preg_match('/^ *:-+ *$/', $s)) {
2974
+				$attr[$n] = ' align="left"';
2975
+			} else {
2976
+				$attr[$n] = '';
2977
+			}
2978
+		}
2979
+
2980
+		# Parsing span elements, including code spans, character escapes,
2981
+		# and inline HTML tags, so that pipes inside those gets ignored.
2982
+		$head      = $this->parseSpan($head);
2983
+		$headers   = preg_split('/ *[|] */', $head);
2984
+		$col_count = count($headers);
2985
+		$attr      = array_pad($attr, $col_count, '');
2986
+
2987
+		# Write column headers.
2988
+		$text = "<table>\n";
2989
+		$text .= "<thead>\n";
2990
+		$text .= "<tr>\n";
2991
+		foreach ($headers as $n => $header) {
2992
+			$text .= "  <th$attr[$n]>" . $this->runSpanGamut(trim($header)) . "</th>\n";
2993
+		}
2994
+		$text .= "</tr>\n";
2995
+		$text .= "</thead>\n";
2996
+
2997
+		# Split content by row.
2998
+		$rows = explode("\n", trim($content, "\n"));
2999
+
3000
+		$text .= "<tbody>\n";
3001
+		foreach ($rows as $row) {
3002
+			# Parsing span elements, including code spans, character escapes,
3003
+			# and inline HTML tags, so that pipes inside those gets ignored.
3004
+			$row = $this->parseSpan($row);
3005
+
3006
+			# Split row by cell.
3007
+			$row_cells = preg_split('/ *[|] */', $row, $col_count);
3008
+			$row_cells = array_pad($row_cells, $col_count, '');
3009
+
3010
+			$text .= "<tr>\n";
3011
+			foreach ($row_cells as $n => $cell) {
3012
+				$text .= "  <td$attr[$n]>" . $this->runSpanGamut(trim($cell)) . "</td>\n";
3013
+			}
3014
+			$text .= "</tr>\n";
3015
+		}
3016
+		$text .= "</tbody>\n";
3017
+		$text .= "</table>";
3018
+
3019
+		return $this->hashBlock($text) . "\n";
3020
+	}
3021
+
3022
+	function doDefLists($text)
3023
+	{
3024
+		#
3025
+		# Form HTML definition lists.
3026
+		#
3027
+		$less_than_tab = $this->tab_width - 1;
3028
+
3029
+		# Re-usable pattern to match any entire dl list:
3030
+		$whole_list_re = '(?>
3031 3031
 			(								# $1 = whole list
3032 3032
 			  (								# $2
3033 3033
 				[ ]{0,' . $less_than_tab . '}
@@ -3054,44 +3054,44 @@  discard block
 block discarded – undo
3054 3054
 			)
3055 3055
 		)'; // mx
3056 3056
 
3057
-        $text = preg_replace_callback(
3058
-            '{
3057
+		$text = preg_replace_callback(
3058
+			'{
3059 3059
 				(?>\A\n?|(?<=\n\n))
3060 3060
 				' . $whole_list_re . '
3061 3061
 			}mx',
3062
-            array(&$this, '_doDefLists_callback'),
3063
-            $text
3064
-        );
3065
-
3066
-        return $text;
3067
-    }
3068
-
3069
-    function _doDefLists_callback($matches)
3070
-    {
3071
-        # Re-usable patterns to match list item bullets and number markers:
3072
-        $list = $matches[1];
3073
-
3074
-        # Turn double returns into triple returns, so that we can make a
3075
-        # paragraph for the last item in a list, if necessary:
3076
-        $result = trim($this->processDefListItems($list));
3077
-        $result = "<dl>\n" . $result . "\n</dl>";
3078
-        return $this->hashBlock($result) . "\n\n";
3079
-    }
3080
-
3081
-    function processDefListItems($list_str)
3082
-    {
3083
-        #
3084
-        #	Process the contents of a single definition list, splitting it
3085
-        #	into individual term and definition list items.
3086
-        #
3087
-        $less_than_tab = $this->tab_width - 1;
3088
-
3089
-        # trim trailing blank lines:
3090
-        $list_str = preg_replace("/\n{2,}\\z/", "\n", $list_str);
3091
-
3092
-        # Process definition terms.
3093
-        $list_str = preg_replace_callback(
3094
-            '{
3062
+			array(&$this, '_doDefLists_callback'),
3063
+			$text
3064
+		);
3065
+
3066
+		return $text;
3067
+	}
3068
+
3069
+	function _doDefLists_callback($matches)
3070
+	{
3071
+		# Re-usable patterns to match list item bullets and number markers:
3072
+		$list = $matches[1];
3073
+
3074
+		# Turn double returns into triple returns, so that we can make a
3075
+		# paragraph for the last item in a list, if necessary:
3076
+		$result = trim($this->processDefListItems($list));
3077
+		$result = "<dl>\n" . $result . "\n</dl>";
3078
+		return $this->hashBlock($result) . "\n\n";
3079
+	}
3080
+
3081
+	function processDefListItems($list_str)
3082
+	{
3083
+		#
3084
+		#	Process the contents of a single definition list, splitting it
3085
+		#	into individual term and definition list items.
3086
+		#
3087
+		$less_than_tab = $this->tab_width - 1;
3088
+
3089
+		# trim trailing blank lines:
3090
+		$list_str = preg_replace("/\n{2,}\\z/", "\n", $list_str);
3091
+
3092
+		# Process definition terms.
3093
+		$list_str = preg_replace_callback(
3094
+			'{
3095 3095
 			(?>\A\n?|\n\n+)					# leading line
3096 3096
 			(								# definition terms = $1
3097 3097
 				[ ]{0,' . $less_than_tab . '}	# leading whitespace
@@ -3102,13 +3102,13 @@  discard block
 block discarded – undo
3102 3102
 			(?=\n?[ ]{0,3}:[ ])				# lookahead for following line feed
3103 3103
 											#   with a definition mark.
3104 3104
 			}xm',
3105
-            array(&$this, '_processDefListItems_callback_dt'),
3106
-            $list_str
3107
-        );
3105
+			array(&$this, '_processDefListItems_callback_dt'),
3106
+			$list_str
3107
+		);
3108 3108
 
3109
-        # Process actual definitions.
3110
-        $list_str = preg_replace_callback(
3111
-            '{
3109
+		# Process actual definitions.
3110
+		$list_str = preg_replace_callback(
3111
+			'{
3112 3112
 			\n(\n+)?						# leading line = $1
3113 3113
 			(								# marker space = $2
3114 3114
 				[ ]{0,' . $less_than_tab . '}	# whitespace before colon
@@ -3122,56 +3122,56 @@  discard block
 block discarded – undo
3122 3122
 				)
3123 3123
 			)
3124 3124
 			}xm',
3125
-            array(&$this, '_processDefListItems_callback_dd'),
3126
-            $list_str
3127
-        );
3128
-
3129
-        return $list_str;
3130
-    }
3131
-
3132
-    function _processDefListItems_callback_dt($matches)
3133
-    {
3134
-        $terms = explode("\n", trim($matches[1]));
3135
-        $text  = '';
3136
-        foreach ($terms as $term) {
3137
-            $term = $this->runSpanGamut(trim($term));
3138
-            $text .= "\n<dt>" . $term . "</dt>";
3139
-        }
3140
-        return $text . "\n";
3141
-    }
3142
-
3143
-    function _processDefListItems_callback_dd($matches)
3144
-    {
3145
-        $leading_line = $matches[1];
3146
-        $marker_space = $matches[2];
3147
-        $def          = $matches[3];
3148
-
3149
-        if ($leading_line || preg_match('/\n{2,}/', $def)) {
3150
-            # Replace marker with the appropriate whitespace indentation
3151
-            $def = str_repeat(' ', strlen($marker_space)) . $def;
3152
-            $def = $this->runBlockGamut($this->outdent($def . "\n\n"));
3153
-            $def = "\n" . $def . "\n";
3154
-        } else {
3155
-            $def = rtrim($def);
3156
-            $def = $this->runSpanGamut($this->outdent($def));
3157
-        }
3158
-
3159
-        return "\n<dd>" . $def . "</dd>\n";
3160
-    }
3161
-
3162
-    function doFencedCodeBlocks($text)
3163
-    {
3164
-        #
3165
-        # Adding the fenced code block syntax to regular Markdown:
3166
-        #
3167
-        # ~~~
3168
-        # Code block
3169
-        # ~~~
3170
-        #
3171
-        $less_than_tab = $this->tab_width;
3172
-
3173
-        $text = preg_replace_callback(
3174
-            '{
3125
+			array(&$this, '_processDefListItems_callback_dd'),
3126
+			$list_str
3127
+		);
3128
+
3129
+		return $list_str;
3130
+	}
3131
+
3132
+	function _processDefListItems_callback_dt($matches)
3133
+	{
3134
+		$terms = explode("\n", trim($matches[1]));
3135
+		$text  = '';
3136
+		foreach ($terms as $term) {
3137
+			$term = $this->runSpanGamut(trim($term));
3138
+			$text .= "\n<dt>" . $term . "</dt>";
3139
+		}
3140
+		return $text . "\n";
3141
+	}
3142
+
3143
+	function _processDefListItems_callback_dd($matches)
3144
+	{
3145
+		$leading_line = $matches[1];
3146
+		$marker_space = $matches[2];
3147
+		$def          = $matches[3];
3148
+
3149
+		if ($leading_line || preg_match('/\n{2,}/', $def)) {
3150
+			# Replace marker with the appropriate whitespace indentation
3151
+			$def = str_repeat(' ', strlen($marker_space)) . $def;
3152
+			$def = $this->runBlockGamut($this->outdent($def . "\n\n"));
3153
+			$def = "\n" . $def . "\n";
3154
+		} else {
3155
+			$def = rtrim($def);
3156
+			$def = $this->runSpanGamut($this->outdent($def));
3157
+		}
3158
+
3159
+		return "\n<dd>" . $def . "</dd>\n";
3160
+	}
3161
+
3162
+	function doFencedCodeBlocks($text)
3163
+	{
3164
+		#
3165
+		# Adding the fenced code block syntax to regular Markdown:
3166
+		#
3167
+		# ~~~
3168
+		# Code block
3169
+		# ~~~
3170
+		#
3171
+		$less_than_tab = $this->tab_width;
3172
+
3173
+		$text = preg_replace_callback(
3174
+			'{
3175 3175
 				(?:\n|\A)
3176 3176
 				# 1: Opening marker
3177 3177
 				(
@@ -3196,120 +3196,120 @@  discard block
 block discarded – undo
3196 3196
 				# Closing marker.
3197 3197
 				\1 [ ]* \n
3198 3198
 			}xm',
3199
-            array(&$this, '_doFencedCodeBlocks_callback'),
3200
-            $text
3201
-        );
3202
-
3203
-        return $text;
3204
-    }
3205
-
3206
-    function _doFencedCodeBlocks_callback($matches)
3207
-    {
3208
-        $classname =& $matches[2];
3209
-        $attrs     =& $matches[3];
3210
-        $codeblock = $matches[4];
3211
-        $codeblock = htmlspecialchars($codeblock, ENT_NOQUOTES);
3212
-        $codeblock = preg_replace_callback(
3213
-            '/^\n+/',
3214
-            array(&$this, '_doFencedCodeBlocks_newlines'),
3215
-            $codeblock
3216
-        );
3217
-
3218
-        if ($classname != "") {
3219
-            if ($classname{0} == '.') {
3220
-                $classname = substr($classname, 1);
3221
-            }
3222
-            $attr_str = ' class="' . $this->code_class_prefix . $classname . '"';
3223
-        } else {
3224
-            $attr_str = $this->doExtraAttributes($this->code_attr_on_pre ? "pre" : "code", $attrs);
3225
-        }
3226
-        $pre_attr_str  = $this->code_attr_on_pre ? $attr_str : '';
3227
-        $code_attr_str = $this->code_attr_on_pre ? '' : $attr_str;
3228
-        $codeblock     = "<pre$pre_attr_str><code$code_attr_str>$codeblock</code></pre>";
3229
-
3230
-        return "\n\n" . $this->hashBlock($codeblock) . "\n\n";
3231
-    }
3232
-
3233
-    function _doFencedCodeBlocks_newlines($matches)
3234
-    {
3235
-        return str_repeat(
3236
-            "<br$this->empty_element_suffix",
3237
-            strlen($matches[0])
3238
-        );
3239
-    }
3240
-
3241
-
3242
-    #
3243
-    # Redefining emphasis markers so that emphasis by underscore does not
3244
-    # work in the middle of a word.
3245
-    #
3246
-    var $em_relist = array(
3247
-        ''  => '(?:(?<!\*)\*(?!\*)|(?<![a-zA-Z0-9_])_(?!_))(?=\S|$)(?![\.,:;]\s)',
3248
-        '*' => '(?<=\S|^)(?<!\*)\*(?!\*)',
3249
-        '_' => '(?<=\S|^)(?<!_)_(?![a-zA-Z0-9_])',
3250
-    );
3251
-
3252
-    var $strong_relist = array(
3253
-        ''   => '(?:(?<!\*)\*\*(?!\*)|(?<![a-zA-Z0-9_])__(?!_))(?=\S|$)(?![\.,:;]\s)',
3254
-        '**' => '(?<=\S|^)(?<!\*)\*\*(?!\*)',
3255
-        '__' => '(?<=\S|^)(?<!_)__(?![a-zA-Z0-9_])',
3256
-    );
3257
-
3258
-    var $em_strong_relist = array(
3259
-        ''    => '(?:(?<!\*)\*\*\*(?!\*)|(?<![a-zA-Z0-9_])___(?!_))(?=\S|$)(?![\.,:;]\s)',
3260
-        '***' => '(?<=\S|^)(?<!\*)\*\*\*(?!\*)',
3261
-        '___' => '(?<=\S|^)(?<!_)___(?![a-zA-Z0-9_])',
3262
-    );
3263
-
3264
-    function formParagraphs($text)
3265
-    {
3266
-        #
3267
-        #	Params:
3268
-        #		$text - string to process with html <p> tags
3269
-        #
3270
-        # Strip leading and trailing lines:
3271
-        $text = preg_replace('/\A\n+|\n+\z/', '', $text);
3272
-
3273
-        $grafs = preg_split('/\n{2,}/', $text, - 1, PREG_SPLIT_NO_EMPTY);
3274
-
3275
-        #
3276
-        # Wrap <p> tags and unhashify HTML blocks
3277
-        #
3278
-        foreach ($grafs as $key => $value) {
3279
-            $value = trim($this->runSpanGamut($value));
3280
-
3281
-            # Check if this should be enclosed in a paragraph.
3282
-            # Clean tag hashes & block tag hashes are left alone.
3283
-            $is_p = !preg_match('/^B\x1A[0-9]+B|^C\x1A[0-9]+C$/', $value);
3284
-
3285
-            if ($is_p) {
3286
-                $value = "<p>$value</p>";
3287
-            }
3288
-            $grafs[$key] = $value;
3289
-        }
3290
-
3291
-        # Join grafs in one text, then unhash HTML tags.
3292
-        $text = implode("\n\n", $grafs);
3293
-
3294
-        # Finish by removing any tag hashes still present in $text.
3295
-        $text = $this->unhash($text);
3296
-
3297
-        return $text;
3298
-    }
3299
-
3300
-    ### Footnotes
3301
-
3302
-    function stripFootnotes($text)
3303
-    {
3304
-        #
3305
-        # Strips link definitions from text, stores the URLs and titles in
3306
-        # hash references.
3307
-        #
3308
-        $less_than_tab = $this->tab_width - 1;
3309
-
3310
-        # Link defs are in the form: [^id]: url "optional title"
3311
-        $text = preg_replace_callback(
3312
-            '{
3199
+			array(&$this, '_doFencedCodeBlocks_callback'),
3200
+			$text
3201
+		);
3202
+
3203
+		return $text;
3204
+	}
3205
+
3206
+	function _doFencedCodeBlocks_callback($matches)
3207
+	{
3208
+		$classname =& $matches[2];
3209
+		$attrs     =& $matches[3];
3210
+		$codeblock = $matches[4];
3211
+		$codeblock = htmlspecialchars($codeblock, ENT_NOQUOTES);
3212
+		$codeblock = preg_replace_callback(
3213
+			'/^\n+/',
3214
+			array(&$this, '_doFencedCodeBlocks_newlines'),
3215
+			$codeblock
3216
+		);
3217
+
3218
+		if ($classname != "") {
3219
+			if ($classname{0} == '.') {
3220
+				$classname = substr($classname, 1);
3221
+			}
3222
+			$attr_str = ' class="' . $this->code_class_prefix . $classname . '"';
3223
+		} else {
3224
+			$attr_str = $this->doExtraAttributes($this->code_attr_on_pre ? "pre" : "code", $attrs);
3225
+		}
3226
+		$pre_attr_str  = $this->code_attr_on_pre ? $attr_str : '';
3227
+		$code_attr_str = $this->code_attr_on_pre ? '' : $attr_str;
3228
+		$codeblock     = "<pre$pre_attr_str><code$code_attr_str>$codeblock</code></pre>";
3229
+
3230
+		return "\n\n" . $this->hashBlock($codeblock) . "\n\n";
3231
+	}
3232
+
3233
+	function _doFencedCodeBlocks_newlines($matches)
3234
+	{
3235
+		return str_repeat(
3236
+			"<br$this->empty_element_suffix",
3237
+			strlen($matches[0])
3238
+		);
3239
+	}
3240
+
3241
+
3242
+	#
3243
+	# Redefining emphasis markers so that emphasis by underscore does not
3244
+	# work in the middle of a word.
3245
+	#
3246
+	var $em_relist = array(
3247
+		''  => '(?:(?<!\*)\*(?!\*)|(?<![a-zA-Z0-9_])_(?!_))(?=\S|$)(?![\.,:;]\s)',
3248
+		'*' => '(?<=\S|^)(?<!\*)\*(?!\*)',
3249
+		'_' => '(?<=\S|^)(?<!_)_(?![a-zA-Z0-9_])',
3250
+	);
3251
+
3252
+	var $strong_relist = array(
3253
+		''   => '(?:(?<!\*)\*\*(?!\*)|(?<![a-zA-Z0-9_])__(?!_))(?=\S|$)(?![\.,:;]\s)',
3254
+		'**' => '(?<=\S|^)(?<!\*)\*\*(?!\*)',
3255
+		'__' => '(?<=\S|^)(?<!_)__(?![a-zA-Z0-9_])',
3256
+	);
3257
+
3258
+	var $em_strong_relist = array(
3259
+		''    => '(?:(?<!\*)\*\*\*(?!\*)|(?<![a-zA-Z0-9_])___(?!_))(?=\S|$)(?![\.,:;]\s)',
3260
+		'***' => '(?<=\S|^)(?<!\*)\*\*\*(?!\*)',
3261
+		'___' => '(?<=\S|^)(?<!_)___(?![a-zA-Z0-9_])',
3262
+	);
3263
+
3264
+	function formParagraphs($text)
3265
+	{
3266
+		#
3267
+		#	Params:
3268
+		#		$text - string to process with html <p> tags
3269
+		#
3270
+		# Strip leading and trailing lines:
3271
+		$text = preg_replace('/\A\n+|\n+\z/', '', $text);
3272
+
3273
+		$grafs = preg_split('/\n{2,}/', $text, - 1, PREG_SPLIT_NO_EMPTY);
3274
+
3275
+		#
3276
+		# Wrap <p> tags and unhashify HTML blocks
3277
+		#
3278
+		foreach ($grafs as $key => $value) {
3279
+			$value = trim($this->runSpanGamut($value));
3280
+
3281
+			# Check if this should be enclosed in a paragraph.
3282
+			# Clean tag hashes & block tag hashes are left alone.
3283
+			$is_p = !preg_match('/^B\x1A[0-9]+B|^C\x1A[0-9]+C$/', $value);
3284
+
3285
+			if ($is_p) {
3286
+				$value = "<p>$value</p>";
3287
+			}
3288
+			$grafs[$key] = $value;
3289
+		}
3290
+
3291
+		# Join grafs in one text, then unhash HTML tags.
3292
+		$text = implode("\n\n", $grafs);
3293
+
3294
+		# Finish by removing any tag hashes still present in $text.
3295
+		$text = $this->unhash($text);
3296
+
3297
+		return $text;
3298
+	}
3299
+
3300
+	### Footnotes
3301
+
3302
+	function stripFootnotes($text)
3303
+	{
3304
+		#
3305
+		# Strips link definitions from text, stores the URLs and titles in
3306
+		# hash references.
3307
+		#
3308
+		$less_than_tab = $this->tab_width - 1;
3309
+
3310
+		# Link defs are in the form: [^id]: url "optional title"
3311
+		$text = preg_replace_callback(
3312
+			'{
3313 3313
 			^[ ]{0,' . $less_than_tab . '}\[\^(.+?)\][ ]?:	# note_id = $1
3314 3314
 			  [ ]*
3315 3315
 			  \n?					# maybe *one* newline
@@ -3324,215 +3324,215 @@  discard block
 block discarded – undo
3324 3324
 				)*
3325 3325
 			)
3326 3326
 			}xm',
3327
-            array(&$this, '_stripFootnotes_callback'),
3328
-            $text
3329
-        );
3330
-        return $text;
3331
-    }
3332
-
3333
-    function _stripFootnotes_callback($matches)
3334
-    {
3335
-        $note_id                   = $this->fn_id_prefix . $matches[1];
3336
-        $this->footnotes[$note_id] = $this->outdent($matches[2]);
3337
-        return ''; # String that will replace the block
3338
-    }
3339
-
3340
-    function doFootnotes($text)
3341
-    {
3342
-        #
3343
-        # Replace footnote references in $text [^id] with a special text-token
3344
-        # which will be replaced by the actual footnote marker in appendFootnotes.
3345
-        #
3346
-        if (!$this->in_anchor) {
3347
-            $text = preg_replace('{\[\^(.+?)\]}', "F\x1Afn:\\1\x1A:", $text);
3348
-        }
3349
-        return $text;
3350
-    }
3351
-
3352
-    function appendFootnotes($text)
3353
-    {
3354
-        #
3355
-        # Append footnote list to text.
3356
-        #
3357
-        $text = preg_replace_callback(
3358
-            '{F\x1Afn:(.*?)\x1A:}',
3359
-            array(&$this, '_appendFootnotes_callback'),
3360
-            $text
3361
-        );
3362
-
3363
-        if (!empty($this->footnotes_ordered)) {
3364
-            $text .= "\n\n";
3365
-            $text .= "<div class=\"footnotes\">\n";
3366
-            $text .= "<hr" . $this->empty_element_suffix . "\n";
3367
-            $text .= "<ol>\n\n";
3368
-
3369
-            $attr = " rev=\"footnote\"";
3370
-            if ($this->fn_backlink_class != "") {
3371
-                $class = $this->fn_backlink_class;
3372
-                $class = $this->encodeAttribute($class);
3373
-                $attr .= " class=\"$class\"";
3374
-            }
3375
-            if ($this->fn_backlink_title != "") {
3376
-                $title = $this->fn_backlink_title;
3377
-                $title = $this->encodeAttribute($title);
3378
-                $attr .= " title=\"$title\"";
3379
-            }
3380
-            $num = 0;
3381
-
3382
-            while (!empty($this->footnotes_ordered)) {
3383
-                $footnote = reset($this->footnotes_ordered);
3384
-                $note_id  = key($this->footnotes_ordered);
3385
-                unset($this->footnotes_ordered[$note_id]);
3386
-                $ref_count = $this->footnotes_ref_count[$note_id];
3387
-                unset($this->footnotes_ref_count[$note_id]);
3388
-                unset($this->footnotes[$note_id]);
3389
-
3390
-                $footnote .= "\n"; # Need to append newline before parsing.
3391
-                $footnote = $this->runBlockGamut("$footnote\n");
3392
-                $footnote = preg_replace_callback(
3393
-                    '{F\x1Afn:(.*?)\x1A:}',
3394
-                    array(&$this, '_appendFootnotes_callback'),
3395
-                    $footnote
3396
-                );
3397
-
3398
-                $attr    = str_replace("%%", ++ $num, $attr);
3399
-                $note_id = $this->encodeAttribute($note_id);
3400
-
3401
-                # Prepare backlink, multiple backlinks if multiple references
3402
-                $backlink = "<a href=\"#fnref:$note_id\"$attr>&#8617;</a>";
3403
-                for ($ref_num = 2; $ref_num <= $ref_count; ++ $ref_num) {
3404
-                    $backlink .= " <a href=\"#fnref$ref_num:$note_id\"$attr>&#8617;</a>";
3405
-                }
3406
-                # Add backlink to last paragraph; create new paragraph if needed.
3407
-                if (preg_match('{</p>$}', $footnote)) {
3408
-                    $footnote = substr($footnote, 0, - 4) . "&#160;$backlink</p>";
3409
-                } else {
3410
-                    $footnote .= "\n\n<p>$backlink</p>";
3411
-                }
3412
-
3413
-                $text .= "<li id=\"fn:$note_id\">\n";
3414
-                $text .= $footnote . "\n";
3415
-                $text .= "</li>\n\n";
3416
-            }
3417
-
3418
-            $text .= "</ol>\n";
3419
-            $text .= "</div>";
3420
-        }
3421
-        return $text;
3422
-    }
3423
-
3424
-    function _appendFootnotes_callback($matches)
3425
-    {
3426
-        $node_id = $this->fn_id_prefix . $matches[1];
3427
-
3428
-        # Create footnote marker only if it has a corresponding footnote *and*
3429
-        # the footnote hasn't been used by another marker.
3430
-        if (isset($this->footnotes[$node_id])) {
3431
-            $num =& $this->footnotes_numbers[$node_id];
3432
-            if (!isset($num)) {
3433
-                # Transfer footnote content to the ordered list and give it its
3434
-                # number
3435
-                $this->footnotes_ordered[$node_id]   = $this->footnotes[$node_id];
3436
-                $this->footnotes_ref_count[$node_id] = 1;
3437
-                $num                                 = $this->footnote_counter ++;
3438
-                $ref_count_mark                      = '';
3439
-            } else {
3440
-                $ref_count_mark = $this->footnotes_ref_count[$node_id] += 1;
3441
-            }
3442
-
3443
-            $attr = " rel=\"footnote\"";
3444
-            if ($this->fn_link_class != "") {
3445
-                $class = $this->fn_link_class;
3446
-                $class = $this->encodeAttribute($class);
3447
-                $attr .= " class=\"$class\"";
3448
-            }
3449
-            if ($this->fn_link_title != "") {
3450
-                $title = $this->fn_link_title;
3451
-                $title = $this->encodeAttribute($title);
3452
-                $attr .= " title=\"$title\"";
3453
-            }
3454
-
3455
-            $attr    = str_replace("%%", $num, $attr);
3456
-            $node_id = $this->encodeAttribute($node_id);
3457
-
3458
-            return
3459
-                "<sup id=\"fnref$ref_count_mark:$node_id\">" .
3460
-                "<a href=\"#fn:$node_id\"$attr>$num</a>" .
3461
-                "</sup>";
3462
-        }
3463
-
3464
-        return "[^" . $matches[1] . "]";
3465
-    }
3466
-
3467
-    ### Abbreviations ###
3468
-
3469
-    function stripAbbreviations($text)
3470
-    {
3471
-        #
3472
-        # Strips abbreviations from text, stores titles in hash references.
3473
-        #
3474
-        $less_than_tab = $this->tab_width - 1;
3475
-
3476
-        # Link defs are in the form: [id]*: url "optional title"
3477
-        $text = preg_replace_callback(
3478
-            '{
3327
+			array(&$this, '_stripFootnotes_callback'),
3328
+			$text
3329
+		);
3330
+		return $text;
3331
+	}
3332
+
3333
+	function _stripFootnotes_callback($matches)
3334
+	{
3335
+		$note_id                   = $this->fn_id_prefix . $matches[1];
3336
+		$this->footnotes[$note_id] = $this->outdent($matches[2]);
3337
+		return ''; # String that will replace the block
3338
+	}
3339
+
3340
+	function doFootnotes($text)
3341
+	{
3342
+		#
3343
+		# Replace footnote references in $text [^id] with a special text-token
3344
+		# which will be replaced by the actual footnote marker in appendFootnotes.
3345
+		#
3346
+		if (!$this->in_anchor) {
3347
+			$text = preg_replace('{\[\^(.+?)\]}', "F\x1Afn:\\1\x1A:", $text);
3348
+		}
3349
+		return $text;
3350
+	}
3351
+
3352
+	function appendFootnotes($text)
3353
+	{
3354
+		#
3355
+		# Append footnote list to text.
3356
+		#
3357
+		$text = preg_replace_callback(
3358
+			'{F\x1Afn:(.*?)\x1A:}',
3359
+			array(&$this, '_appendFootnotes_callback'),
3360
+			$text
3361
+		);
3362
+
3363
+		if (!empty($this->footnotes_ordered)) {
3364
+			$text .= "\n\n";
3365
+			$text .= "<div class=\"footnotes\">\n";
3366
+			$text .= "<hr" . $this->empty_element_suffix . "\n";
3367
+			$text .= "<ol>\n\n";
3368
+
3369
+			$attr = " rev=\"footnote\"";
3370
+			if ($this->fn_backlink_class != "") {
3371
+				$class = $this->fn_backlink_class;
3372
+				$class = $this->encodeAttribute($class);
3373
+				$attr .= " class=\"$class\"";
3374
+			}
3375
+			if ($this->fn_backlink_title != "") {
3376
+				$title = $this->fn_backlink_title;
3377
+				$title = $this->encodeAttribute($title);
3378
+				$attr .= " title=\"$title\"";
3379
+			}
3380
+			$num = 0;
3381
+
3382
+			while (!empty($this->footnotes_ordered)) {
3383
+				$footnote = reset($this->footnotes_ordered);
3384
+				$note_id  = key($this->footnotes_ordered);
3385
+				unset($this->footnotes_ordered[$note_id]);
3386
+				$ref_count = $this->footnotes_ref_count[$note_id];
3387
+				unset($this->footnotes_ref_count[$note_id]);
3388
+				unset($this->footnotes[$note_id]);
3389
+
3390
+				$footnote .= "\n"; # Need to append newline before parsing.
3391
+				$footnote = $this->runBlockGamut("$footnote\n");
3392
+				$footnote = preg_replace_callback(
3393
+					'{F\x1Afn:(.*?)\x1A:}',
3394
+					array(&$this, '_appendFootnotes_callback'),
3395
+					$footnote
3396
+				);
3397
+
3398
+				$attr    = str_replace("%%", ++ $num, $attr);
3399
+				$note_id = $this->encodeAttribute($note_id);
3400
+
3401
+				# Prepare backlink, multiple backlinks if multiple references
3402
+				$backlink = "<a href=\"#fnref:$note_id\"$attr>&#8617;</a>";
3403
+				for ($ref_num = 2; $ref_num <= $ref_count; ++ $ref_num) {
3404
+					$backlink .= " <a href=\"#fnref$ref_num:$note_id\"$attr>&#8617;</a>";
3405
+				}
3406
+				# Add backlink to last paragraph; create new paragraph if needed.
3407
+				if (preg_match('{</p>$}', $footnote)) {
3408
+					$footnote = substr($footnote, 0, - 4) . "&#160;$backlink</p>";
3409
+				} else {
3410
+					$footnote .= "\n\n<p>$backlink</p>";
3411
+				}
3412
+
3413
+				$text .= "<li id=\"fn:$note_id\">\n";
3414
+				$text .= $footnote . "\n";
3415
+				$text .= "</li>\n\n";
3416
+			}
3417
+
3418
+			$text .= "</ol>\n";
3419
+			$text .= "</div>";
3420
+		}
3421
+		return $text;
3422
+	}
3423
+
3424
+	function _appendFootnotes_callback($matches)
3425
+	{
3426
+		$node_id = $this->fn_id_prefix . $matches[1];
3427
+
3428
+		# Create footnote marker only if it has a corresponding footnote *and*
3429
+		# the footnote hasn't been used by another marker.
3430
+		if (isset($this->footnotes[$node_id])) {
3431
+			$num =& $this->footnotes_numbers[$node_id];
3432
+			if (!isset($num)) {
3433
+				# Transfer footnote content to the ordered list and give it its
3434
+				# number
3435
+				$this->footnotes_ordered[$node_id]   = $this->footnotes[$node_id];
3436
+				$this->footnotes_ref_count[$node_id] = 1;
3437
+				$num                                 = $this->footnote_counter ++;
3438
+				$ref_count_mark                      = '';
3439
+			} else {
3440
+				$ref_count_mark = $this->footnotes_ref_count[$node_id] += 1;
3441
+			}
3442
+
3443
+			$attr = " rel=\"footnote\"";
3444
+			if ($this->fn_link_class != "") {
3445
+				$class = $this->fn_link_class;
3446
+				$class = $this->encodeAttribute($class);
3447
+				$attr .= " class=\"$class\"";
3448
+			}
3449
+			if ($this->fn_link_title != "") {
3450
+				$title = $this->fn_link_title;
3451
+				$title = $this->encodeAttribute($title);
3452
+				$attr .= " title=\"$title\"";
3453
+			}
3454
+
3455
+			$attr    = str_replace("%%", $num, $attr);
3456
+			$node_id = $this->encodeAttribute($node_id);
3457
+
3458
+			return
3459
+				"<sup id=\"fnref$ref_count_mark:$node_id\">" .
3460
+				"<a href=\"#fn:$node_id\"$attr>$num</a>" .
3461
+				"</sup>";
3462
+		}
3463
+
3464
+		return "[^" . $matches[1] . "]";
3465
+	}
3466
+
3467
+	### Abbreviations ###
3468
+
3469
+	function stripAbbreviations($text)
3470
+	{
3471
+		#
3472
+		# Strips abbreviations from text, stores titles in hash references.
3473
+		#
3474
+		$less_than_tab = $this->tab_width - 1;
3475
+
3476
+		# Link defs are in the form: [id]*: url "optional title"
3477
+		$text = preg_replace_callback(
3478
+			'{
3479 3479
 			^[ ]{0,' . $less_than_tab . '}\*\[(.+?)\][ ]?:	# abbr_id = $1
3480 3480
 			(.*)					# text = $2 (no blank lines allowed)
3481 3481
 			}xm',
3482
-            array(&$this, '_stripAbbreviations_callback'),
3483
-            $text
3484
-        );
3485
-        return $text;
3486
-    }
3487
-
3488
-    function _stripAbbreviations_callback($matches)
3489
-    {
3490
-        $abbr_word = $matches[1];
3491
-        $abbr_desc = $matches[2];
3492
-        if ($this->abbr_word_re) {
3493
-            $this->abbr_word_re .= '|';
3494
-        }
3495
-        $this->abbr_word_re .= preg_quote($abbr_word);
3496
-        $this->abbr_desciptions[$abbr_word] = trim($abbr_desc);
3497
-        return ''; # String that will replace the block
3498
-    }
3499
-
3500
-    function doAbbreviations($text)
3501
-    {
3502
-        #
3503
-        # Find defined abbreviations in text and wrap them in <abbr> elements.
3504
-        #
3505
-        if ($this->abbr_word_re) {
3506
-            // cannot use the /x modifier because abbr_word_re may
3507
-            // contain significant spaces:
3508
-            $text = preg_replace_callback(
3509
-                '{' .
3510
-                '(?<![\w\x1A])' .
3511
-                '(?:' . $this->abbr_word_re . ')' .
3512
-                '(?![\w\x1A])' .
3513
-                '}',
3514
-                array(&$this, '_doAbbreviations_callback'),
3515
-                $text
3516
-            );
3517
-        }
3518
-        return $text;
3519
-    }
3520
-
3521
-    function _doAbbreviations_callback($matches)
3522
-    {
3523
-        $abbr = $matches[0];
3524
-        if (isset($this->abbr_desciptions[$abbr])) {
3525
-            $desc = $this->abbr_desciptions[$abbr];
3526
-            if (empty($desc)) {
3527
-                return $this->hashPart("<abbr>$abbr</abbr>");
3528
-            } else {
3529
-                $desc = $this->encodeAttribute($desc);
3530
-                return $this->hashPart("<abbr title=\"$desc\">$abbr</abbr>");
3531
-            }
3532
-        } else {
3533
-            return $matches[0];
3534
-        }
3535
-    }
3482
+			array(&$this, '_stripAbbreviations_callback'),
3483
+			$text
3484
+		);
3485
+		return $text;
3486
+	}
3487
+
3488
+	function _stripAbbreviations_callback($matches)
3489
+	{
3490
+		$abbr_word = $matches[1];
3491
+		$abbr_desc = $matches[2];
3492
+		if ($this->abbr_word_re) {
3493
+			$this->abbr_word_re .= '|';
3494
+		}
3495
+		$this->abbr_word_re .= preg_quote($abbr_word);
3496
+		$this->abbr_desciptions[$abbr_word] = trim($abbr_desc);
3497
+		return ''; # String that will replace the block
3498
+	}
3499
+
3500
+	function doAbbreviations($text)
3501
+	{
3502
+		#
3503
+		# Find defined abbreviations in text and wrap them in <abbr> elements.
3504
+		#
3505
+		if ($this->abbr_word_re) {
3506
+			// cannot use the /x modifier because abbr_word_re may
3507
+			// contain significant spaces:
3508
+			$text = preg_replace_callback(
3509
+				'{' .
3510
+				'(?<![\w\x1A])' .
3511
+				'(?:' . $this->abbr_word_re . ')' .
3512
+				'(?![\w\x1A])' .
3513
+				'}',
3514
+				array(&$this, '_doAbbreviations_callback'),
3515
+				$text
3516
+			);
3517
+		}
3518
+		return $text;
3519
+	}
3520
+
3521
+	function _doAbbreviations_callback($matches)
3522
+	{
3523
+		$abbr = $matches[0];
3524
+		if (isset($this->abbr_desciptions[$abbr])) {
3525
+			$desc = $this->abbr_desciptions[$abbr];
3526
+			if (empty($desc)) {
3527
+				return $this->hashPart("<abbr>$abbr</abbr>");
3528
+			} else {
3529
+				$desc = $this->encodeAttribute($desc);
3530
+				return $this->hashPart("<abbr title=\"$desc\">$abbr</abbr>");
3531
+			}
3532
+		} else {
3533
+			return $matches[0];
3534
+		}
3535
+	}
3536 3536
 }
3537 3537
 
3538 3538
 /*
Please login to merge, or discard this patch.
application/libraries/Migration.php 2 patches
Doc Comments   +2 added lines, -2 removed lines patch added patch discarded remove patch
@@ -321,7 +321,7 @@  discard block
 block discarded – undo
321 321
      *
322 322
      * @param string $type  Any key from _migration_paths, or {module_name}
323 323
      *
324
-     * @return bool|string
324
+     * @return false|string
325 325
      */
326 326
     public function get_latest($type='app')
327 327
     {
@@ -465,7 +465,7 @@  discard block
 block discarded – undo
465 465
 	/**
466 466
 	 * Based on the 'type', determines the correct migration path.
467 467
 	 *
468
-	 * @param $type
468
+	 * @param string $type
469 469
 	 * @param bool $create
470 470
 	 *
471 471
 	 * @return null|string
Please login to merge, or discard this patch.
Indentation   +508 added lines, -508 removed lines patch added patch discarded remove patch
@@ -51,415 +51,415 @@  discard block
 block discarded – undo
51 51
  */
52 52
 class CI_Migration {
53 53
 
54
-    /**
55
-     * Whether the library is enabled
56
-     *
57
-     * @var bool
58
-     */
59
-    protected $_migration_enabled = FALSE;
60
-
61
-    /**
62
-     * Migration numbering type
63
-     *
64
-     * @var	bool
65
-     */
66
-    protected $_migration_type = 'sequential';
67
-
68
-    /**
69
-     * Path to migration classes
70
-     *
71
-     * @var string
72
-     */
73
-    protected  $_migration_paths = NULL;
74
-
75
-    /**
76
-     * Current migration version
77
-     *
78
-     * @var mixed
79
-     */
80
-    protected $_migration_version = 0;
81
-
82
-    /**
83
-     * Database table with migration info
84
-     *
85
-     * @var string
86
-     */
87
-    protected $_migration_table = 'migrations';
88
-
89
-    /**
90
-     * Whether to automatically run migrations
91
-     *
92
-     * @var	bool
93
-     */
94
-    protected $_migration_auto_latest = FALSE;
95
-
96
-    /**
97
-     * Migration basename regex
98
-     *
99
-     * @var bool
100
-     */
101
-    protected $_migration_regex = NULL;
102
-
103
-    /**
104
-     * Error message
105
-     *
106
-     * @var string
107
-     */
108
-    protected $_error_string = '';
109
-
110
-    /**
111
-     * Initialize Migration Class
112
-     *
113
-     * @param	array	$config
114
-     * @return	void
115
-     */
116
-    public function __construct($config = array())
117
-    {
118
-        // Only run this constructor on main library load
119
-        if ( ! in_array(get_class($this), array('CI_Migration', config_item('subclass_prefix').'Migration'), TRUE))
120
-        {
121
-            return;
122
-        }
123
-
124
-        foreach ($config as $key => $val)
125
-        {
126
-            $this->{'_'.$key} = $val;
127
-        }
128
-
129
-        log_message('debug', 'Migrations class initialized');
130
-
131
-        // Are they trying to use migrations while it is disabled?
132
-        if ($this->_migration_enabled !== TRUE)
133
-        {
134
-            show_error('Migrations has been loaded but is disabled or set up incorrectly.');
135
-        }
136
-
137
-        // If not set, set it
138
-        count($this->_migration_paths) OR $this->_migration_paths = array(APPPATH.'database/migrations/');
139
-
140
-        // Add trailing slash if not set
141
-        foreach ($this->_migration_paths as $alias => $path) {
142
-            $this->_migration_paths[$alias] = rtrim($this->_migration_paths[$alias], '/') . '/';
143
-        }
144
-
145
-        // Load migration language
146
-        $this->lang->load('migration');
147
-
148
-        // They'll probably be using dbforge
149
-        $this->load->dbforge();
150
-
151
-        // Make sure the migration table name was set.
152
-        if (empty($this->_migration_table))
153
-        {
154
-            show_error('Migrations configuration file (migration.php) must have "migration_table" set.');
155
-        }
156
-
157
-        // Migration basename regex
158
-        $this->_migration_regex = ($this->_migration_type === 'timestamp')
159
-            ? '/^\d{14}_(\w+)$/'
160
-            : '/^\d{3}_(\w+)$/';
161
-
162
-        // Make sure a valid migration numbering type was set.
163
-        if ( ! in_array($this->_migration_type, array('sequential', 'timestamp')))
164
-        {
165
-            show_error('An invalid migration numbering type was specified: '.$this->_migration_type);
166
-        }
167
-
168
-        // If the migrations table is missing, make it
169
-        if ( ! $this->db->table_exists($this->_migration_table))
170
-        {
171
-            $this->dbforge->add_field(array(
172
-                'version' => array('type' => 'BIGINT', 'constraint' => 20),
173
-                'alias' => array('type' => 'VARCHAR', 'constraint' => 255),
174
-                'ondate'  => array('type' => 'DATETIME')
175
-            ));
176
-
177
-            $this->dbforge->add_key('alias');
178
-
179
-            $this->dbforge->create_table($this->_migration_table, TRUE);
180
-        }
181
-
182
-        // Do we auto migrate to the latest migration?
183
-        if ($this->_migration_auto_latest === TRUE && ! $this->latest())
184
-        {
185
-            show_error($this->error_string());
186
-        }
187
-
188
-    }
189
-
190
-    // --------------------------------------------------------------------
191
-
192
-    /**
193
-     * Migrate to a schema version
194
-     *
195
-     * Calls each migration step required to get to the schema version of
196
-     * choice
197
-     *
198
-     * @param string $type  Any key from _migration_paths, or {module_name}
199
-     * @param	string	$target_version	Target schema version
200
-     *
201
-     * @return	mixed	TRUE if already latest, FALSE if failed, string if upgraded
202
-     */
203
-    public function version($type='all', $target_version)
204
-    {
205
-        // Note: We use strings, so that timestamp versions work on 32-bit systems
206
-        $current_version = $this->get_version($type);
207
-
208
-        if ($this->_migration_type === 'sequential')
209
-        {
210
-            $target_version = sprintf('%03d', $target_version);
211
-        }
212
-        else
213
-        {
214
-            $target_version = (string) $target_version;
215
-        }
216
-
217
-        $migrations = $this->find_migrations($type);
218
-
219
-        if ($target_version > 0 && ! isset($migrations[$target_version]))
220
-        {
221
-            $this->_error_string = sprintf($this->lang->line('migration_not_found'), $target_version);
222
-            return FALSE;
223
-        }
224
-
225
-        if ($target_version > $current_version)
226
-        {
227
-            // Moving Up
228
-            $method = 'up';
229
-        }
230
-        else
231
-        {
232
-            // Moving Down, apply in reverse order
233
-            $method = 'down';
234
-            krsort($migrations);
235
-        }
236
-
237
-        if (empty($migrations))
238
-        {
239
-            return TRUE;
240
-        }
241
-
242
-        $previous = FALSE;
243
-
244
-        // Validate all available migrations, and run the ones within our target range
245
-        foreach ($migrations as $number => $file)
246
-        {
247
-            // Check for sequence gaps
248
-            if ($this->_migration_type === 'sequential' && $previous !== FALSE && abs($number - $previous) > 1)
249
-            {
250
-                $this->_error_string = sprintf($this->lang->line('migration_sequence_gap'), $number);
251
-                return FALSE;
252
-            }
253
-
254
-            include_once($file);
255
-            $class = 'Migration_'.ucfirst(strtolower($this->_get_migration_name(basename($file, '.php'))));
256
-
257
-            // Validate the migration file structure
258
-            if ( ! class_exists($class, FALSE))
259
-            {
260
-                $this->_error_string = sprintf($this->lang->line('migration_class_doesnt_exist'), $class);
261
-                return FALSE;
262
-            }
263
-
264
-            $previous = $number;
265
-
266
-            // Run migrations that are inside the target range
267
-            if (
268
-                ($method === 'up'   && $number > $current_version && $number <= $target_version) OR
269
-                ($method === 'down' && $number <= $current_version && $number > $target_version)
270
-            )
271
-            {
272
-                $instance = new $class();
273
-                if ( ! is_callable(array($instance, $method)))
274
-                {
275
-                    $this->_error_string = sprintf($this->lang->line('migration_missing_'.$method.'_method'), $class);
276
-                    return FALSE;
277
-                }
278
-
279
-                log_message('debug', 'Migrating '.$method.' from version '.$current_version.' to version '.$number);
280
-                call_user_func(array($instance, $method));
281
-                $current_version = $number;
282
-                $this->_update_version($type, $current_version);
283
-            }
284
-        }
285
-
286
-        // This is necessary when moving down, since the the last migration applied
287
-        // will be the down() method for the next migration up from the target
288
-        if ($current_version <> $target_version)
289
-        {
290
-            $current_version = $target_version;
291
-            $this->_update_version($type, $current_version);
292
-        }
293
-
294
-        log_message('debug', 'Finished migrating to '.$current_version);
295
-
296
-        return $current_version;
297
-    }
298
-
299
-    // --------------------------------------------------------------------
300
-
301
-    /**
302
-     * Sets the schema to the latest migration
303
-     *
304
-     * @param string $type  Any key from _migration_paths, or {module_name}
305
-     *
306
-     * @return	mixed	TRUE if already latest, FALSE if failed, string if upgraded
307
-     */
308
-    public function latest($type='app')
309
-    {
310
-        $last_migration = $this->get_latest($type);
311
-
312
-        // Calculate the last migration step from existing migration
313
-        // filenames and proceed to the standard version migration
314
-        return $this->version($type, $this->_get_migration_number($last_migration));
315
-    }
316
-
317
-    // --------------------------------------------------------------------
318
-
319
-    /**
320
-     * Retrieves the latest migration version available.
321
-     *
322
-     * @param string $type  Any key from _migration_paths, or {module_name}
323
-     *
324
-     * @return bool|string
325
-     */
326
-    public function get_latest($type='app')
327
-    {
328
-        $migrations = $this->find_migrations($type);
329
-
330
-        if (empty($migrations))
331
-        {
332
-            $this->_error_string = $this->lang->line('migration_none_found');
333
-            return FALSE;
334
-        }
335
-
336
-        return basename(end($migrations));
337
-    }
338
-
339
-    //--------------------------------------------------------------------
340
-
341
-
342
-
343
-    /**
344
-     * Sets the schema to the migration version set in config
345
-     *
346
-     * @return	mixed	TRUE if already current, FALSE if failed, string if upgraded
347
-     */
348
-    public function current()
349
-    {
350
-        return $this->version($this->_migration_version);
351
-    }
352
-
353
-    // --------------------------------------------------------------------
354
-
355
-    /**
356
-     * Error string
357
-     *
358
-     * @return	string	Error message returned as a string
359
-     */
360
-    public function error_string()
361
-    {
362
-        return $this->_error_string;
363
-    }
364
-
365
-    // --------------------------------------------------------------------
366
-
367
-    /**
368
-     * Retrieves list of available migration scripts
369
-     *
370
-     * @return	array	list of migration file paths sorted by version
371
-     */
372
-    public function find_migrations($type='app')
373
-    {
374
-        $migrations = array();
375
-
376
-        $path = $this->determine_migration_path($type);
377
-
378
-        // Load all *_*.php files in the migrations path
379
-        foreach (glob($path.'*_*.php') as $file)
380
-        {
381
-            $name = basename($file, '.php');
382
-
383
-            // Filter out non-migration files
384
-            if (preg_match($this->_migration_regex, $name))
385
-            {
386
-                $number = $this->_get_migration_number($name);
387
-
388
-                // There cannot be duplicate migration numbers
389
-                if (isset($migrations[$number]))
390
-                {
391
-                    $this->_error_string = sprintf($this->lang->line('migration_multiple_version'), $number);
392
-                    show_error($this->_error_string);
393
-                }
394
-
395
-                $migrations[$number] = $file;
396
-            }
397
-        }
398
-
399
-        ksort($migrations);
400
-        return $migrations;
401
-    }
402
-
403
-    // --------------------------------------------------------------------
404
-
405
-    /**
406
-     * Retrieves current schema version
407
-     *
408
-     * @return	string	Current migration version
409
-     */
410
-    public function get_version($type='app')
411
-    {
412
-        $row = $this->db->select('version')
413
-                        ->where('alias', $type)
414
-                        ->get($this->_migration_table)
415
-                        ->row();
416
-
417
-        return $row ? $row->version : '0';
418
-    }
419
-
420
-    // --------------------------------------------------------------------
421
-
422
-    /**
423
-     * Given the string for the name of the file, will
424
-     * generate the rest of the filename based on the current
425
-     * $config['migration_type'] setting.
426
-     *
427
-     * @param $name
428
-     * @return string The final name (with extension)
429
-     */
430
-    public function make_name($name)
431
-    {
432
-        if (empty($name))
433
-        {
434
-            return null;
435
-        }
436
-
437
-        if ($this->_migration_type == 'timestamp')
438
-        {
439
-            $prefix = date('YmdHis');
440
-        }
441
-        else
442
-        {
443
-            $prefix = str_pad($this->get_version() + 1, 3, '0', STR_PAD_LEFT);
444
-        }
445
-
446
-        return $prefix .'_'. ucfirst(strtolower($name)) .'.php';
447
-    }
448
-
449
-    //--------------------------------------------------------------------
450
-
451
-    /**
452
-     * Enable the use of CI super-global
453
-     *
454
-     * @param	string	$var
455
-     * @return	mixed
456
-     */
457
-    public function __get($var)
458
-    {
459
-        return get_instance()->$var;
460
-    }
461
-
462
-    //--------------------------------------------------------------------
54
+	/**
55
+	 * Whether the library is enabled
56
+	 *
57
+	 * @var bool
58
+	 */
59
+	protected $_migration_enabled = FALSE;
60
+
61
+	/**
62
+	 * Migration numbering type
63
+	 *
64
+	 * @var	bool
65
+	 */
66
+	protected $_migration_type = 'sequential';
67
+
68
+	/**
69
+	 * Path to migration classes
70
+	 *
71
+	 * @var string
72
+	 */
73
+	protected  $_migration_paths = NULL;
74
+
75
+	/**
76
+	 * Current migration version
77
+	 *
78
+	 * @var mixed
79
+	 */
80
+	protected $_migration_version = 0;
81
+
82
+	/**
83
+	 * Database table with migration info
84
+	 *
85
+	 * @var string
86
+	 */
87
+	protected $_migration_table = 'migrations';
88
+
89
+	/**
90
+	 * Whether to automatically run migrations
91
+	 *
92
+	 * @var	bool
93
+	 */
94
+	protected $_migration_auto_latest = FALSE;
95
+
96
+	/**
97
+	 * Migration basename regex
98
+	 *
99
+	 * @var bool
100
+	 */
101
+	protected $_migration_regex = NULL;
102
+
103
+	/**
104
+	 * Error message
105
+	 *
106
+	 * @var string
107
+	 */
108
+	protected $_error_string = '';
109
+
110
+	/**
111
+	 * Initialize Migration Class
112
+	 *
113
+	 * @param	array	$config
114
+	 * @return	void
115
+	 */
116
+	public function __construct($config = array())
117
+	{
118
+		// Only run this constructor on main library load
119
+		if ( ! in_array(get_class($this), array('CI_Migration', config_item('subclass_prefix').'Migration'), TRUE))
120
+		{
121
+			return;
122
+		}
123
+
124
+		foreach ($config as $key => $val)
125
+		{
126
+			$this->{'_'.$key} = $val;
127
+		}
128
+
129
+		log_message('debug', 'Migrations class initialized');
130
+
131
+		// Are they trying to use migrations while it is disabled?
132
+		if ($this->_migration_enabled !== TRUE)
133
+		{
134
+			show_error('Migrations has been loaded but is disabled or set up incorrectly.');
135
+		}
136
+
137
+		// If not set, set it
138
+		count($this->_migration_paths) OR $this->_migration_paths = array(APPPATH.'database/migrations/');
139
+
140
+		// Add trailing slash if not set
141
+		foreach ($this->_migration_paths as $alias => $path) {
142
+			$this->_migration_paths[$alias] = rtrim($this->_migration_paths[$alias], '/') . '/';
143
+		}
144
+
145
+		// Load migration language
146
+		$this->lang->load('migration');
147
+
148
+		// They'll probably be using dbforge
149
+		$this->load->dbforge();
150
+
151
+		// Make sure the migration table name was set.
152
+		if (empty($this->_migration_table))
153
+		{
154
+			show_error('Migrations configuration file (migration.php) must have "migration_table" set.');
155
+		}
156
+
157
+		// Migration basename regex
158
+		$this->_migration_regex = ($this->_migration_type === 'timestamp')
159
+			? '/^\d{14}_(\w+)$/'
160
+			: '/^\d{3}_(\w+)$/';
161
+
162
+		// Make sure a valid migration numbering type was set.
163
+		if ( ! in_array($this->_migration_type, array('sequential', 'timestamp')))
164
+		{
165
+			show_error('An invalid migration numbering type was specified: '.$this->_migration_type);
166
+		}
167
+
168
+		// If the migrations table is missing, make it
169
+		if ( ! $this->db->table_exists($this->_migration_table))
170
+		{
171
+			$this->dbforge->add_field(array(
172
+				'version' => array('type' => 'BIGINT', 'constraint' => 20),
173
+				'alias' => array('type' => 'VARCHAR', 'constraint' => 255),
174
+				'ondate'  => array('type' => 'DATETIME')
175
+			));
176
+
177
+			$this->dbforge->add_key('alias');
178
+
179
+			$this->dbforge->create_table($this->_migration_table, TRUE);
180
+		}
181
+
182
+		// Do we auto migrate to the latest migration?
183
+		if ($this->_migration_auto_latest === TRUE && ! $this->latest())
184
+		{
185
+			show_error($this->error_string());
186
+		}
187
+
188
+	}
189
+
190
+	// --------------------------------------------------------------------
191
+
192
+	/**
193
+	 * Migrate to a schema version
194
+	 *
195
+	 * Calls each migration step required to get to the schema version of
196
+	 * choice
197
+	 *
198
+	 * @param string $type  Any key from _migration_paths, or {module_name}
199
+	 * @param	string	$target_version	Target schema version
200
+	 *
201
+	 * @return	mixed	TRUE if already latest, FALSE if failed, string if upgraded
202
+	 */
203
+	public function version($type='all', $target_version)
204
+	{
205
+		// Note: We use strings, so that timestamp versions work on 32-bit systems
206
+		$current_version = $this->get_version($type);
207
+
208
+		if ($this->_migration_type === 'sequential')
209
+		{
210
+			$target_version = sprintf('%03d', $target_version);
211
+		}
212
+		else
213
+		{
214
+			$target_version = (string) $target_version;
215
+		}
216
+
217
+		$migrations = $this->find_migrations($type);
218
+
219
+		if ($target_version > 0 && ! isset($migrations[$target_version]))
220
+		{
221
+			$this->_error_string = sprintf($this->lang->line('migration_not_found'), $target_version);
222
+			return FALSE;
223
+		}
224
+
225
+		if ($target_version > $current_version)
226
+		{
227
+			// Moving Up
228
+			$method = 'up';
229
+		}
230
+		else
231
+		{
232
+			// Moving Down, apply in reverse order
233
+			$method = 'down';
234
+			krsort($migrations);
235
+		}
236
+
237
+		if (empty($migrations))
238
+		{
239
+			return TRUE;
240
+		}
241
+
242
+		$previous = FALSE;
243
+
244
+		// Validate all available migrations, and run the ones within our target range
245
+		foreach ($migrations as $number => $file)
246
+		{
247
+			// Check for sequence gaps
248
+			if ($this->_migration_type === 'sequential' && $previous !== FALSE && abs($number - $previous) > 1)
249
+			{
250
+				$this->_error_string = sprintf($this->lang->line('migration_sequence_gap'), $number);
251
+				return FALSE;
252
+			}
253
+
254
+			include_once($file);
255
+			$class = 'Migration_'.ucfirst(strtolower($this->_get_migration_name(basename($file, '.php'))));
256
+
257
+			// Validate the migration file structure
258
+			if ( ! class_exists($class, FALSE))
259
+			{
260
+				$this->_error_string = sprintf($this->lang->line('migration_class_doesnt_exist'), $class);
261
+				return FALSE;
262
+			}
263
+
264
+			$previous = $number;
265
+
266
+			// Run migrations that are inside the target range
267
+			if (
268
+				($method === 'up'   && $number > $current_version && $number <= $target_version) OR
269
+				($method === 'down' && $number <= $current_version && $number > $target_version)
270
+			)
271
+			{
272
+				$instance = new $class();
273
+				if ( ! is_callable(array($instance, $method)))
274
+				{
275
+					$this->_error_string = sprintf($this->lang->line('migration_missing_'.$method.'_method'), $class);
276
+					return FALSE;
277
+				}
278
+
279
+				log_message('debug', 'Migrating '.$method.' from version '.$current_version.' to version '.$number);
280
+				call_user_func(array($instance, $method));
281
+				$current_version = $number;
282
+				$this->_update_version($type, $current_version);
283
+			}
284
+		}
285
+
286
+		// This is necessary when moving down, since the the last migration applied
287
+		// will be the down() method for the next migration up from the target
288
+		if ($current_version <> $target_version)
289
+		{
290
+			$current_version = $target_version;
291
+			$this->_update_version($type, $current_version);
292
+		}
293
+
294
+		log_message('debug', 'Finished migrating to '.$current_version);
295
+
296
+		return $current_version;
297
+	}
298
+
299
+	// --------------------------------------------------------------------
300
+
301
+	/**
302
+	 * Sets the schema to the latest migration
303
+	 *
304
+	 * @param string $type  Any key from _migration_paths, or {module_name}
305
+	 *
306
+	 * @return	mixed	TRUE if already latest, FALSE if failed, string if upgraded
307
+	 */
308
+	public function latest($type='app')
309
+	{
310
+		$last_migration = $this->get_latest($type);
311
+
312
+		// Calculate the last migration step from existing migration
313
+		// filenames and proceed to the standard version migration
314
+		return $this->version($type, $this->_get_migration_number($last_migration));
315
+	}
316
+
317
+	// --------------------------------------------------------------------
318
+
319
+	/**
320
+	 * Retrieves the latest migration version available.
321
+	 *
322
+	 * @param string $type  Any key from _migration_paths, or {module_name}
323
+	 *
324
+	 * @return bool|string
325
+	 */
326
+	public function get_latest($type='app')
327
+	{
328
+		$migrations = $this->find_migrations($type);
329
+
330
+		if (empty($migrations))
331
+		{
332
+			$this->_error_string = $this->lang->line('migration_none_found');
333
+			return FALSE;
334
+		}
335
+
336
+		return basename(end($migrations));
337
+	}
338
+
339
+	//--------------------------------------------------------------------
340
+
341
+
342
+
343
+	/**
344
+	 * Sets the schema to the migration version set in config
345
+	 *
346
+	 * @return	mixed	TRUE if already current, FALSE if failed, string if upgraded
347
+	 */
348
+	public function current()
349
+	{
350
+		return $this->version($this->_migration_version);
351
+	}
352
+
353
+	// --------------------------------------------------------------------
354
+
355
+	/**
356
+	 * Error string
357
+	 *
358
+	 * @return	string	Error message returned as a string
359
+	 */
360
+	public function error_string()
361
+	{
362
+		return $this->_error_string;
363
+	}
364
+
365
+	// --------------------------------------------------------------------
366
+
367
+	/**
368
+	 * Retrieves list of available migration scripts
369
+	 *
370
+	 * @return	array	list of migration file paths sorted by version
371
+	 */
372
+	public function find_migrations($type='app')
373
+	{
374
+		$migrations = array();
375
+
376
+		$path = $this->determine_migration_path($type);
377
+
378
+		// Load all *_*.php files in the migrations path
379
+		foreach (glob($path.'*_*.php') as $file)
380
+		{
381
+			$name = basename($file, '.php');
382
+
383
+			// Filter out non-migration files
384
+			if (preg_match($this->_migration_regex, $name))
385
+			{
386
+				$number = $this->_get_migration_number($name);
387
+
388
+				// There cannot be duplicate migration numbers
389
+				if (isset($migrations[$number]))
390
+				{
391
+					$this->_error_string = sprintf($this->lang->line('migration_multiple_version'), $number);
392
+					show_error($this->_error_string);
393
+				}
394
+
395
+				$migrations[$number] = $file;
396
+			}
397
+		}
398
+
399
+		ksort($migrations);
400
+		return $migrations;
401
+	}
402
+
403
+	// --------------------------------------------------------------------
404
+
405
+	/**
406
+	 * Retrieves current schema version
407
+	 *
408
+	 * @return	string	Current migration version
409
+	 */
410
+	public function get_version($type='app')
411
+	{
412
+		$row = $this->db->select('version')
413
+						->where('alias', $type)
414
+						->get($this->_migration_table)
415
+						->row();
416
+
417
+		return $row ? $row->version : '0';
418
+	}
419
+
420
+	// --------------------------------------------------------------------
421
+
422
+	/**
423
+	 * Given the string for the name of the file, will
424
+	 * generate the rest of the filename based on the current
425
+	 * $config['migration_type'] setting.
426
+	 *
427
+	 * @param $name
428
+	 * @return string The final name (with extension)
429
+	 */
430
+	public function make_name($name)
431
+	{
432
+		if (empty($name))
433
+		{
434
+			return null;
435
+		}
436
+
437
+		if ($this->_migration_type == 'timestamp')
438
+		{
439
+			$prefix = date('YmdHis');
440
+		}
441
+		else
442
+		{
443
+			$prefix = str_pad($this->get_version() + 1, 3, '0', STR_PAD_LEFT);
444
+		}
445
+
446
+		return $prefix .'_'. ucfirst(strtolower($name)) .'.php';
447
+	}
448
+
449
+	//--------------------------------------------------------------------
450
+
451
+	/**
452
+	 * Enable the use of CI super-global
453
+	 *
454
+	 * @param	string	$var
455
+	 * @return	mixed
456
+	 */
457
+	public function __get($var)
458
+	{
459
+		return get_instance()->$var;
460
+	}
461
+
462
+	//--------------------------------------------------------------------
463 463
 
464 464
 
465 465
 	/**
@@ -470,109 +470,109 @@  discard block
 block discarded – undo
470 470
 	 *
471 471
 	 * @return null|string
472 472
 	 */
473
-    public function determine_migration_path($type, $create=false)
474
-    {
475
-        $type = strtolower($type);
473
+	public function determine_migration_path($type, $create=false)
474
+	{
475
+		$type = strtolower($type);
476 476
 
477
-        // Is it a module?
478
-        if (strpos($type, 'mod:') === 0)
479
-        {
480
-            $module = str_replace('mod:', '', $type);
477
+		// Is it a module?
478
+		if (strpos($type, 'mod:') === 0)
479
+		{
480
+			$module = str_replace('mod:', '', $type);
481 481
 
482
-            $path = \Myth\Modules::path($module, 'migrations');
482
+			$path = \Myth\Modules::path($module, 'migrations');
483 483
 
484
-	        // Should we return a 'created' module?
485
-	        // Use the first module path.
486
-	        if (empty($path) && $create === true)
487
-	        {
484
+			// Should we return a 'created' module?
485
+			// Use the first module path.
486
+			if (empty($path) && $create === true)
487
+			{
488 488
 				$folders = config_item('modules_locations');
489 489
 
490
-		        if (is_array($folders) && count($folders))
491
-		        {
492
-			        $path = $folders[0] . $module .'/migrations';
493
-		        }
494
-	        }
495
-
496
-            return rtrim($path, '/') .'/';
497
-        }
498
-
499
-        // Look in our predefined groups.
500
-        if (! empty($this->_migration_paths[$type]))
501
-        {
502
-            return rtrim($this->_migration_paths[$type], '/') .'/';
503
-        }
504
-
505
-        return null;
506
-    }
507
-
508
-    //--------------------------------------------------------------------
509
-
510
-    /**
511
-     * Returns the default migration path. This is basically the first
512
-     * path in the migration_paths array.
513
-     *
514
-     * @return string
515
-     */
516
-    public function default_migration_path()
517
-    {
518
-        return key($this->_migration_paths);
519
-    }
520
-
521
-    //--------------------------------------------------------------------
522
-
523
-
524
-    //--------------------------------------------------------------------
525
-    // Protected Methods
526
-    //--------------------------------------------------------------------
527
-
528
-    /**
529
-     * Extracts the migration number from a filename
530
-     *
531
-     * @param	string	$migration
532
-     * @return	string	Numeric portion of a migration filename
533
-     */
534
-    protected function _get_migration_number($migration)
535
-    {
536
-        return sscanf($migration, '%[0-9]+', $number)
537
-            ? $number : '0';
538
-    }
539
-
540
-    // --------------------------------------------------------------------
541
-
542
-    /**
543
-     * Extracts the migration class name from a filename
544
-     *
545
-     * @param	string	$migration
546
-     * @return	string	text portion of a migration filename
547
-     */
548
-    protected function _get_migration_name($migration)
549
-    {
550
-        $parts = explode('_', $migration);
551
-        array_shift($parts);
552
-        return implode('_', $parts);
553
-    }
554
-
555
-    // --------------------------------------------------------------------
556
-
557
-    /**
558
-     * Stores the current schema version
559
-     *
560
-     * @param   string  $type  Any key from _migration_paths, or {module_name}
561
-     * @param	string	$migration	Migration reached
562
-     * @return	mixed	Outputs a report of the migration
563
-     */
564
-    protected function _update_version($type='all', $migration)
565
-    {
566
-        $this->db->where('alias', $type)
567
-                 ->delete($this->_migration_table);
568
-
569
-        return $this->db->insert($this->_migration_table, array(
570
-            'version'   => $migration,
571
-            'alias'     => $type,
572
-            'ondate'    => date('Y-m-d H:i:s')
573
-        ));
574
-    }
575
-
576
-    // --------------------------------------------------------------------
490
+				if (is_array($folders) && count($folders))
491
+				{
492
+					$path = $folders[0] . $module .'/migrations';
493
+				}
494
+			}
495
+
496
+			return rtrim($path, '/') .'/';
497
+		}
498
+
499
+		// Look in our predefined groups.
500
+		if (! empty($this->_migration_paths[$type]))
501
+		{
502
+			return rtrim($this->_migration_paths[$type], '/') .'/';
503
+		}
504
+
505
+		return null;
506
+	}
507
+
508
+	//--------------------------------------------------------------------
509
+
510
+	/**
511
+	 * Returns the default migration path. This is basically the first
512
+	 * path in the migration_paths array.
513
+	 *
514
+	 * @return string
515
+	 */
516
+	public function default_migration_path()
517
+	{
518
+		return key($this->_migration_paths);
519
+	}
520
+
521
+	//--------------------------------------------------------------------
522
+
523
+
524
+	//--------------------------------------------------------------------
525
+	// Protected Methods
526
+	//--------------------------------------------------------------------
527
+
528
+	/**
529
+	 * Extracts the migration number from a filename
530
+	 *
531
+	 * @param	string	$migration
532
+	 * @return	string	Numeric portion of a migration filename
533
+	 */
534
+	protected function _get_migration_number($migration)
535
+	{
536
+		return sscanf($migration, '%[0-9]+', $number)
537
+			? $number : '0';
538
+	}
539
+
540
+	// --------------------------------------------------------------------
541
+
542
+	/**
543
+	 * Extracts the migration class name from a filename
544
+	 *
545
+	 * @param	string	$migration
546
+	 * @return	string	text portion of a migration filename
547
+	 */
548
+	protected function _get_migration_name($migration)
549
+	{
550
+		$parts = explode('_', $migration);
551
+		array_shift($parts);
552
+		return implode('_', $parts);
553
+	}
554
+
555
+	// --------------------------------------------------------------------
556
+
557
+	/**
558
+	 * Stores the current schema version
559
+	 *
560
+	 * @param   string  $type  Any key from _migration_paths, or {module_name}
561
+	 * @param	string	$migration	Migration reached
562
+	 * @return	mixed	Outputs a report of the migration
563
+	 */
564
+	protected function _update_version($type='all', $migration)
565
+	{
566
+		$this->db->where('alias', $type)
567
+				 ->delete($this->_migration_table);
568
+
569
+		return $this->db->insert($this->_migration_table, array(
570
+			'version'   => $migration,
571
+			'alias'     => $type,
572
+			'ondate'    => date('Y-m-d H:i:s')
573
+		));
574
+	}
575
+
576
+	// --------------------------------------------------------------------
577 577
 
578 578
 }
Please login to merge, or discard this patch.
application/models/User_model.php 2 patches
Doc Comments   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -292,7 +292,7 @@
 block discarded – undo
292 292
      * @param $user_id
293 293
      * @param $key
294 294
      *
295
-     * @return bool
295
+     * @return false|null
296 296
      */
297 297
     public function removeMetaFromUser($user_id, $key)
298 298
     {
Please login to merge, or discard this patch.
Indentation   +356 added lines, -356 removed lines patch added patch discarded remove patch
@@ -4,363 +4,363 @@
 block discarded – undo
4 4
 
5 5
 class User_model extends \Myth\Models\CIDbModel {
6 6
 
7
-    protected $table_name = 'users';
8
-
9
-    protected $soft_deletes = true;
10
-
11
-    protected $set_created = true;
12
-
13
-    protected $set_modified = false;
14
-
15
-    protected $protected_attributes = ['id', 'submit'];
16
-
17
-    protected $validation_rules = [
18
-        [
19
-            'field' => 'first_name',
20
-            'label' => 'lang:auth.first_name',
21
-            'rules' => 'trim|alpha|max_length[255]'
22
-        ],
23
-        [
24
-            'field' => 'last_name',
25
-            'label' => 'lang:auth.last_name',
26
-            'rules' => 'trim|alpha|max_length[255]'
27
-        ],
28
-        [
29
-            'field' => 'email',
30
-            'label' => 'lang:auth.email',
31
-            'rules' => 'trim|valid_email|max_length[255]'
32
-        ],
33
-        [
34
-            'field' => 'username',
35
-            'label' => 'lang:auth.username',
36
-            'rules' => 'trim|alpha_numeric|max_length[255]'
37
-        ],
38
-        [
39
-            'field' => 'password',
40
-            'label' => 'lang:auth.password',
41
-            'rules' => 'trim|max_length[255]|isStrongPassword'
42
-        ],
43
-        [
44
-            'field' => 'pass_confirm',
45
-            'label' => 'lang:auth.pass_confirm',
46
-            'rules' => 'trim|matches[password]'
47
-        ],
48
-    ];
49
-
50
-    protected $insert_validate_rules = [
51
-        'email'        => 'required|is_unique[users.email]',
52
-        'username'     => 'required|is_unique[users.username]',
53
-        'password'     => 'required',
54
-        'pass_confirm' => 'required'
55
-    ];
56
-
57
-    protected $before_insert = ['hashPassword'];
58
-    protected $before_update = ['hashPassword'];
59
-    protected $after_insert  = ['updateMeta'];
60
-    protected $after_update  = ['updateMeta'];
61
-
62
-    // The columns in the 'users_meta' table - for auto updating of profile information.
63
-    protected $meta_fields = ['first_name', 'last_name'];
64
-
65
-    protected $fields = ['id', 'email', 'username', 'password_hash', 'reset_hash', 'activate_hash', 'created_on', 'status', 'status_message', 'active', 'deleted', 'force_pass_reset'];
66
-
67
-    //--------------------------------------------------------------------
68
-
69
-    public function __construct()
70
-    {
71
-        parent::__construct();
72
-
73
-        $this->load->helper('auth/password');
74
-    }
75
-
76
-    //--------------------------------------------------------------------
77
-
78
-    /**
79
-     * Works with any find queries to return user_meta information.
80
-     *
81
-     * @return $this
82
-     */
83
-    public function withMeta()
84
-    {
85
-        $this->after_find[] = 'grabMeta';
86
-
87
-        return $this;
88
-    }
89
-
90
-    //--------------------------------------------------------------------
91
-
92
-    /**
93
-     * If exists, will take our password out of the data array, and
94
-     * create a new hash for it, which is inserted back into the
95
-     * data array to be saved to the database.
96
-     *
97
-     * @param array $data
98
-     *
99
-     * @return array
100
-     */
101
-    protected function hashPassword($data)
102
-    {
103
-        if (isset($data['fields']))
104
-        {
105
-            $data = $data['fields'];
106
-        }
107
-
108
-        if (isset($data['password']))
109
-        {
110
-            $data['password_hash'] = \Myth\Auth\Password::hashPassword($data['password']);
111
-
112
-            unset($data['password'], $data['pass_confirm']);
113
-        }
114
-
115
-        return $data;
116
-    }
117
-
118
-    //--------------------------------------------------------------------
119
-
120
-    /**
121
-     * A callback designed to work with Digest Authentication to create
122
-     * and store the $A1 value since we'll never have access to the
123
-     * password except during inserts or updates.
124
-     *
125
-     * This assumes that this is working as part of an API and that
126
-     * the api config file is already loaded into memory.
127
-     *
128
-     * @param $data
129
-     *
130
-     * @return $data
131
-     */
132
-    public function createDigestKey($data)
133
-    {
134
-        $field = config_item('api.auth_field');
135
-        $value = null;
136
-
137
-        // If it's an update, we probably won't have the username/email
138
-        // so grab it so that we can use it.
139
-        if (! empty($data[ $this->primary_key ]))
140
-        {
141
-            if (! isset($data[$field]) )
142
-            {
143
-                $value = $this->get_field( $data['id'], $field );
144
-            }
145
-        }
146
-        // However, if it's an insert, then we should have it, If we don't, leave.
147
-        else
148
-        {
149
-            if (empty($data[$field]))
150
-            {
151
-                return $data;
152
-            }
153
-
154
-            $value = $data[$field];
155
-        }
156
-
157
-        // Still here? then create the hash based on the current realm.
158
-        if (! empty($data['password']) )
159
-        {
160
-            $key = md5($value .':'. config_item('api.realm') .':'. $data['password']);
161
-            $data['api_key'] = $key;
162
-        }
163
-
164
-        return $data;
165
-    }
166
-
167
-    //--------------------------------------------------------------------
168
-
169
-
170
-    /**
171
-     * A callback method intended to hook into the after_insert and after_udpate
172
-     * methods.
173
-     *
174
-     * NOTE: Will only work for insert and update methods.
175
-     *
176
-     * @param array $data
177
-     * @return mixed
178
-     */
179
-    public function updateMeta($data)
180
-    {
181
-        // If no 'id' is in the $data array, then
182
-        // we don't have successful insert, get out of here
183
-        if (empty($data['id']) || ($data['method'] != 'insert' && $data['method'] != 'update'))
184
-        {
185
-            return $data;
186
-        }
187
-
188
-        // Collect any meta fields
189
-        foreach ($data['fields'] as $key => $value)
190
-        {
191
-            if (in_array($key, $this->meta_fields))
192
-            {
193
-                $this->db->where('user_id', $data['id']);
194
-                $this->db->where('meta_key', $key);
195
-                $query = $this->db->get('user_meta');
196
-
197
-                $obj = [
198
-                    'user_id'    => $data['id'],
199
-                    'meta_key'   => $key,
200
-                    'meta_value' => $value
201
-                ];
202
-
203
-                if ($query->num_rows() == 0)
204
-                {
205
-                    $this->db->insert('user_meta', $obj);
206
-                }
207
-                else if ($query->num_rows() > 0)
208
-                {
209
-                    $this->db->where('user_id', $data['id'])
210
-                             ->where('meta_key', $key)
211
-                             ->set('meta_value', $value)
212
-                             ->update('user_meta', $obj);
213
-                }
214
-            }
215
-        }
216
-
217
-        return $data;
218
-    }
219
-
220
-    //--------------------------------------------------------------------
221
-
222
-    /**
223
-     * Adds a single piece of meta information to a user.
224
-     *
225
-     * @param $user_id
226
-     * @param $key
227
-     * @param null $value
228
-     *
229
-     * @return object
230
-     */
231
-    public function saveMetaToUser($user_id, $key, $value=null)
232
-    {
233
-        if (! Events::trigger('beforeAddMetaToUser', [$user_id, $key]))
234
-        {
235
-            return false;
236
-        }
237
-
238
-        $user_id = (int)$user_id;
239
-
240
-        // Does this key already exist?
241
-        $test = $this->db->where([ 'user_id' => $user_id, 'meta_key' => $key ])->get('user_meta');
242
-
243
-        // Doesn't exist, so insert it.
244
-        if (! $test->num_rows())
245
-        {
246
-            $data = [
247
-                'user_id'       => $user_id,
248
-                'meta_key'      => $key,
249
-                'meta_value'    => $value
250
-            ];
251
-
252
-            return $this->db->insert('user_meta', $data);
253
-        }
254
-
255
-        // Otherwise, we need to update the existing.
256
-        return $this->db->where('user_id', $user_id)
257
-                        ->where('meta_key', $key)
258
-                        ->set('meta_value', $value)
259
-                        ->update('user_meta');
260
-    }
7
+	protected $table_name = 'users';
8
+
9
+	protected $soft_deletes = true;
10
+
11
+	protected $set_created = true;
12
+
13
+	protected $set_modified = false;
14
+
15
+	protected $protected_attributes = ['id', 'submit'];
16
+
17
+	protected $validation_rules = [
18
+		[
19
+			'field' => 'first_name',
20
+			'label' => 'lang:auth.first_name',
21
+			'rules' => 'trim|alpha|max_length[255]'
22
+		],
23
+		[
24
+			'field' => 'last_name',
25
+			'label' => 'lang:auth.last_name',
26
+			'rules' => 'trim|alpha|max_length[255]'
27
+		],
28
+		[
29
+			'field' => 'email',
30
+			'label' => 'lang:auth.email',
31
+			'rules' => 'trim|valid_email|max_length[255]'
32
+		],
33
+		[
34
+			'field' => 'username',
35
+			'label' => 'lang:auth.username',
36
+			'rules' => 'trim|alpha_numeric|max_length[255]'
37
+		],
38
+		[
39
+			'field' => 'password',
40
+			'label' => 'lang:auth.password',
41
+			'rules' => 'trim|max_length[255]|isStrongPassword'
42
+		],
43
+		[
44
+			'field' => 'pass_confirm',
45
+			'label' => 'lang:auth.pass_confirm',
46
+			'rules' => 'trim|matches[password]'
47
+		],
48
+	];
49
+
50
+	protected $insert_validate_rules = [
51
+		'email'        => 'required|is_unique[users.email]',
52
+		'username'     => 'required|is_unique[users.username]',
53
+		'password'     => 'required',
54
+		'pass_confirm' => 'required'
55
+	];
56
+
57
+	protected $before_insert = ['hashPassword'];
58
+	protected $before_update = ['hashPassword'];
59
+	protected $after_insert  = ['updateMeta'];
60
+	protected $after_update  = ['updateMeta'];
61
+
62
+	// The columns in the 'users_meta' table - for auto updating of profile information.
63
+	protected $meta_fields = ['first_name', 'last_name'];
64
+
65
+	protected $fields = ['id', 'email', 'username', 'password_hash', 'reset_hash', 'activate_hash', 'created_on', 'status', 'status_message', 'active', 'deleted', 'force_pass_reset'];
66
+
67
+	//--------------------------------------------------------------------
68
+
69
+	public function __construct()
70
+	{
71
+		parent::__construct();
72
+
73
+		$this->load->helper('auth/password');
74
+	}
75
+
76
+	//--------------------------------------------------------------------
77
+
78
+	/**
79
+	 * Works with any find queries to return user_meta information.
80
+	 *
81
+	 * @return $this
82
+	 */
83
+	public function withMeta()
84
+	{
85
+		$this->after_find[] = 'grabMeta';
86
+
87
+		return $this;
88
+	}
89
+
90
+	//--------------------------------------------------------------------
91
+
92
+	/**
93
+	 * If exists, will take our password out of the data array, and
94
+	 * create a new hash for it, which is inserted back into the
95
+	 * data array to be saved to the database.
96
+	 *
97
+	 * @param array $data
98
+	 *
99
+	 * @return array
100
+	 */
101
+	protected function hashPassword($data)
102
+	{
103
+		if (isset($data['fields']))
104
+		{
105
+			$data = $data['fields'];
106
+		}
107
+
108
+		if (isset($data['password']))
109
+		{
110
+			$data['password_hash'] = \Myth\Auth\Password::hashPassword($data['password']);
111
+
112
+			unset($data['password'], $data['pass_confirm']);
113
+		}
114
+
115
+		return $data;
116
+	}
117
+
118
+	//--------------------------------------------------------------------
119
+
120
+	/**
121
+	 * A callback designed to work with Digest Authentication to create
122
+	 * and store the $A1 value since we'll never have access to the
123
+	 * password except during inserts or updates.
124
+	 *
125
+	 * This assumes that this is working as part of an API and that
126
+	 * the api config file is already loaded into memory.
127
+	 *
128
+	 * @param $data
129
+	 *
130
+	 * @return $data
131
+	 */
132
+	public function createDigestKey($data)
133
+	{
134
+		$field = config_item('api.auth_field');
135
+		$value = null;
136
+
137
+		// If it's an update, we probably won't have the username/email
138
+		// so grab it so that we can use it.
139
+		if (! empty($data[ $this->primary_key ]))
140
+		{
141
+			if (! isset($data[$field]) )
142
+			{
143
+				$value = $this->get_field( $data['id'], $field );
144
+			}
145
+		}
146
+		// However, if it's an insert, then we should have it, If we don't, leave.
147
+		else
148
+		{
149
+			if (empty($data[$field]))
150
+			{
151
+				return $data;
152
+			}
153
+
154
+			$value = $data[$field];
155
+		}
156
+
157
+		// Still here? then create the hash based on the current realm.
158
+		if (! empty($data['password']) )
159
+		{
160
+			$key = md5($value .':'. config_item('api.realm') .':'. $data['password']);
161
+			$data['api_key'] = $key;
162
+		}
163
+
164
+		return $data;
165
+	}
166
+
167
+	//--------------------------------------------------------------------
168
+
169
+
170
+	/**
171
+	 * A callback method intended to hook into the after_insert and after_udpate
172
+	 * methods.
173
+	 *
174
+	 * NOTE: Will only work for insert and update methods.
175
+	 *
176
+	 * @param array $data
177
+	 * @return mixed
178
+	 */
179
+	public function updateMeta($data)
180
+	{
181
+		// If no 'id' is in the $data array, then
182
+		// we don't have successful insert, get out of here
183
+		if (empty($data['id']) || ($data['method'] != 'insert' && $data['method'] != 'update'))
184
+		{
185
+			return $data;
186
+		}
187
+
188
+		// Collect any meta fields
189
+		foreach ($data['fields'] as $key => $value)
190
+		{
191
+			if (in_array($key, $this->meta_fields))
192
+			{
193
+				$this->db->where('user_id', $data['id']);
194
+				$this->db->where('meta_key', $key);
195
+				$query = $this->db->get('user_meta');
196
+
197
+				$obj = [
198
+					'user_id'    => $data['id'],
199
+					'meta_key'   => $key,
200
+					'meta_value' => $value
201
+				];
202
+
203
+				if ($query->num_rows() == 0)
204
+				{
205
+					$this->db->insert('user_meta', $obj);
206
+				}
207
+				else if ($query->num_rows() > 0)
208
+				{
209
+					$this->db->where('user_id', $data['id'])
210
+							 ->where('meta_key', $key)
211
+							 ->set('meta_value', $value)
212
+							 ->update('user_meta', $obj);
213
+				}
214
+			}
215
+		}
216
+
217
+		return $data;
218
+	}
219
+
220
+	//--------------------------------------------------------------------
221
+
222
+	/**
223
+	 * Adds a single piece of meta information to a user.
224
+	 *
225
+	 * @param $user_id
226
+	 * @param $key
227
+	 * @param null $value
228
+	 *
229
+	 * @return object
230
+	 */
231
+	public function saveMetaToUser($user_id, $key, $value=null)
232
+	{
233
+		if (! Events::trigger('beforeAddMetaToUser', [$user_id, $key]))
234
+		{
235
+			return false;
236
+		}
237
+
238
+		$user_id = (int)$user_id;
239
+
240
+		// Does this key already exist?
241
+		$test = $this->db->where([ 'user_id' => $user_id, 'meta_key' => $key ])->get('user_meta');
242
+
243
+		// Doesn't exist, so insert it.
244
+		if (! $test->num_rows())
245
+		{
246
+			$data = [
247
+				'user_id'       => $user_id,
248
+				'meta_key'      => $key,
249
+				'meta_value'    => $value
250
+			];
251
+
252
+			return $this->db->insert('user_meta', $data);
253
+		}
254
+
255
+		// Otherwise, we need to update the existing.
256
+		return $this->db->where('user_id', $user_id)
257
+						->where('meta_key', $key)
258
+						->set('meta_value', $value)
259
+						->update('user_meta');
260
+	}
261 261
     
262
-    //--------------------------------------------------------------------
263
-
264
-    /**
265
-     * Gets the value of a single Meta item from a user.
266
-     *
267
-     * @param $user_id
268
-     * @param $key
269
-     *
270
-     * @return mixed
271
-     */
272
-    public function getMetaItem($user_id, $key)
273
-    {
274
-        $query = $this->db->where('user_id', (int)$user_id)
275
-                          ->where('meta_key', $key)
276
-                          ->select('meta_value')
277
-                          ->get('user_meta');
278
-
279
-        if (! $query->num_rows())
280
-        {
281
-            return null;
282
-        }
283
-
284
-        return $query->row()->meta_value;
285
-    }
286
-
287
-    //--------------------------------------------------------------------
288
-
289
-    /**
290
-     * Deletes one or more meta values from a user.
291
-     *
292
-     * @param $user_id
293
-     * @param $key
294
-     *
295
-     * @return bool
296
-     */
297
-    public function removeMetaFromUser($user_id, $key)
298
-    {
299
-        if (! Events::trigger('beforeRemoveMetaFromUser', [$user_id, $key]))
300
-        {
301
-            return false;
302
-        }
303
-
304
-	    if (is_array($key))
305
-	    {
306
-		    $this->db->where_in('meta_key', $key);
307
-	    }
308
-	    else
309
-	    {
310
-		    $this->db->where('meta_key', $key);
311
-	    }
312
-
313
-        $this->db->where('user_id', (int)$user_id)
314
-                 ->delete('user_meta');
315
-    }
316
-
317
-    //--------------------------------------------------------------------
318
-
319
-    public function getMetaForUser($user_id)
320
-    {
321
-        $query = $this->db->where('user_id', (int)$user_id)
322
-                          ->select('meta_key, meta_value')
323
-                          ->get('user_meta');
324
-
325
-        $rows = $query->result();
326
-
327
-        $meta = [];
328
-
329
-        if (count($rows))
330
-        {
331
-            array_walk( $rows, function ( $row ) use ( &$meta )
332
-            {
333
-                $meta[ $row->meta_key ] = $row->meta_value;
334
-            } );
335
-        }
336
-
337
-        return $meta;
338
-    }
339
-
340
-    //--------------------------------------------------------------------
341
-
342
-    protected function grabMeta($data)
343
-    {
344
-        if (strpos($data['method'], 'find') === false)
345
-        {
346
-            return $data;
347
-        }
348
-
349
-        $meta = $this->getMetaForUser($data['fields']->id);
350
-
351
-        if (is_object($data['fields']))
352
-        {
353
-            $data['fields']->meta = (object)$meta;
354
-        }
355
-        else
356
-        {
357
-            $data['fields']['meta']= $meta;
358
-        }
359
-
360
-        return $data;
361
-    }
362
-
363
-    //--------------------------------------------------------------------
262
+	//--------------------------------------------------------------------
263
+
264
+	/**
265
+	 * Gets the value of a single Meta item from a user.
266
+	 *
267
+	 * @param $user_id
268
+	 * @param $key
269
+	 *
270
+	 * @return mixed
271
+	 */
272
+	public function getMetaItem($user_id, $key)
273
+	{
274
+		$query = $this->db->where('user_id', (int)$user_id)
275
+						  ->where('meta_key', $key)
276
+						  ->select('meta_value')
277
+						  ->get('user_meta');
278
+
279
+		if (! $query->num_rows())
280
+		{
281
+			return null;
282
+		}
283
+
284
+		return $query->row()->meta_value;
285
+	}
286
+
287
+	//--------------------------------------------------------------------
288
+
289
+	/**
290
+	 * Deletes one or more meta values from a user.
291
+	 *
292
+	 * @param $user_id
293
+	 * @param $key
294
+	 *
295
+	 * @return bool
296
+	 */
297
+	public function removeMetaFromUser($user_id, $key)
298
+	{
299
+		if (! Events::trigger('beforeRemoveMetaFromUser', [$user_id, $key]))
300
+		{
301
+			return false;
302
+		}
303
+
304
+		if (is_array($key))
305
+		{
306
+			$this->db->where_in('meta_key', $key);
307
+		}
308
+		else
309
+		{
310
+			$this->db->where('meta_key', $key);
311
+		}
312
+
313
+		$this->db->where('user_id', (int)$user_id)
314
+				 ->delete('user_meta');
315
+	}
316
+
317
+	//--------------------------------------------------------------------
318
+
319
+	public function getMetaForUser($user_id)
320
+	{
321
+		$query = $this->db->where('user_id', (int)$user_id)
322
+						  ->select('meta_key, meta_value')
323
+						  ->get('user_meta');
324
+
325
+		$rows = $query->result();
326
+
327
+		$meta = [];
328
+
329
+		if (count($rows))
330
+		{
331
+			array_walk( $rows, function ( $row ) use ( &$meta )
332
+			{
333
+				$meta[ $row->meta_key ] = $row->meta_value;
334
+			} );
335
+		}
336
+
337
+		return $meta;
338
+	}
339
+
340
+	//--------------------------------------------------------------------
341
+
342
+	protected function grabMeta($data)
343
+	{
344
+		if (strpos($data['method'], 'find') === false)
345
+		{
346
+			return $data;
347
+		}
348
+
349
+		$meta = $this->getMetaForUser($data['fields']->id);
350
+
351
+		if (is_object($data['fields']))
352
+		{
353
+			$data['fields']->meta = (object)$meta;
354
+		}
355
+		else
356
+		{
357
+			$data['fields']['meta']= $meta;
358
+		}
359
+
360
+		return $data;
361
+	}
362
+
363
+	//--------------------------------------------------------------------
364 364
 
365 365
 
366 366
 }
Please login to merge, or discard this patch.
application/third_party/HMVC/Loader.php 2 patches
Doc Comments   +6 added lines, -6 removed lines patch added patch discarded remove patch
@@ -107,7 +107,7 @@  discard block
 block discarded – undo
107 107
      * @param	string	the name of the class
108 108
      * @param	mixed	the optional parameters
109 109
      * @param	string	an optional object name
110
-     * @return	void
110
+     * @return	null|HMVC_Loader
111 111
      */
112 112
     public function library($library = '', $params = NULL, $object_name = NULL) {
113 113
         if (is_array($library)) {
@@ -147,7 +147,7 @@  discard block
 block discarded – undo
147 147
      * @param	string	the name of the class
148 148
      * @param	string	name for the model
149 149
      * @param	bool	database connection
150
-     * @return	void
150
+     * @return	null|HMVC_Loader
151 151
      */
152 152
     public function model($model, $name = '', $db_conn = FALSE) {
153 153
         if (is_array($model)) {
@@ -232,7 +232,7 @@  discard block
 block discarded – undo
232 232
      * @param	string
233 233
      * @param	bool
234 234
      * @param 	bool
235
-     * @return	void
235
+     * @return	boolean|null
236 236
      */
237 237
     public function config($file = '', $use_sections = FALSE, $fail_gracefully = FALSE) {
238 238
         // Detect module
@@ -263,7 +263,7 @@  discard block
 block discarded – undo
263 263
      * This function loads the specified helper file.
264 264
      *
265 265
      * @param	mixed
266
-     * @return	void
266
+     * @return	null|HMVC_Loader
267 267
      */
268 268
     public function helper($helper = array()) {
269 269
         if (is_array($helper)) {
@@ -300,7 +300,7 @@  discard block
 block discarded – undo
300 300
      *
301 301
      * @param	array
302 302
      * @param	string
303
-     * @return	void
303
+     * @return	null|HMVC_Loader
304 304
      */
305 305
     public function language($file = array(), $lang = '') {
306 306
         if (is_array($file)) {
@@ -517,7 +517,7 @@  discard block
 block discarded – undo
517 517
      * Searches a given module name. Returns the path if found, FALSE otherwise
518 518
      *
519 519
      * @param string $module
520
-     * @return string|boolean
520
+     * @return string|false
521 521
      */
522 522
     private function find_module($module) {
523 523
         $config = & $this->_ci_get_component('config');
Please login to merge, or discard this patch.
Indentation   +499 added lines, -499 removed lines patch added patch discarded remove patch
@@ -30,506 +30,506 @@
 block discarded – undo
30 30
  */
31 31
 
32 32
 if (!defined("BASEPATH"))
33
-    exit("No direct script access allowed");
33
+	exit("No direct script access allowed");
34 34
 
35 35
 class HMVC_Loader extends CI_Loader {
36 36
 
37
-    /**
38
-     * List of loaded modules
39
-     *
40
-     * @var array
41
-     * @access protected
42
-     */
43
-    protected $_ci_modules = array();
44
-
45
-    /**
46
-     * List of loaded controllers
47
-     *
48
-     * @var array
49
-     * @access protected
50
-     */
51
-    protected $_ci_controllers = array();
52
-
53
-    /**
54
-     * Constructor
55
-     *
56
-     * Add the current module to all paths permanently
57
-     */
58
-    public function __construct() {
59
-        parent::__construct();
60
-
61
-        // Get current module from the router
62
-        $router = & $this->_ci_get_component('router');
63
-        if ($router->module) {
64
-            $this->add_module($router->module);
65
-        }
66
-    }
67
-
68
-    /**
69
-     * Controller Loader
70
-     *
71
-     * This function lets users load and hierarchical controllers to enable HMVC support
72
-     *
73
-     * @param	string	the uri to the controller
74
-     * @param	array	parameters for the requested method
75
-     * @param	boolean return the result instead of showing it
76
-     * @return	void
77
-     */
78
-    public function controller($uri, $params = array(), $return = FALSE) {
79
-        // No valid module detected, add current module to uri
80
-        list($module) = $this->detect_module($uri);
81
-        if (!isset($module)) {
82
-            $router = & $this->_ci_get_component('router');
83
-            if ($router->module) {
84
-                $module = $router->module;
85
-                $uri = $module . '/' . $uri;
86
-            }
87
-        }
88
-
89
-        // Add module
90
-        $this->add_module($module);
91
-
92
-        // Execute the controller method and capture output
93
-        $void = $this->_load_controller($uri, $params, $return);
94
-
95
-        // Remove module
96
-        $this->remove_module();
97
-
98
-        return $void;
99
-    }
100
-
101
-    /**
102
-     * Class Loader
103
-     *
104
-     * This function lets users load and instantiate classes.
105
-     * It is designed to be called from a user's app controllers.
106
-     *
107
-     * @param	string	the name of the class
108
-     * @param	mixed	the optional parameters
109
-     * @param	string	an optional object name
110
-     * @return	void
111
-     */
112
-    public function library($library = '', $params = NULL, $object_name = NULL) {
113
-        if (is_array($library)) {
114
-            foreach ($library as $class) {
115
-                $this->library($class, $params);
116
-            }
117
-            return;
118
-        }
119
-
120
-        // Detect module
121
-        if (list($module, $class) = $this->detect_module($library)) {
122
-            // Module already loaded
123
-            if (in_array($module, $this->_ci_modules)) {
124
-                return parent::library($class, $params, $object_name);
125
-            }
126
-
127
-            // Add module
128
-            $this->add_module($module);
129
-
130
-            // Let parent do the heavy work
131
-            $void = parent::library($class, $params, $object_name);
132
-
133
-            // Remove module
134
-            $this->remove_module();
135
-
136
-            return $void;
137
-        } else {
138
-            return parent::library($library, $params, $object_name);
139
-        }
140
-    }
141
-
142
-    /**
143
-     * Model Loader
144
-     *
145
-     * This function lets users load and instantiate models.
146
-     *
147
-     * @param	string	the name of the class
148
-     * @param	string	name for the model
149
-     * @param	bool	database connection
150
-     * @return	void
151
-     */
152
-    public function model($model, $name = '', $db_conn = FALSE) {
153
-        if (is_array($model)) {
154
-            foreach ($model as $babe) {
155
-                $this->model($babe);
156
-            }
157
-            return;
158
-        }
159
-
160
-        // Detect module
161
-        if (list($module, $class) = $this->detect_module($model)) {
162
-            // Module already loaded
163
-            if (in_array($module, $this->_ci_modules)) {
164
-                return parent::model($class, $name, $db_conn);
165
-            }
166
-
167
-            // Add module
168
-            $this->add_module($module);
169
-
170
-            // Let parent do the heavy work
171
-            $void = parent::model($class, $name, $db_conn);
172
-
173
-            // Remove module
174
-            $this->remove_module();
175
-
176
-            return $void;
177
-        } else {
178
-            return parent::model($model, $name, $db_conn);
179
-        }
180
-    }
181
-
182
-    /**
183
-     * Load View
184
-     *
185
-     * This function is used to load a "view" file.  It has three parameters:
186
-     *
187
-     * 1. The name of the "view" file to be included.
188
-     * 2. An associative array of data to be extracted for use in the view.
189
-     * 3. TRUE/FALSE - whether to return the data or load it.  In
190
-     * some cases it's advantageous to be able to return data so that
191
-     * a developer can process it in some way.
192
-     *
193
-     * @param	string
194
-     * @param	array
195
-     * @param	bool
196
-     * @return	void
197
-     */
198
-    public function view($view, $vars = array(), $return = FALSE)
199
-    {
200
-        // Allow application/views/* to override any module
201
-        // views for easier app customization.
202
-        if (file_exists(APPPATH .'views/'. $view .'.php'))
203
-        {
204
-            return parent::view($view, $vars, $return);
205
-        }
206
-
207
-        // Detect module
208
-        if (list($module, $class) = $this->detect_module($view)) {
209
-            // Module already loaded
210
-            if (in_array($module, $this->_ci_modules)) {
211
-                return parent::view($class, $vars, $return);
212
-            }
213
-
214
-            // Add module
215
-            $this->add_module($module);
216
-
217
-            // Let parent do the heavy work
218
-            $void = parent::view($class, $vars, $return);
219
-
220
-            // Remove module
221
-            $this->remove_module();
222
-
223
-            return $void;
224
-        } else {
225
-            return parent::view($view, $vars, $return);
226
-        }
227
-    }
228
-
229
-    /**
230
-     * Loads a config file
231
-     *
232
-     * @param	string
233
-     * @param	bool
234
-     * @param 	bool
235
-     * @return	void
236
-     */
237
-    public function config($file = '', $use_sections = FALSE, $fail_gracefully = FALSE) {
238
-        // Detect module
239
-        if (list($module, $class) = $this->detect_module($file)) {
240
-            // Module already loaded
241
-            if (in_array($module, $this->_ci_modules)) {
242
-                return parent::config($class, $use_sections, $fail_gracefully);
243
-            }
244
-
245
-            // Add module
246
-            $this->add_module($module);
247
-
248
-            // Let parent do the heavy work
249
-            $void = parent::config($class, $use_sections, $fail_gracefully);
250
-
251
-            // Remove module
252
-            $this->remove_module();
253
-
254
-            return $void;
255
-        } else {
256
-            parent::config($file, $use_sections, $fail_gracefully);
257
-        }
258
-    }
259
-
260
-    /**
261
-     * Load Helper
262
-     *
263
-     * This function loads the specified helper file.
264
-     *
265
-     * @param	mixed
266
-     * @return	void
267
-     */
268
-    public function helper($helper = array()) {
269
-        if (is_array($helper)) {
270
-            foreach ($helper as $help) {
271
-                $this->helper($help);
272
-            }
273
-            return;
274
-        }
275
-
276
-        // Detect module
277
-        if (list($module, $class) = $this->detect_module($helper)) {
278
-            // Module already loaded
279
-            if (in_array($module, $this->_ci_modules)) {
280
-                return parent::helper($class);
281
-            }
282
-
283
-            // Add module
284
-            $this->add_module($module);
285
-
286
-            // Let parent do the heavy work
287
-            $void = parent::helper($class);
288
-
289
-            // Remove module
290
-            $this->remove_module();
291
-
292
-            return $void;
293
-        } else {
294
-            return parent::helper($helper);
295
-        }
296
-    }
297
-
298
-    /**
299
-     * Loads a language file
300
-     *
301
-     * @param	array
302
-     * @param	string
303
-     * @return	void
304
-     */
305
-    public function language($file = array(), $lang = '') {
306
-        if (is_array($file)) {
307
-            foreach ($file as $langfile) {
308
-                $this->language($langfile, $lang);
309
-            }
310
-            return;
311
-        }
312
-
313
-        // Detect module
314
-        if (list($module, $class) = $this->detect_module($file)) {
315
-            // Module already loaded
316
-            if (in_array($module, $this->_ci_modules)) {
317
-                return parent::language($class, $lang);
318
-            }
319
-
320
-            // Add module
321
-            $this->add_module($module);
322
-
323
-            // Let parent do the heavy work
324
-            $void = parent::language($class, $lang);
325
-
326
-            // Remove module
327
-            $this->remove_module();
328
-
329
-            return $void;
330
-        } else {
331
-            return parent::language($file, $lang);
332
-        }
333
-    }
334
-
335
-    /**
336
-     * Load Widget
337
-     *
338
-     * This function provides support to Jens Segers Template Library for loading
339
-     * widget controllers within modules (place in module/widgets folder).
340
-     * @author  hArpanet - 23-Jun-2014
341
-     *
342
-     * @param   string $widget  Must contain Module name if widget within a module
343
-     *                          (eg. test/nav  where module name is 'test')
344
-     * @return  array|false
345
-     */
346
-    public function widget($widget) {
347
-
348
-        // Detect module
349
-        if (list($module, $widget) = $this->detect_module($widget)) {
350
-            // Module already loaded
351
-            if (in_array($module, $this->_ci_modules)) {
352
-                return array($module, $widget);
353
-            }
354
-
355
-            // Add module
356
-            $this->add_module($module);
357
-
358
-            // Look again now we've added new module path
359
-            $void = $this->widget($module.'/'.$widget);
360
-
361
-            // Remove module if widget not found within it
362
-            if (!$void) {
363
-                $this->remove_module();
364
-            }
365
-
366
-            return $void;
367
-
368
-        } else {
369
-            // widget not found in module
370
-            return FALSE;
371
-        }
372
-    }
373
-
374
-    /**
375
-     * Add Module
376
-     *
377
-     * Allow resources to be loaded from this module path
378
-     *
379
-     * @param	string
380
-     * @param 	boolean
381
-     */
382
-    public function add_module($module, $view_cascade = TRUE) {
383
-        if ($path = $this->find_module($module)) {
384
-            // Mark module as loaded
385
-            array_unshift($this->_ci_modules, $module);
386
-
387
-            // Add package path
388
-            parent::add_package_path($path, $view_cascade);
389
-        }
390
-    }
391
-
392
-    /**
393
-     * Remove Module
394
-     *
395
-     * Remove a module from the allowed module paths
396
-     *
397
-     * @param	type
398
-     * @param 	bool
399
-     */
400
-    public function remove_module($module = '', $remove_config = TRUE) {
401
-        if ($module == '') {
402
-            // Mark module as not loaded
403
-            array_shift($this->_ci_modules);
404
-
405
-            // Remove package path
406
-            parent::remove_package_path('', $remove_config);
407
-        } else if (($key = array_search($module, $this->_ci_modules)) !== FALSE) {
408
-            if ($path = $this->find_module($module)) {
409
-                // Mark module as not loaded
410
-                unset($this->_ci_modules[$key]);
411
-
412
-                // Remove package path
413
-                parent::remove_package_path($path, $remove_config);
414
-            }
415
-        }
416
-    }
417
-
418
-    /**
419
-     * Controller loader
420
-     *
421
-     * This function is used to load and instantiate controllers
422
-     *
423
-     * @param	string
424
-     * @param	array
425
-     * @param	boolean
426
-     * @return	object
427
-     */
428
-    private function _load_controller($uri = '', $params = array(), $return = FALSE) {
429
-        $router = & $this->_ci_get_component('router');
430
-
431
-        // Back up current router values (before loading new controller)
432
-        $backup = array();
433
-        foreach (array('directory', 'class', 'method', 'module') as $prop) {
434
-            $backup[$prop] = $router->{$prop};
435
-        }
436
-
437
-        // Locate the controller
438
-        $segments = $router->locate(explode('/', $uri));
439
-        $class = isset($segments[0]) ? $segments[0] : FALSE;
440
-        $method = isset($segments[1]) ? $segments[1] : "index";
441
-
442
-        // Controller not found
443
-        if (!$class) {
444
-            return;
445
-        }
446
-
447
-        if (!array_key_exists(strtolower($class), $this->_ci_controllers)) {
448
-            // Determine filepath
449
-            $filepath = APPPATH . 'controllers/' . $router->fetch_directory() . $class . '.php';
450
-
451
-            // Load the controller file
452
-            if (file_exists($filepath)) {
453
-                include_once ($filepath);
454
-            }
455
-
456
-            // Controller class not found, show 404
457
-            if (!class_exists($class)) {
458
-                show_404("{$class}/{$method}");
459
-            }
460
-
461
-            // Create a controller object
462
-            $this->_ci_controllers[strtolower($class)] = new $class();
463
-        }
464
-
465
-        $controller = $this->_ci_controllers[strtolower($class)];
466
-
467
-        // Method does not exists
468
-        if (!method_exists($controller, $method)) {
469
-            show_404("{$class}/{$method}");
470
-        }
471
-
472
-        // Restore router state
473
-        foreach ($backup as $prop => $value) {
474
-            $router->{$prop} = $value;
475
-        }
476
-
477
-        // Capture output and return
478
-        ob_start();
479
-        $result = call_user_func_array(array($controller, $method), $params);
480
-
481
-        // Return the buffered output
482
-        if ($return === TRUE) {
483
-            $buffer = ob_get_contents();
484
-            @ob_end_clean();
485
-            return $buffer;
486
-        }
487
-
488
-        // Close buffer and flush output to screen
489
-        ob_end_flush();
490
-
491
-        // Return controller return value
492
-        return $result;
493
-    }
494
-
495
-    /**
496
-     * Detects the module from a string. Returns the module name and class if found.
497
-     *
498
-     * @param	string
499
-     * @return	array|boolean
500
-     */
501
-    private function detect_module($class) {
502
-        $class = str_replace('.php', '', trim($class, '/'));
503
-        if (($first_slash = strpos($class, '/')) !== FALSE) {
504
-            $module = substr($class, 0, $first_slash);
505
-            $class = substr($class, $first_slash + 1);
506
-
507
-            // Check if module exists
508
-            if ($this->find_module($module)) {
509
-                return array($module, $class);
510
-            }
511
-        }
512
-
513
-        return FALSE;
514
-    }
515
-
516
-    /**
517
-     * Searches a given module name. Returns the path if found, FALSE otherwise
518
-     *
519
-     * @param string $module
520
-     * @return string|boolean
521
-     */
522
-    private function find_module($module) {
523
-        $config = & $this->_ci_get_component('config');
524
-
525
-        // Check all locations for this module
526
-        foreach ($config->item('modules_locations') as $location) {
527
-            $path = $location . rtrim($module, '/') . '/';
528
-            if (is_dir($path)) {
529
-                return $path;
530
-            }
531
-        }
532
-
533
-        return FALSE;
534
-    }
37
+	/**
38
+	 * List of loaded modules
39
+	 *
40
+	 * @var array
41
+	 * @access protected
42
+	 */
43
+	protected $_ci_modules = array();
44
+
45
+	/**
46
+	 * List of loaded controllers
47
+	 *
48
+	 * @var array
49
+	 * @access protected
50
+	 */
51
+	protected $_ci_controllers = array();
52
+
53
+	/**
54
+	 * Constructor
55
+	 *
56
+	 * Add the current module to all paths permanently
57
+	 */
58
+	public function __construct() {
59
+		parent::__construct();
60
+
61
+		// Get current module from the router
62
+		$router = & $this->_ci_get_component('router');
63
+		if ($router->module) {
64
+			$this->add_module($router->module);
65
+		}
66
+	}
67
+
68
+	/**
69
+	 * Controller Loader
70
+	 *
71
+	 * This function lets users load and hierarchical controllers to enable HMVC support
72
+	 *
73
+	 * @param	string	the uri to the controller
74
+	 * @param	array	parameters for the requested method
75
+	 * @param	boolean return the result instead of showing it
76
+	 * @return	void
77
+	 */
78
+	public function controller($uri, $params = array(), $return = FALSE) {
79
+		// No valid module detected, add current module to uri
80
+		list($module) = $this->detect_module($uri);
81
+		if (!isset($module)) {
82
+			$router = & $this->_ci_get_component('router');
83
+			if ($router->module) {
84
+				$module = $router->module;
85
+				$uri = $module . '/' . $uri;
86
+			}
87
+		}
88
+
89
+		// Add module
90
+		$this->add_module($module);
91
+
92
+		// Execute the controller method and capture output
93
+		$void = $this->_load_controller($uri, $params, $return);
94
+
95
+		// Remove module
96
+		$this->remove_module();
97
+
98
+		return $void;
99
+	}
100
+
101
+	/**
102
+	 * Class Loader
103
+	 *
104
+	 * This function lets users load and instantiate classes.
105
+	 * It is designed to be called from a user's app controllers.
106
+	 *
107
+	 * @param	string	the name of the class
108
+	 * @param	mixed	the optional parameters
109
+	 * @param	string	an optional object name
110
+	 * @return	void
111
+	 */
112
+	public function library($library = '', $params = NULL, $object_name = NULL) {
113
+		if (is_array($library)) {
114
+			foreach ($library as $class) {
115
+				$this->library($class, $params);
116
+			}
117
+			return;
118
+		}
119
+
120
+		// Detect module
121
+		if (list($module, $class) = $this->detect_module($library)) {
122
+			// Module already loaded
123
+			if (in_array($module, $this->_ci_modules)) {
124
+				return parent::library($class, $params, $object_name);
125
+			}
126
+
127
+			// Add module
128
+			$this->add_module($module);
129
+
130
+			// Let parent do the heavy work
131
+			$void = parent::library($class, $params, $object_name);
132
+
133
+			// Remove module
134
+			$this->remove_module();
135
+
136
+			return $void;
137
+		} else {
138
+			return parent::library($library, $params, $object_name);
139
+		}
140
+	}
141
+
142
+	/**
143
+	 * Model Loader
144
+	 *
145
+	 * This function lets users load and instantiate models.
146
+	 *
147
+	 * @param	string	the name of the class
148
+	 * @param	string	name for the model
149
+	 * @param	bool	database connection
150
+	 * @return	void
151
+	 */
152
+	public function model($model, $name = '', $db_conn = FALSE) {
153
+		if (is_array($model)) {
154
+			foreach ($model as $babe) {
155
+				$this->model($babe);
156
+			}
157
+			return;
158
+		}
159
+
160
+		// Detect module
161
+		if (list($module, $class) = $this->detect_module($model)) {
162
+			// Module already loaded
163
+			if (in_array($module, $this->_ci_modules)) {
164
+				return parent::model($class, $name, $db_conn);
165
+			}
166
+
167
+			// Add module
168
+			$this->add_module($module);
169
+
170
+			// Let parent do the heavy work
171
+			$void = parent::model($class, $name, $db_conn);
172
+
173
+			// Remove module
174
+			$this->remove_module();
175
+
176
+			return $void;
177
+		} else {
178
+			return parent::model($model, $name, $db_conn);
179
+		}
180
+	}
181
+
182
+	/**
183
+	 * Load View
184
+	 *
185
+	 * This function is used to load a "view" file.  It has three parameters:
186
+	 *
187
+	 * 1. The name of the "view" file to be included.
188
+	 * 2. An associative array of data to be extracted for use in the view.
189
+	 * 3. TRUE/FALSE - whether to return the data or load it.  In
190
+	 * some cases it's advantageous to be able to return data so that
191
+	 * a developer can process it in some way.
192
+	 *
193
+	 * @param	string
194
+	 * @param	array
195
+	 * @param	bool
196
+	 * @return	void
197
+	 */
198
+	public function view($view, $vars = array(), $return = FALSE)
199
+	{
200
+		// Allow application/views/* to override any module
201
+		// views for easier app customization.
202
+		if (file_exists(APPPATH .'views/'. $view .'.php'))
203
+		{
204
+			return parent::view($view, $vars, $return);
205
+		}
206
+
207
+		// Detect module
208
+		if (list($module, $class) = $this->detect_module($view)) {
209
+			// Module already loaded
210
+			if (in_array($module, $this->_ci_modules)) {
211
+				return parent::view($class, $vars, $return);
212
+			}
213
+
214
+			// Add module
215
+			$this->add_module($module);
216
+
217
+			// Let parent do the heavy work
218
+			$void = parent::view($class, $vars, $return);
219
+
220
+			// Remove module
221
+			$this->remove_module();
222
+
223
+			return $void;
224
+		} else {
225
+			return parent::view($view, $vars, $return);
226
+		}
227
+	}
228
+
229
+	/**
230
+	 * Loads a config file
231
+	 *
232
+	 * @param	string
233
+	 * @param	bool
234
+	 * @param 	bool
235
+	 * @return	void
236
+	 */
237
+	public function config($file = '', $use_sections = FALSE, $fail_gracefully = FALSE) {
238
+		// Detect module
239
+		if (list($module, $class) = $this->detect_module($file)) {
240
+			// Module already loaded
241
+			if (in_array($module, $this->_ci_modules)) {
242
+				return parent::config($class, $use_sections, $fail_gracefully);
243
+			}
244
+
245
+			// Add module
246
+			$this->add_module($module);
247
+
248
+			// Let parent do the heavy work
249
+			$void = parent::config($class, $use_sections, $fail_gracefully);
250
+
251
+			// Remove module
252
+			$this->remove_module();
253
+
254
+			return $void;
255
+		} else {
256
+			parent::config($file, $use_sections, $fail_gracefully);
257
+		}
258
+	}
259
+
260
+	/**
261
+	 * Load Helper
262
+	 *
263
+	 * This function loads the specified helper file.
264
+	 *
265
+	 * @param	mixed
266
+	 * @return	void
267
+	 */
268
+	public function helper($helper = array()) {
269
+		if (is_array($helper)) {
270
+			foreach ($helper as $help) {
271
+				$this->helper($help);
272
+			}
273
+			return;
274
+		}
275
+
276
+		// Detect module
277
+		if (list($module, $class) = $this->detect_module($helper)) {
278
+			// Module already loaded
279
+			if (in_array($module, $this->_ci_modules)) {
280
+				return parent::helper($class);
281
+			}
282
+
283
+			// Add module
284
+			$this->add_module($module);
285
+
286
+			// Let parent do the heavy work
287
+			$void = parent::helper($class);
288
+
289
+			// Remove module
290
+			$this->remove_module();
291
+
292
+			return $void;
293
+		} else {
294
+			return parent::helper($helper);
295
+		}
296
+	}
297
+
298
+	/**
299
+	 * Loads a language file
300
+	 *
301
+	 * @param	array
302
+	 * @param	string
303
+	 * @return	void
304
+	 */
305
+	public function language($file = array(), $lang = '') {
306
+		if (is_array($file)) {
307
+			foreach ($file as $langfile) {
308
+				$this->language($langfile, $lang);
309
+			}
310
+			return;
311
+		}
312
+
313
+		// Detect module
314
+		if (list($module, $class) = $this->detect_module($file)) {
315
+			// Module already loaded
316
+			if (in_array($module, $this->_ci_modules)) {
317
+				return parent::language($class, $lang);
318
+			}
319
+
320
+			// Add module
321
+			$this->add_module($module);
322
+
323
+			// Let parent do the heavy work
324
+			$void = parent::language($class, $lang);
325
+
326
+			// Remove module
327
+			$this->remove_module();
328
+
329
+			return $void;
330
+		} else {
331
+			return parent::language($file, $lang);
332
+		}
333
+	}
334
+
335
+	/**
336
+	 * Load Widget
337
+	 *
338
+	 * This function provides support to Jens Segers Template Library for loading
339
+	 * widget controllers within modules (place in module/widgets folder).
340
+	 * @author  hArpanet - 23-Jun-2014
341
+	 *
342
+	 * @param   string $widget  Must contain Module name if widget within a module
343
+	 *                          (eg. test/nav  where module name is 'test')
344
+	 * @return  array|false
345
+	 */
346
+	public function widget($widget) {
347
+
348
+		// Detect module
349
+		if (list($module, $widget) = $this->detect_module($widget)) {
350
+			// Module already loaded
351
+			if (in_array($module, $this->_ci_modules)) {
352
+				return array($module, $widget);
353
+			}
354
+
355
+			// Add module
356
+			$this->add_module($module);
357
+
358
+			// Look again now we've added new module path
359
+			$void = $this->widget($module.'/'.$widget);
360
+
361
+			// Remove module if widget not found within it
362
+			if (!$void) {
363
+				$this->remove_module();
364
+			}
365
+
366
+			return $void;
367
+
368
+		} else {
369
+			// widget not found in module
370
+			return FALSE;
371
+		}
372
+	}
373
+
374
+	/**
375
+	 * Add Module
376
+	 *
377
+	 * Allow resources to be loaded from this module path
378
+	 *
379
+	 * @param	string
380
+	 * @param 	boolean
381
+	 */
382
+	public function add_module($module, $view_cascade = TRUE) {
383
+		if ($path = $this->find_module($module)) {
384
+			// Mark module as loaded
385
+			array_unshift($this->_ci_modules, $module);
386
+
387
+			// Add package path
388
+			parent::add_package_path($path, $view_cascade);
389
+		}
390
+	}
391
+
392
+	/**
393
+	 * Remove Module
394
+	 *
395
+	 * Remove a module from the allowed module paths
396
+	 *
397
+	 * @param	type
398
+	 * @param 	bool
399
+	 */
400
+	public function remove_module($module = '', $remove_config = TRUE) {
401
+		if ($module == '') {
402
+			// Mark module as not loaded
403
+			array_shift($this->_ci_modules);
404
+
405
+			// Remove package path
406
+			parent::remove_package_path('', $remove_config);
407
+		} else if (($key = array_search($module, $this->_ci_modules)) !== FALSE) {
408
+			if ($path = $this->find_module($module)) {
409
+				// Mark module as not loaded
410
+				unset($this->_ci_modules[$key]);
411
+
412
+				// Remove package path
413
+				parent::remove_package_path($path, $remove_config);
414
+			}
415
+		}
416
+	}
417
+
418
+	/**
419
+	 * Controller loader
420
+	 *
421
+	 * This function is used to load and instantiate controllers
422
+	 *
423
+	 * @param	string
424
+	 * @param	array
425
+	 * @param	boolean
426
+	 * @return	object
427
+	 */
428
+	private function _load_controller($uri = '', $params = array(), $return = FALSE) {
429
+		$router = & $this->_ci_get_component('router');
430
+
431
+		// Back up current router values (before loading new controller)
432
+		$backup = array();
433
+		foreach (array('directory', 'class', 'method', 'module') as $prop) {
434
+			$backup[$prop] = $router->{$prop};
435
+		}
436
+
437
+		// Locate the controller
438
+		$segments = $router->locate(explode('/', $uri));
439
+		$class = isset($segments[0]) ? $segments[0] : FALSE;
440
+		$method = isset($segments[1]) ? $segments[1] : "index";
441
+
442
+		// Controller not found
443
+		if (!$class) {
444
+			return;
445
+		}
446
+
447
+		if (!array_key_exists(strtolower($class), $this->_ci_controllers)) {
448
+			// Determine filepath
449
+			$filepath = APPPATH . 'controllers/' . $router->fetch_directory() . $class . '.php';
450
+
451
+			// Load the controller file
452
+			if (file_exists($filepath)) {
453
+				include_once ($filepath);
454
+			}
455
+
456
+			// Controller class not found, show 404
457
+			if (!class_exists($class)) {
458
+				show_404("{$class}/{$method}");
459
+			}
460
+
461
+			// Create a controller object
462
+			$this->_ci_controllers[strtolower($class)] = new $class();
463
+		}
464
+
465
+		$controller = $this->_ci_controllers[strtolower($class)];
466
+
467
+		// Method does not exists
468
+		if (!method_exists($controller, $method)) {
469
+			show_404("{$class}/{$method}");
470
+		}
471
+
472
+		// Restore router state
473
+		foreach ($backup as $prop => $value) {
474
+			$router->{$prop} = $value;
475
+		}
476
+
477
+		// Capture output and return
478
+		ob_start();
479
+		$result = call_user_func_array(array($controller, $method), $params);
480
+
481
+		// Return the buffered output
482
+		if ($return === TRUE) {
483
+			$buffer = ob_get_contents();
484
+			@ob_end_clean();
485
+			return $buffer;
486
+		}
487
+
488
+		// Close buffer and flush output to screen
489
+		ob_end_flush();
490
+
491
+		// Return controller return value
492
+		return $result;
493
+	}
494
+
495
+	/**
496
+	 * Detects the module from a string. Returns the module name and class if found.
497
+	 *
498
+	 * @param	string
499
+	 * @return	array|boolean
500
+	 */
501
+	private function detect_module($class) {
502
+		$class = str_replace('.php', '', trim($class, '/'));
503
+		if (($first_slash = strpos($class, '/')) !== FALSE) {
504
+			$module = substr($class, 0, $first_slash);
505
+			$class = substr($class, $first_slash + 1);
506
+
507
+			// Check if module exists
508
+			if ($this->find_module($module)) {
509
+				return array($module, $class);
510
+			}
511
+		}
512
+
513
+		return FALSE;
514
+	}
515
+
516
+	/**
517
+	 * Searches a given module name. Returns the path if found, FALSE otherwise
518
+	 *
519
+	 * @param string $module
520
+	 * @return string|boolean
521
+	 */
522
+	private function find_module($module) {
523
+		$config = & $this->_ci_get_component('config');
524
+
525
+		// Check all locations for this module
526
+		foreach ($config->item('modules_locations') as $location) {
527
+			$path = $location . rtrim($module, '/') . '/';
528
+			if (is_dir($path)) {
529
+				return $path;
530
+			}
531
+		}
532
+
533
+		return FALSE;
534
+	}
535 535
 }
Please login to merge, or discard this patch.
build/lib/BaseBuilder.php 2 patches
Doc Comments   +8 added lines, -4 removed lines patch added patch discarded remove patch
@@ -21,8 +21,8 @@  discard block
 block discarded – undo
21 21
 	 * Copies the entire contents of a single folder
22 22
 	 * into a source folder, recursively.
23 23
 	 *
24
-	 * @param $source
25
-	 * @param $destination
24
+	 * @param string $source
25
+	 * @param string $destination
26 26
 	 */
27 27
 	public function copyFolder($source, $destination)
28 28
 	{
@@ -71,8 +71,8 @@  discard block
 block discarded – undo
71 71
 	 * Deletes all files and folders, recursively, within $path,
72 72
 	 * except for any files listed in the $leave_files array.
73 73
 	 *
74
-	 * @param $path
75
-	 * @param array $leave_files
74
+	 * @param string $path
75
+	 * @param string[] $leave_files
76 76
 	 */
77 77
 	public function cleanFolder($path, $leave_files=[])
78 78
 	{
@@ -97,6 +97,10 @@  discard block
 block discarded – undo
97 97
 
98 98
 	//--------------------------------------------------------------------
99 99
 
100
+	/**
101
+	 * @param string $source
102
+	 * @param string $destination
103
+	 */
100 104
 	public function compressFolder($source, $destination, $include_dir=false)
101 105
 	{
102 106
 		if (! extension_loaded('zip') ) {
Please login to merge, or discard this patch.
Indentation   +5 added lines, -5 removed lines patch added patch discarded remove patch
@@ -12,7 +12,7 @@  discard block
 block discarded – undo
12 12
 
13 13
 	public function __construct($ci=null)
14 14
 	{
15
-	    $this->ci =& $ci;
15
+		$this->ci =& $ci;
16 16
 	}
17 17
 
18 18
 	//--------------------------------------------------------------------
@@ -57,10 +57,10 @@  discard block
 block discarded – undo
57 57
 	 */
58 58
 	public function ensureFolder($path)
59 59
 	{
60
-	    if (is_dir($path))
61
-	    {
62
-		    return true;
63
-	    }
60
+		if (is_dir($path))
61
+		{
62
+			return true;
63
+		}
64 64
 
65 65
 		return mkdir($path, 0777, true);
66 66
 	}
Please login to merge, or discard this patch.
myth/_generators/Api/ApiGenerator.php 2 patches
Doc Comments   +17 added lines, -10 removed lines patch added patch discarded remove patch
@@ -103,7 +103,7 @@  discard block
 block discarded – undo
103 103
 	 * Quickly creates boilerplate code for a new API resource.
104 104
 	 *
105 105
 	 * @param $segments
106
-	 * @param $quiet
106
+	 * @param boolean $quiet
107 107
 	 */
108 108
 	private function scaffold( $segments, $quiet )
109 109
 	{
@@ -220,6 +220,9 @@  discard block
 block discarded – undo
220 220
 
221 221
 	//--------------------------------------------------------------------
222 222
 
223
+	/**
224
+	 * @param string $name
225
+	 */
223 226
 	private function detectModel($name)
224 227
 	{
225 228
 		$model_name = ucfirst($name) .'_model.php';
@@ -242,8 +245,8 @@  discard block
 block discarded – undo
242 245
     /**
243 246
      * Creates the new APIController - based resource with basic CRUD.
244 247
      *
245
-     * @param $single
246
-     * @param $plural
248
+     * @param string $single
249
+     * @param string $plural
247 250
      * @param $version
248 251
      *
249 252
      * @return $this
@@ -271,8 +274,8 @@  discard block
 block discarded – undo
271 274
     /**
272 275
      * Creates the language file to accompany the controller.
273 276
      *
274
-     * @param $single
275
-     * @param $plural
277
+     * @param string $single
278
+     * @param string $plural
276 279
      * @param $version
277 280
      *
278 281
      * @return $this
@@ -297,8 +300,8 @@  discard block
 block discarded – undo
297 300
      * Creates the API Blueprint file for that resource in
298 301
      * APPPATH/docs/api
299 302
      *
300
-     * @param $single
301
-     * @param $plural
303
+     * @param string $single
304
+     * @param string $plural
302 305
      * @param $version
303 306
      * @param $model
304 307
      *
@@ -349,7 +352,7 @@  discard block
 block discarded – undo
349 352
      * Modifies the _toc.ini file (or creates) for the specified Blueprint docs.
350 353
      *
351 354
      * @param $plural
352
-     * @param $version
355
+     * @param string $version
353 356
      *
354 357
      * @return $this|bool
355 358
      */
@@ -385,7 +388,7 @@  discard block
 block discarded – undo
385 388
      * Creates a generic representation of the object from the database
386 389
      * table.
387 390
      *
388
-     * @param $model
391
+     * @param string $model
389 392
      *
390 393
      * @return string
391 394
      */
@@ -537,6 +540,10 @@  discard block
 block discarded – undo
537 540
 	//--------------------------------------------------------------------
538 541
 
539 542
 
543
+	/**
544
+	 * @param string $tpl
545
+	 * @param string $name
546
+	 */
540 547
 	private function makeMigration( $tpl, $name )
541 548
 	{
542 549
 		// Create the migration
@@ -561,7 +568,7 @@  discard block
 block discarded – undo
561 568
 	/**
562 569
 	 * Modifies the config/api.php file and sets the Auth type
563 570
 	 *
564
-	 * @param $type
571
+	 * @param string $type
565 572
 	 */
566 573
 	private function setAuthType( $type )
567 574
 	{
Please login to merge, or discard this patch.
Indentation   +160 added lines, -160 removed lines patch added patch discarded remove patch
@@ -239,17 +239,17 @@  discard block
 block discarded – undo
239 239
 
240 240
 	//--------------------------------------------------------------------
241 241
 
242
-    /**
243
-     * Creates the new APIController - based resource with basic CRUD.
244
-     *
245
-     * @param $single
246
-     * @param $plural
247
-     * @param $version
248
-     *
249
-     * @return $this
250
-     * @internal param $name
251
-     *
252
-     */
242
+	/**
243
+	 * Creates the new APIController - based resource with basic CRUD.
244
+	 *
245
+	 * @param $single
246
+	 * @param $plural
247
+	 * @param $version
248
+	 *
249
+	 * @return $this
250
+	 * @internal param $name
251
+	 *
252
+	 */
253 253
 	private function createController( $single, $plural, $version )
254 254
 	{
255 255
 		$data = [
@@ -268,44 +268,44 @@  discard block
 block discarded – undo
268 268
 
269 269
 	//--------------------------------------------------------------------
270 270
 
271
-    /**
272
-     * Creates the language file to accompany the controller.
273
-     *
274
-     * @param $single
275
-     * @param $plural
276
-     * @param $version
277
-     *
278
-     * @return $this
279
-     */
271
+	/**
272
+	 * Creates the language file to accompany the controller.
273
+	 *
274
+	 * @param $single
275
+	 * @param $plural
276
+	 * @param $version
277
+	 *
278
+	 * @return $this
279
+	 */
280 280
 	private function createLanguage( $single, $plural, $version )
281 281
 	{
282
-        $data = [
283
-            'plural'            => $plural,
284
-            'single'            => $single,
285
-            'uc_single'         => ucfirst($single),
286
-            'uc_plural'         => ucfirst($plural),
287
-        ];
282
+		$data = [
283
+			'plural'            => $plural,
284
+			'single'            => $single,
285
+			'uc_single'         => ucfirst($single),
286
+			'uc_plural'         => ucfirst($plural),
287
+		];
288 288
 
289
-        $destination = APPPATH ."language/english/api_{$plural}_lang.php";
289
+		$destination = APPPATH ."language/english/api_{$plural}_lang.php";
290 290
 
291
-        return $this->copyTemplate( 'lang', $destination, $data, $this->overwrite );
291
+		return $this->copyTemplate( 'lang', $destination, $data, $this->overwrite );
292 292
 	}
293 293
 
294 294
 	//--------------------------------------------------------------------
295 295
 
296
-    /**
297
-     * Creates the API Blueprint file for that resource in
298
-     * APPPATH/docs/api
299
-     *
300
-     * @param $single
301
-     * @param $plural
302
-     * @param $version
303
-     * @param $model
304
-     *
305
-     * @return $this
306
-     * @internal param $name
307
-     *
308
-     */
296
+	/**
297
+	 * Creates the API Blueprint file for that resource in
298
+	 * APPPATH/docs/api
299
+	 *
300
+	 * @param $single
301
+	 * @param $plural
302
+	 * @param $version
303
+	 * @param $model
304
+	 *
305
+	 * @return $this
306
+	 * @internal param $name
307
+	 *
308
+	 */
309 309
 	private function createBlueprint( $single, $plural, $version, $model )
310 310
 	{
311 311
 		$version = rtrim($version, '/');
@@ -315,10 +315,10 @@  discard block
 block discarded – undo
315 315
 		}
316 316
 
317 317
 		// Load the model so we can use the correct table to use
318
-        $model = strtolower( str_replace('.php', '', $model) );
318
+		$model = strtolower( str_replace('.php', '', $model) );
319 319
 		$this->load->model( $model, $model, true );
320 320
 
321
-        $obj = $this->formatObject($model);
321
+		$obj = $this->formatObject($model);
322 322
 
323 323
 
324 324
 		$data = [
@@ -328,139 +328,139 @@  discard block
 block discarded – undo
328 328
 			'uc_plural'         => ucfirst($plural),
329 329
 			'version'           => $version,
330 330
 			'site_url'          => site_url(),
331
-            'formatted'         => $obj
331
+			'formatted'         => $obj
332 332
 		];
333 333
 
334 334
 		$destination = APPPATH .'docs/api/'. $version . $plural .'.md';
335 335
 
336 336
 		$success = $this->copyTemplate( 'blueprint', $destination, $data, $this->overwrite );
337 337
 
338
-        if (! $this->updateTOC($plural, $version))
339
-        {
340
-            CLI::write("\tUnable to modify the toc file.", 'light_red');
341
-        }
338
+		if (! $this->updateTOC($plural, $version))
339
+		{
340
+			CLI::write("\tUnable to modify the toc file.", 'light_red');
341
+		}
342
+
343
+		return $success;
344
+	}
345
+
346
+	//--------------------------------------------------------------------
347
+
348
+	/**
349
+	 * Modifies the _toc.ini file (or creates) for the specified Blueprint docs.
350
+	 *
351
+	 * @param $plural
352
+	 * @param $version
353
+	 *
354
+	 * @return $this|bool
355
+	 */
356
+	private function updateTOC( $plural, $version )
357
+	{
358
+		$path = APPPATH .'docs/_toc.ini';
359
+
360
+		// We need a TOC file to exist if we're going to modify it silly.
361
+		if (! file_exists($path))
362
+		{
363
+			if (! $this->copyTemplate('toc', $path))
364
+			{
365
+				return false;
366
+			}
367
+		}
368
+
369
+		$version = rtrim($version, '/ ');
370
+		if (! empty($version))
371
+		{
372
+			$version .= '/';
373
+		}
374
+
375
+		$ucname = ucfirst($plural);
376
+
377
+		$content = "api/{$version}{$plural}\t= {$ucname}\n";
378
+
379
+		return $this->injectIntoFile($path, $content);
380
+	}
381
+
382
+	//--------------------------------------------------------------------
383
+
384
+	/**
385
+	 * Creates a generic representation of the object from the database
386
+	 * table.
387
+	 *
388
+	 * @param $model
389
+	 *
390
+	 * @return string
391
+	 */
392
+	private function formatObject( $model )
393
+	{
394
+		$fields = $this->db->field_data($this->$model->table_name);
395
+
396
+		$obj = '';
397
+
398
+		foreach ($fields as $field)
399
+		{
400
+			$obj .= "\"$field->name\":  ";
342 401
 
343
-        return $success;
402
+			switch ($field->type)
403
+			{
404
+				case 'tinyint':
405
+					$obj .= "0,\n";
406
+					break;
407
+				case 'int':
408
+				case 'bigint':
409
+					$obj .= "1234,\n";
410
+					break;
411
+				case 'float':
412
+				case 'double':
413
+					$obj .= "123.45,\n";
414
+					break;
415
+				case 'date':
416
+					$obj .= date("\"Y-m-d\",\n");
417
+					break;
418
+				case 'datetime':
419
+					$obj .= date("\"Y-m-d H:i:s\",\n");
420
+					break;
421
+			}
422
+
423
+			if ($field->name == 'email')
424
+			{
425
+				$obj .= "\"[email protected]\",\n";
426
+			}
427
+			else if (strpos('name', $field->name) !== false)
428
+			{
429
+				$obj .= "\"Lefty\",\n";
430
+			}
431
+			else if (in_array($field->type, ['char', 'varchar', 'text']))
432
+			{
433
+				$obj .= "\"Some default string\",\n";
434
+			}
435
+		}
436
+
437
+		return $obj;
344 438
 	}
345 439
 
346 440
 	//--------------------------------------------------------------------
347 441
 
348
-    /**
349
-     * Modifies the _toc.ini file (or creates) for the specified Blueprint docs.
350
-     *
351
-     * @param $plural
352
-     * @param $version
353
-     *
354
-     * @return $this|bool
355
-     */
356
-    private function updateTOC( $plural, $version )
357
-    {
358
-        $path = APPPATH .'docs/_toc.ini';
359
-
360
-        // We need a TOC file to exist if we're going to modify it silly.
361
-        if (! file_exists($path))
362
-        {
363
-            if (! $this->copyTemplate('toc', $path))
364
-            {
365
-                return false;
366
-            }
367
-        }
368
-
369
-        $version = rtrim($version, '/ ');
370
-        if (! empty($version))
371
-        {
372
-            $version .= '/';
373
-        }
374
-
375
-        $ucname = ucfirst($plural);
376
-
377
-        $content = "api/{$version}{$plural}\t= {$ucname}\n";
378
-
379
-        return $this->injectIntoFile($path, $content);
380
-    }
381
-
382
-    //--------------------------------------------------------------------
383
-
384
-    /**
385
-     * Creates a generic representation of the object from the database
386
-     * table.
387
-     *
388
-     * @param $model
389
-     *
390
-     * @return string
391
-     */
392
-    private function formatObject( $model )
393
-    {
394
-        $fields = $this->db->field_data($this->$model->table_name);
395
-
396
-        $obj = '';
397
-
398
-        foreach ($fields as $field)
399
-        {
400
-            $obj .= "\"$field->name\":  ";
401
-
402
-            switch ($field->type)
403
-            {
404
-                case 'tinyint':
405
-                    $obj .= "0,\n";
406
-                    break;
407
-                case 'int':
408
-                case 'bigint':
409
-                    $obj .= "1234,\n";
410
-                    break;
411
-                case 'float':
412
-                case 'double':
413
-                    $obj .= "123.45,\n";
414
-                    break;
415
-                case 'date':
416
-                    $obj .= date("\"Y-m-d\",\n");
417
-                    break;
418
-                case 'datetime':
419
-                    $obj .= date("\"Y-m-d H:i:s\",\n");
420
-                    break;
421
-            }
422
-
423
-            if ($field->name == 'email')
424
-            {
425
-                $obj .= "\"[email protected]\",\n";
426
-            }
427
-            else if (strpos('name', $field->name) !== false)
428
-            {
429
-                $obj .= "\"Lefty\",\n";
430
-            }
431
-            else if (in_array($field->type, ['char', 'varchar', 'text']))
432
-            {
433
-                $obj .= "\"Some default string\",\n";
434
-            }
435
-        }
436
-
437
-        return $obj;
438
-    }
439
-
440
-    //--------------------------------------------------------------------
441
-
442
-    /**
443
-     * Modifies the routes file to include a line for the API endpoints
444
-     * for this resource.
445
-     *
446
-     * @param string $plural
447
-     * @param string $version
448
-     *
449
-     * @return $this
450
-     */
442
+	/**
443
+	 * Modifies the routes file to include a line for the API endpoints
444
+	 * for this resource.
445
+	 *
446
+	 * @param string $plural
447
+	 * @param string $version
448
+	 *
449
+	 * @return $this
450
+	 */
451 451
 	private function addRoutes( $plural, $version )
452 452
 	{
453
-        $path = APPPATH .'config/routes.php';
453
+		$path = APPPATH .'config/routes.php';
454 454
 
455
-        $version = rtrim($version, ', ');
456
-        if (! empty($version))
457
-        {
458
-            $version .='/';
459
-        }
455
+		$version = rtrim($version, ', ');
456
+		if (! empty($version))
457
+		{
458
+			$version .='/';
459
+		}
460 460
 
461
-        $content = "\$routes->resources('{$version}{$plural}');\n";
461
+		$content = "\$routes->resources('{$version}{$plural}');\n";
462 462
 
463
-        return $this->injectIntoFile($path, $content, ['after' => "// Auto-generated routes go here\n"]);
463
+		return $this->injectIntoFile($path, $content, ['after' => "// Auto-generated routes go here\n"]);
464 464
 	}
465 465
 
466 466
 	//--------------------------------------------------------------------
Please login to merge, or discard this patch.
myth/_generators/Controller/ControllerGenerator.php 2 patches
Doc Comments   +7 added lines patch added patch discarded remove patch
@@ -97,6 +97,9 @@  discard block
 block discarded – undo
97 97
 
98 98
 	//--------------------------------------------------------------------
99 99
 
100
+	/**
101
+	 * @param string $name
102
+	 */
100 103
 	protected function quietSetOptions( $name )
101 104
 	{
102 105
 		$options = CLI::getOptions();
@@ -120,6 +123,9 @@  discard block
 block discarded – undo
120 123
 
121 124
 	//--------------------------------------------------------------------
122 125
 
126
+	/**
127
+	 * @param string $name
128
+	 */
123 129
 	protected function collectOptions( $name )
124 130
 	{
125 131
 		$options = CLI::getOptions();
@@ -159,6 +165,7 @@  discard block
 block discarded – undo
159 165
 
160 166
 	/**
161 167
 	 * Generates the standard views for our CRUD methods.
168
+	 * @param string $name
162 169
 	 */
163 170
 	protected function createViews( $name )
164 171
 	{
Please login to merge, or discard this patch.
Indentation   +37 added lines, -37 removed lines patch added patch discarded remove patch
@@ -203,20 +203,20 @@  discard block
 block discarded – undo
203 203
 
204 204
 		if ( empty( $fields ) )
205 205
 		{
206
-            // If we have a model, we can get our fields from there
207
-            if (! empty($this->options['model']))
208
-            {
209
-                $fields = $this->getFieldsFromModel( $this->options['model'] );
210
-
211
-                if (empty($fields))
212
-                {
213
-                    return NULL;
214
-                }
215
-            }
216
-            else
217
-            {
218
-                return NULL;
219
-            }
206
+			// If we have a model, we can get our fields from there
207
+			if (! empty($this->options['model']))
208
+			{
209
+				$fields = $this->getFieldsFromModel( $this->options['model'] );
210
+
211
+				if (empty($fields))
212
+				{
213
+					return NULL;
214
+				}
215
+			}
216
+			else
217
+			{
218
+				return NULL;
219
+			}
220 220
 		}
221 221
 
222 222
 		$fields = explode( ' ', $fields );
@@ -277,36 +277,36 @@  discard block
 block discarded – undo
277 277
 
278 278
 	//--------------------------------------------------------------------
279 279
 
280
-    private function getFieldsFromModel( $model )
281
-    {
282
-        $this->load->model($model);
280
+	private function getFieldsFromModel( $model )
281
+	{
282
+		$this->load->model($model);
283 283
 
284
-        if (! $this->db->table_exists( $this->$model->table() ))
285
-        {
286
-            return '';
287
-        }
284
+		if (! $this->db->table_exists( $this->$model->table() ))
285
+		{
286
+			return '';
287
+		}
288 288
 
289
-        $fields = $this->db->field_data( $this->$model->table() );
289
+		$fields = $this->db->field_data( $this->$model->table() );
290 290
 
291
-        $return = '';
291
+		$return = '';
292 292
 
293
-        // Prepare the fields in a string format like
294
-        // it would have been passed on the CLI
295
-        foreach ($fields as $field)
296
-        {
297
-            $temp = $field->name .':'. $field->type;
293
+		// Prepare the fields in a string format like
294
+		// it would have been passed on the CLI
295
+		foreach ($fields as $field)
296
+		{
297
+			$temp = $field->name .':'. $field->type;
298 298
 
299
-            if (! empty($field->max_length))
300
-            {
301
-                $temp .= ':'. $field->max_length;
302
-            }
299
+			if (! empty($field->max_length))
300
+			{
301
+				$temp .= ':'. $field->max_length;
302
+			}
303 303
 
304
-            $return .= ' '. $temp;
305
-        }
304
+			$return .= ' '. $temp;
305
+		}
306 306
 
307
-        return $return;
308
-    }
307
+		return $return;
308
+	}
309 309
 
310
-    //--------------------------------------------------------------------
310
+	//--------------------------------------------------------------------
311 311
 
312 312
 }
Please login to merge, or discard this patch.
myth/_generators/Migration/MigrationGenerator.php 2 patches
Doc Comments   +5 added lines, -2 removed lines patch added patch discarded remove patch
@@ -159,7 +159,7 @@  discard block
 block discarded – undo
159 159
 	 *  'create_user_table'         action = 'create', table = 'user'
160 160
 	 *  'add_name_to_user_table     action = 'add', table = 'user'
161 161
 	 *
162
-	 * @param $name
162
+	 * @param string $name
163 163
 	 */
164 164
 	public function detectAction($name)
165 165
 	{
@@ -199,6 +199,9 @@  discard block
 block discarded – undo
199 199
 
200 200
 	//--------------------------------------------------------------------
201 201
 
202
+	/**
203
+	 * @param string $name
204
+	 */
202 205
 	public function collectOptions($name, $quiet=false)
203 206
 	{
204 207
 		$options = CLI::getOptions();
@@ -308,7 +311,7 @@  discard block
 block discarded – undo
308 311
 	 * Reads the fields from an existing table and fills out our
309 312
 	 * $fields array and $primary key information.
310 313
 	 *
311
-	 * @param $table
314
+	 * @param string $table
312 315
 	 */
313 316
 	protected function readTable($table)
314 317
 	{
Please login to merge, or discard this patch.
Indentation   +34 added lines, -34 removed lines patch added patch discarded remove patch
@@ -1,34 +1,34 @@  discard block
 block discarded – undo
1 1
 <?php
2 2
 /**
3
- * Sprint
4
- *
5
- * A set of power tools to enhance the CodeIgniter framework and provide consistent workflow.
6
- *
7
- * Permission is hereby granted, free of charge, to any person obtaining a copy
8
- * of this software and associated documentation files (the "Software"), to deal
9
- * in the Software without restriction, including without limitation the rights
10
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
- * copies of the Software, and to permit persons to whom the Software is
12
- * furnished to do so, subject to the following conditions:
13
- *
14
- * The above copyright notice and this permission notice shall be included in
15
- * all copies or substantial portions of the Software.
16
- *
17
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
- * THE SOFTWARE.
24
- *
25
- * @package     Sprint
26
- * @author      Lonnie Ezell
27
- * @copyright   Copyright 2014-2015, New Myth Media, LLC (http://newmythmedia.com)
28
- * @license     http://opensource.org/licenses/MIT  (MIT)
29
- * @link        http://sprintphp.com
30
- * @since       Version 1.0
31
- */
3
+	 * Sprint
4
+	 *
5
+	 * A set of power tools to enhance the CodeIgniter framework and provide consistent workflow.
6
+	 *
7
+	 * Permission is hereby granted, free of charge, to any person obtaining a copy
8
+	 * of this software and associated documentation files (the "Software"), to deal
9
+	 * in the Software without restriction, including without limitation the rights
10
+	 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+	 * copies of the Software, and to permit persons to whom the Software is
12
+	 * furnished to do so, subject to the following conditions:
13
+	 *
14
+	 * The above copyright notice and this permission notice shall be included in
15
+	 * all copies or substantial portions of the Software.
16
+	 *
17
+	 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+	 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+	 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+	 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+	 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+	 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+	 * THE SOFTWARE.
24
+	 *
25
+	 * @package     Sprint
26
+	 * @author      Lonnie Ezell
27
+	 * @copyright   Copyright 2014-2015, New Myth Media, LLC (http://newmythmedia.com)
28
+	 * @license     http://opensource.org/licenses/MIT  (MIT)
29
+	 * @link        http://sprintphp.com
30
+	 * @since       Version 1.0
31
+	 */
32 32
 
33 33
 use Myth\CLI as CLI;
34 34
 
@@ -163,7 +163,7 @@  discard block
 block discarded – undo
163 163
 	 */
164 164
 	public function detectAction($name)
165 165
 	{
166
-	    $segments = explode('_', $name);
166
+		$segments = explode('_', $name);
167 167
 
168 168
 		$action = trim(strtolower(array_shift($segments)));
169 169
 
@@ -236,10 +236,10 @@  discard block
 block discarded – undo
236 236
 	 */
237 237
 	public function parseFields($str)
238 238
 	{
239
-        if (empty($str))
240
-        {
241
-	        return;
242
-        }
239
+		if (empty($str))
240
+		{
241
+			return;
242
+		}
243 243
 
244 244
 		$fields = [];
245 245
 		$segments = explode(' ', $str);
Please login to merge, or discard this patch.
myth/_generators/Model/ModelGenerator.php 2 patches
Doc Comments   +7 added lines patch added patch discarded remove patch
@@ -107,6 +107,10 @@  discard block
 block discarded – undo
107 107
 	/*
108 108
 	 * Customizes our settings
109 109
 	 */
110
+
111
+	/**
112
+	 * @param string $model_name
113
+	 */
110 114
 	protected function collectOptions( $model_name, $options=[] )
111 115
 	{
112 116
 		$this->load->helper( 'inflector' );
@@ -180,6 +184,9 @@  discard block
 block discarded – undo
180 184
 
181 185
 	//--------------------------------------------------------------------
182 186
 
187
+	/**
188
+	 * @param string $model_name
189
+	 */
183 190
 	protected function quietSetOptions( $model_name, $options=[] )
184 191
 	{
185 192
 		$this->load->helper( 'inflector' );
Please login to merge, or discard this patch.
Indentation   +92 added lines, -92 removed lines patch added patch discarded remove patch
@@ -60,7 +60,7 @@  discard block
 block discarded – undo
60 60
 	{
61 61
 		$name = array_shift( $segments );
62 62
 
63
-        $options = CLI::getOptions();
63
+		$options = CLI::getOptions();
64 64
 
65 65
 		$this->options['table_name'] = array_shift( $segments );
66 66
 
@@ -213,17 +213,17 @@  discard block
 block discarded – undo
213 213
 		// Check whether the table exists in this database
214 214
 		if ( ! $this->db->table_exists( $table_name ) )
215 215
 		{
216
-            if (empty($options['fields']))
217
-            {
218
-                return FALSE;
219
-            }
216
+			if (empty($options['fields']))
217
+			{
218
+				return FALSE;
219
+			}
220 220
 
221
-            $fields = $this->parseFieldString($options['fields']);
221
+			$fields = $this->parseFieldString($options['fields']);
222
+		}
223
+		else
224
+		{
225
+			$fields = $this->db->field_data( $table_name );
222 226
 		}
223
-        else
224
-        {
225
-            $fields = $this->db->field_data( $table_name );
226
-        }
227 227
 
228 228
 		// There may be something wrong or the database driver may not return
229 229
 		// field data
@@ -232,9 +232,9 @@  discard block
 block discarded – undo
232 232
 			return FALSE;
233 233
 		}
234 234
 
235
-        $this->options['set_created']       = false;
236
-        $this->options['set_modified']      = false;
237
-        $this->options['use_soft_deletes']  = false;
235
+		$this->options['set_created']       = false;
236
+		$this->options['set_modified']      = false;
237
+		$this->options['use_soft_deletes']  = false;
238 238
 
239 239
 		// Use the primary key if the table has one already set.
240 240
 		foreach ( $fields as $field )
@@ -244,20 +244,20 @@  discard block
 block discarded – undo
244 244
 				$this->options['primary_key'] = $field->name;
245 245
 			}
246 246
 
247
-            if ($field->name == $this->options['created_field'])
248
-            {
249
-                $this->options['set_created'] = true;
250
-            }
247
+			if ($field->name == $this->options['created_field'])
248
+			{
249
+				$this->options['set_created'] = true;
250
+			}
251 251
 
252
-            if ($field->name == $this->options['modified_field'])
253
-            {
254
-                $this->options['set_modified'] = true;
255
-            }
252
+			if ($field->name == $this->options['modified_field'])
253
+			{
254
+				$this->options['set_modified'] = true;
255
+			}
256 256
 
257
-            if ($field->name == $this->options['soft_delete_key'])
258
-            {
259
-                $this->options['use_soft_deletes'] = true;
260
-            }
257
+			if ($field->name == $this->options['soft_delete_key'])
258
+			{
259
+				$this->options['use_soft_deletes'] = true;
260
+			}
261 261
 		}
262 262
 
263 263
 		// Set our validation rules based on these fields
@@ -268,73 +268,73 @@  discard block
 block discarded – undo
268 268
 
269 269
 	//--------------------------------------------------------------------
270 270
 
271
-    /**
272
-     * Grabs the fields from the CLI options and gets them ready for
273
-     * use within the views.
274
-     */
275
-    protected function parseFieldString($fields)
276
-    {
277
-        if ( empty( $fields ) )
278
-        {
279
-            return NULL;
280
-        }
281
-
282
-        $fields = explode( ' ', $fields );
283
-
284
-        $new_fields = [ ];
285
-
286
-        foreach ( $fields as $field )
287
-        {
288
-            $pop = [ NULL, NULL, NULL ];
289
-            list( $field, $type, $size ) = array_merge( explode( ':', $field ), $pop );
290
-            $type = strtolower( $type );
291
-
292
-            // Strings
293
-            if (in_array($type, ['char', 'varchar', 'string']))
294
-            {
295
-                $new_fields[] = [
296
-                    'name'  => $field,
297
-                    'type'  => 'text'
298
-                ];
299
-            }
300
-
301
-            // Textarea
302
-            else if ($type == 'text')
303
-            {
304
-                $new_fields[] = [
305
-                    'name'  => $field,
306
-                    'type'  => 'textarea'
307
-                ];
308
-            }
309
-
310
-            // Number
311
-            else if (in_array($type, ['tinyint', 'int', 'bigint', 'mediumint', 'float', 'double', 'number']))
312
-            {
313
-                $new_fields[] = [
314
-                    'name'  => $field,
315
-                    'type'  => 'number'
316
-                ];
317
-            }
318
-
319
-            // Date
320
-            else if (in_array($type, ['date', 'datetime', 'time']))
321
-            {
322
-                $new_fields[] = [
323
-                    'name'  => $field,
324
-                    'type'  => $type
325
-                ];
326
-            }
327
-        }
328
-
329
-        // Convert to objects
330
-        array_walk($new_fields, function(&$item, $key) {
331
-            $item = (object)$item;
332
-        });
333
-
334
-        return $new_fields;
335
-    }
336
-
337
-    //--------------------------------------------------------------------
271
+	/**
272
+	 * Grabs the fields from the CLI options and gets them ready for
273
+	 * use within the views.
274
+	 */
275
+	protected function parseFieldString($fields)
276
+	{
277
+		if ( empty( $fields ) )
278
+		{
279
+			return NULL;
280
+		}
281
+
282
+		$fields = explode( ' ', $fields );
283
+
284
+		$new_fields = [ ];
285
+
286
+		foreach ( $fields as $field )
287
+		{
288
+			$pop = [ NULL, NULL, NULL ];
289
+			list( $field, $type, $size ) = array_merge( explode( ':', $field ), $pop );
290
+			$type = strtolower( $type );
291
+
292
+			// Strings
293
+			if (in_array($type, ['char', 'varchar', 'string']))
294
+			{
295
+				$new_fields[] = [
296
+					'name'  => $field,
297
+					'type'  => 'text'
298
+				];
299
+			}
300
+
301
+			// Textarea
302
+			else if ($type == 'text')
303
+			{
304
+				$new_fields[] = [
305
+					'name'  => $field,
306
+					'type'  => 'textarea'
307
+				];
308
+			}
309
+
310
+			// Number
311
+			else if (in_array($type, ['tinyint', 'int', 'bigint', 'mediumint', 'float', 'double', 'number']))
312
+			{
313
+				$new_fields[] = [
314
+					'name'  => $field,
315
+					'type'  => 'number'
316
+				];
317
+			}
318
+
319
+			// Date
320
+			else if (in_array($type, ['date', 'datetime', 'time']))
321
+			{
322
+				$new_fields[] = [
323
+					'name'  => $field,
324
+					'type'  => $type
325
+				];
326
+			}
327
+		}
328
+
329
+		// Convert to objects
330
+		array_walk($new_fields, function(&$item, $key) {
331
+			$item = (object)$item;
332
+		});
333
+
334
+		return $new_fields;
335
+	}
336
+
337
+	//--------------------------------------------------------------------
338 338
 
339 339
 
340 340
 	/**
Please login to merge, or discard this patch.