SiteOrigin Widgets Bundle - Version 1.7.2

Version Description

  • 09 November 2016 =
  • Made fixes to pass PHP 7 compatibility checks.
  • Image Widget: Get alt and title text from chosen image.
  • Replaced markup parser with more actively maintained one.
  • Simple Masonry: ensure resize on load
  • Image Grid: Allow 0 as valid spacing value.
  • Editor Widget: Call WP_Embed::run_shortcode on Editor widget content
  • Maps: Added missing typeof causing maps api not to load properly.
  • Icon: Fixed URL output.
  • Fixed double slash in URLs.
  • Features: Use Measurement fields.
  • Apply modify_form to form arrays created in the constructor.
  • Contact Form: add email default email subject if no subject defined.
Download this release

Release Info

Developer gpriday
Plugin Icon 128x128 SiteOrigin Widgets Bundle
Version 1.7.2
Comparing to
See all releases

Code changes from version 1.7.1 to 1.7.2

base/inc/Parsedown.php ADDED
@@ -0,0 +1,1548 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ #
4
+ #
5
+ # Parsedown
6
+ # http://parsedown.org
7
+ #
8
+ # (c) Emanuil Rusev
9
+ # http://erusev.com
10
+ #
11
+ # For the full license information, view the LICENSE file that was distributed
12
+ # with this source code.
13
+ #
14
+ #
15
+
16
+ class Parsedown
17
+ {
18
+ # ~
19
+
20
+ const version = '1.6.0';
21
+
22
+ # ~
23
+
24
+ function text($text)
25
+ {
26
+ # make sure no definitions are set
27
+ $this->DefinitionData = array();
28
+
29
+ # standardize line breaks
30
+ $text = str_replace(array("\r\n", "\r"), "\n", $text);
31
+
32
+ # remove surrounding line breaks
33
+ $text = trim($text, "\n");
34
+
35
+ # split text into lines
36
+ $lines = explode("\n", $text);
37
+
38
+ # iterate through lines to identify blocks
39
+ $markup = $this->lines($lines);
40
+
41
+ # trim line breaks
42
+ $markup = trim($markup, "\n");
43
+
44
+ return $markup;
45
+ }
46
+
47
+ #
48
+ # Setters
49
+ #
50
+
51
+ function setBreaksEnabled($breaksEnabled)
52
+ {
53
+ $this->breaksEnabled = $breaksEnabled;
54
+
55
+ return $this;
56
+ }
57
+
58
+ protected $breaksEnabled;
59
+
60
+ function setMarkupEscaped($markupEscaped)
61
+ {
62
+ $this->markupEscaped = $markupEscaped;
63
+
64
+ return $this;
65
+ }
66
+
67
+ protected $markupEscaped;
68
+
69
+ function setUrlsLinked($urlsLinked)
70
+ {
71
+ $this->urlsLinked = $urlsLinked;
72
+
73
+ return $this;
74
+ }
75
+
76
+ protected $urlsLinked = true;
77
+
78
+ #
79
+ # Lines
80
+ #
81
+
82
+ protected $BlockTypes = array(
83
+ '#' => array('Header'),
84
+ '*' => array('Rule', 'List'),
85
+ '+' => array('List'),
86
+ '-' => array('SetextHeader', 'Table', 'Rule', 'List'),
87
+ '0' => array('List'),
88
+ '1' => array('List'),
89
+ '2' => array('List'),
90
+ '3' => array('List'),
91
+ '4' => array('List'),
92
+ '5' => array('List'),
93
+ '6' => array('List'),
94
+ '7' => array('List'),
95
+ '8' => array('List'),
96
+ '9' => array('List'),
97
+ ':' => array('Table'),
98
+ '<' => array('Comment', 'Markup'),
99
+ '=' => array('SetextHeader'),
100
+ '>' => array('Quote'),
101
+ '[' => array('Reference'),
102
+ '_' => array('Rule'),
103
+ '`' => array('FencedCode'),
104
+ '|' => array('Table'),
105
+ '~' => array('FencedCode'),
106
+ );
107
+
108
+ # ~
109
+
110
+ protected $unmarkedBlockTypes = array(
111
+ 'Code',
112
+ );
113
+
114
+ #
115
+ # Blocks
116
+ #
117
+
118
+ protected function lines(array $lines)
119
+ {
120
+ $CurrentBlock = null;
121
+
122
+ foreach ($lines as $line)
123
+ {
124
+ if (chop($line) === '')
125
+ {
126
+ if (isset($CurrentBlock))
127
+ {
128
+ $CurrentBlock['interrupted'] = true;
129
+ }
130
+
131
+ continue;
132
+ }
133
+
134
+ if (strpos($line, "\t") !== false)
135
+ {
136
+ $parts = explode("\t", $line);
137
+
138
+ $line = $parts[0];
139
+
140
+ unset($parts[0]);
141
+
142
+ foreach ($parts as $part)
143
+ {
144
+ $shortage = 4 - mb_strlen($line, 'utf-8') % 4;
145
+
146
+ $line .= str_repeat(' ', $shortage);
147
+ $line .= $part;
148
+ }
149
+ }
150
+
151
+ $indent = 0;
152
+
153
+ while (isset($line[$indent]) and $line[$indent] === ' ')
154
+ {
155
+ $indent ++;
156
+ }
157
+
158
+ $text = $indent > 0 ? substr($line, $indent) : $line;
159
+
160
+ # ~
161
+
162
+ $Line = array('body' => $line, 'indent' => $indent, 'text' => $text);
163
+
164
+ # ~
165
+
166
+ if (isset($CurrentBlock['continuable']))
167
+ {
168
+ $Block = $this->{'block'.$CurrentBlock['type'].'Continue'}($Line, $CurrentBlock);
169
+
170
+ if (isset($Block))
171
+ {
172
+ $CurrentBlock = $Block;
173
+
174
+ continue;
175
+ }
176
+ else
177
+ {
178
+ if ($this->isBlockCompletable($CurrentBlock['type']))
179
+ {
180
+ $CurrentBlock = $this->{'block'.$CurrentBlock['type'].'Complete'}($CurrentBlock);
181
+ }
182
+ }
183
+ }
184
+
185
+ # ~
186
+
187
+ $marker = $text[0];
188
+
189
+ # ~
190
+
191
+ $blockTypes = $this->unmarkedBlockTypes;
192
+
193
+ if (isset($this->BlockTypes[$marker]))
194
+ {
195
+ foreach ($this->BlockTypes[$marker] as $blockType)
196
+ {
197
+ $blockTypes []= $blockType;
198
+ }
199
+ }
200
+
201
+ #
202
+ # ~
203
+
204
+ foreach ($blockTypes as $blockType)
205
+ {
206
+ $Block = $this->{'block'.$blockType}($Line, $CurrentBlock);
207
+
208
+ if (isset($Block))
209
+ {
210
+ $Block['type'] = $blockType;
211
+
212
+ if ( ! isset($Block['identified']))
213
+ {
214
+ $Blocks []= $CurrentBlock;
215
+
216
+ $Block['identified'] = true;
217
+ }
218
+
219
+ if ($this->isBlockContinuable($blockType))
220
+ {
221
+ $Block['continuable'] = true;
222
+ }
223
+
224
+ $CurrentBlock = $Block;
225
+
226
+ continue 2;
227
+ }
228
+ }
229
+
230
+ # ~
231
+
232
+ if (isset($CurrentBlock) and ! isset($CurrentBlock['type']) and ! isset($CurrentBlock['interrupted']))
233
+ {
234
+ $CurrentBlock['element']['text'] .= "\n".$text;
235
+ }
236
+ else
237
+ {
238
+ $Blocks []= $CurrentBlock;
239
+
240
+ $CurrentBlock = $this->paragraph($Line);
241
+
242
+ $CurrentBlock['identified'] = true;
243
+ }
244
+ }
245
+
246
+ # ~
247
+
248
+ if (isset($CurrentBlock['continuable']) and $this->isBlockCompletable($CurrentBlock['type']))
249
+ {
250
+ $CurrentBlock = $this->{'block'.$CurrentBlock['type'].'Complete'}($CurrentBlock);
251
+ }
252
+
253
+ # ~
254
+
255
+ $Blocks []= $CurrentBlock;
256
+
257
+ unset($Blocks[0]);
258
+
259
+ # ~
260
+
261
+ $markup = '';
262
+
263
+ foreach ($Blocks as $Block)
264
+ {
265
+ if (isset($Block['hidden']))
266
+ {
267
+ continue;
268
+ }
269
+
270
+ $markup .= "\n";
271
+ $markup .= isset($Block['markup']) ? $Block['markup'] : $this->element($Block['element']);
272
+ }
273
+
274
+ $markup .= "\n";
275
+
276
+ # ~
277
+
278
+ return $markup;
279
+ }
280
+
281
+ protected function isBlockContinuable($Type)
282
+ {
283
+ return method_exists($this, 'block'.$Type.'Continue');
284
+ }
285
+
286
+ protected function isBlockCompletable($Type)
287
+ {
288
+ return method_exists($this, 'block'.$Type.'Complete');
289
+ }
290
+
291
+ #
292
+ # Code
293
+
294
+ protected function blockCode($Line, $Block = null)
295
+ {
296
+ if (isset($Block) and ! isset($Block['type']) and ! isset($Block['interrupted']))
297
+ {
298
+ return;
299
+ }
300
+
301
+ if ($Line['indent'] >= 4)
302
+ {
303
+ $text = substr($Line['body'], 4);
304
+
305
+ $Block = array(
306
+ 'element' => array(
307
+ 'name' => 'pre',
308
+ 'handler' => 'element',
309
+ 'text' => array(
310
+ 'name' => 'code',
311
+ 'text' => $text,
312
+ ),
313
+ ),
314
+ );
315
+
316
+ return $Block;
317
+ }
318
+ }
319
+
320
+ protected function blockCodeContinue($Line, $Block)
321
+ {
322
+ if ($Line['indent'] >= 4)
323
+ {
324
+ if (isset($Block['interrupted']))
325
+ {
326
+ $Block['element']['text']['text'] .= "\n";
327
+
328
+ unset($Block['interrupted']);
329
+ }
330
+
331
+ $Block['element']['text']['text'] .= "\n";
332
+
333
+ $text = substr($Line['body'], 4);
334
+
335
+ $Block['element']['text']['text'] .= $text;
336
+
337
+ return $Block;
338
+ }
339
+ }
340
+
341
+ protected function blockCodeComplete($Block)
342
+ {
343
+ $text = $Block['element']['text']['text'];
344
+
345
+ $text = htmlspecialchars($text, ENT_NOQUOTES, 'UTF-8');
346
+
347
+ $Block['element']['text']['text'] = $text;
348
+
349
+ return $Block;
350
+ }
351
+
352
+ #
353
+ # Comment
354
+
355
+ protected function blockComment($Line)
356
+ {
357
+ if ($this->markupEscaped)
358
+ {
359
+ return;
360
+ }
361
+
362
+ if (isset($Line['text'][3]) and $Line['text'][3] === '-' and $Line['text'][2] === '-' and $Line['text'][1] === '!')
363
+ {
364
+ $Block = array(
365
+ 'markup' => $Line['body'],
366
+ );
367
+
368
+ if (preg_match('/-->$/', $Line['text']))
369
+ {
370
+ $Block['closed'] = true;
371
+ }
372
+
373
+ return $Block;
374
+ }
375
+ }
376
+
377
+ protected function blockCommentContinue($Line, array $Block)
378
+ {
379
+ if (isset($Block['closed']))
380
+ {
381
+ return;
382
+ }
383
+
384
+ $Block['markup'] .= "\n" . $Line['body'];
385
+
386
+ if (preg_match('/-->$/', $Line['text']))
387
+ {
388
+ $Block['closed'] = true;
389
+ }
390
+
391
+ return $Block;
392
+ }
393
+
394
+ #
395
+ # Fenced Code
396
+
397
+ protected function blockFencedCode($Line)
398
+ {
399
+ if (preg_match('/^['.$Line['text'][0].']{3,}[ ]*([\w-]+)?[ ]*$/', $Line['text'], $matches))
400
+ {
401
+ $Element = array(
402
+ 'name' => 'code',
403
+ 'text' => '',
404
+ );
405
+
406
+ if (isset($matches[1]))
407
+ {
408
+ $class = 'language-'.$matches[1];
409
+
410
+ $Element['attributes'] = array(
411
+ 'class' => $class,
412
+ );
413
+ }
414
+
415
+ $Block = array(
416
+ 'char' => $Line['text'][0],
417
+ 'element' => array(
418
+ 'name' => 'pre',
419
+ 'handler' => 'element',
420
+ 'text' => $Element,
421
+ ),
422
+ );
423
+
424
+ return $Block;
425
+ }
426
+ }
427
+
428
+ protected function blockFencedCodeContinue($Line, $Block)
429
+ {
430
+ if (isset($Block['complete']))
431
+ {
432
+ return;
433
+ }
434
+
435
+ if (isset($Block['interrupted']))
436
+ {
437
+ $Block['element']['text']['text'] .= "\n";
438
+
439
+ unset($Block['interrupted']);
440
+ }
441
+
442
+ if (preg_match('/^'.$Block['char'].'{3,}[ ]*$/', $Line['text']))
443
+ {
444
+ $Block['element']['text']['text'] = substr($Block['element']['text']['text'], 1);
445
+
446
+ $Block['complete'] = true;
447
+
448
+ return $Block;
449
+ }
450
+
451
+ $Block['element']['text']['text'] .= "\n".$Line['body'];;
452
+
453
+ return $Block;
454
+ }
455
+
456
+ protected function blockFencedCodeComplete($Block)
457
+ {
458
+ $text = $Block['element']['text']['text'];
459
+
460
+ $text = htmlspecialchars($text, ENT_NOQUOTES, 'UTF-8');
461
+
462
+ $Block['element']['text']['text'] = $text;
463
+
464
+ return $Block;
465
+ }
466
+
467
+ #
468
+ # Header
469
+
470
+ protected function blockHeader($Line)
471
+ {
472
+ if (isset($Line['text'][1]))
473
+ {
474
+ $level = 1;
475
+
476
+ while (isset($Line['text'][$level]) and $Line['text'][$level] === '#')
477
+ {
478
+ $level ++;
479
+ }
480
+
481
+ if ($level > 6)
482
+ {
483
+ return;
484
+ }
485
+
486
+ $text = trim($Line['text'], '# ');
487
+
488
+ $Block = array(
489
+ 'element' => array(
490
+ 'name' => 'h' . min(6, $level),
491
+ 'text' => $text,
492
+ 'handler' => 'line',
493
+ ),
494
+ );
495
+
496
+ return $Block;
497
+ }
498
+ }
499
+
500
+ #
501
+ # List
502
+
503
+ protected function blockList($Line)
504
+ {
505
+ list($name, $pattern) = $Line['text'][0] <= '-' ? array('ul', '[*+-]') : array('ol', '[0-9]+[.]');
506
+
507
+ if (preg_match('/^('.$pattern.'[ ]+)(.*)/', $Line['text'], $matches))
508
+ {
509
+ $Block = array(
510
+ 'indent' => $Line['indent'],
511
+ 'pattern' => $pattern,
512
+ 'element' => array(
513
+ 'name' => $name,
514
+ 'handler' => 'elements',
515
+ ),
516
+ );
517
+
518
+ if($name === 'ol')
519
+ {
520
+ $listStart = stristr($matches[0], '.', true);
521
+
522
+ if($listStart !== '1')
523
+ {
524
+ $Block['element']['attributes'] = array('start' => $listStart);
525
+ }
526
+ }
527
+
528
+ $Block['li'] = array(
529
+ 'name' => 'li',
530
+ 'handler' => 'li',
531
+ 'text' => array(
532
+ $matches[2],
533
+ ),
534
+ );
535
+
536
+ $Block['element']['text'] []= & $Block['li'];
537
+
538
+ return $Block;
539
+ }
540
+ }
541
+
542
+ protected function blockListContinue($Line, array $Block)
543
+ {
544
+ if ($Block['indent'] === $Line['indent'] and preg_match('/^'.$Block['pattern'].'(?:[ ]+(.*)|$)/', $Line['text'], $matches))
545
+ {
546
+ if (isset($Block['interrupted']))
547
+ {
548
+ $Block['li']['text'] []= '';
549
+
550
+ unset($Block['interrupted']);
551
+ }
552
+
553
+ unset($Block['li']);
554
+
555
+ $text = isset($matches[1]) ? $matches[1] : '';
556
+
557
+ $Block['li'] = array(
558
+ 'name' => 'li',
559
+ 'handler' => 'li',
560
+ 'text' => array(
561
+ $text,
562
+ ),
563
+ );
564
+
565
+ $Block['element']['text'] []= & $Block['li'];
566
+
567
+ return $Block;
568
+ }
569
+
570
+ if ($Line['text'][0] === '[' and $this->blockReference($Line))
571
+ {
572
+ return $Block;
573
+ }
574
+
575
+ if ( ! isset($Block['interrupted']))
576
+ {
577
+ $text = preg_replace('/^[ ]{0,4}/', '', $Line['body']);
578
+
579
+ $Block['li']['text'] []= $text;
580
+
581
+ return $Block;
582
+ }
583
+
584
+ if ($Line['indent'] > 0)
585
+ {
586
+ $Block['li']['text'] []= '';
587
+
588
+ $text = preg_replace('/^[ ]{0,4}/', '', $Line['body']);
589
+
590
+ $Block['li']['text'] []= $text;
591
+
592
+ unset($Block['interrupted']);
593
+
594
+ return $Block;
595
+ }
596
+ }
597
+
598
+ #
599
+ # Quote
600
+
601
+ protected function blockQuote($Line)
602
+ {
603
+ if (preg_match('/^>[ ]?(.*)/', $Line['text'], $matches))
604
+ {
605
+ $Block = array(
606
+ 'element' => array(
607
+ 'name' => 'blockquote',
608
+ 'handler' => 'lines',
609
+ 'text' => (array) $matches[1],
610
+ ),
611
+ );
612
+
613
+ return $Block;
614
+ }
615
+ }
616
+
617
+ protected function blockQuoteContinue($Line, array $Block)
618
+ {
619
+ if ($Line['text'][0] === '>' and preg_match('/^>[ ]?(.*)/', $Line['text'], $matches))
620
+ {
621
+ if (isset($Block['interrupted']))
622
+ {
623
+ $Block['element']['text'] []= '';
624
+
625
+ unset($Block['interrupted']);
626
+ }
627
+
628
+ $Block['element']['text'] []= $matches[1];
629
+
630
+ return $Block;
631
+ }
632
+
633
+ if ( ! isset($Block['interrupted']))
634
+ {
635
+ $Block['element']['text'] []= $Line['text'];
636
+
637
+ return $Block;
638
+ }
639
+ }
640
+
641
+ #
642
+ # Rule
643
+
644
+ protected function blockRule($Line)
645
+ {
646
+ if (preg_match('/^(['.$Line['text'][0].'])([ ]*\1){2,}[ ]*$/', $Line['text']))
647
+ {
648
+ $Block = array(
649
+ 'element' => array(
650
+ 'name' => 'hr'
651
+ ),
652
+ );
653
+
654
+ return $Block;
655
+ }
656
+ }
657
+
658
+ #
659
+ # Setext
660
+
661
+ protected function blockSetextHeader($Line, array $Block = null)
662
+ {
663
+ if ( ! isset($Block) or isset($Block['type']) or isset($Block['interrupted']))
664
+ {
665
+ return;
666
+ }
667
+
668
+ if (chop($Line['text'], $Line['text'][0]) === '')
669
+ {
670
+ $Block['element']['name'] = $Line['text'][0] === '=' ? 'h1' : 'h2';
671
+
672
+ return $Block;
673
+ }
674
+ }
675
+
676
+ #
677
+ # Markup
678
+
679
+ protected function blockMarkup($Line)
680
+ {
681
+ if ($this->markupEscaped)
682
+ {
683
+ return;
684
+ }
685
+
686
+ if (preg_match('/^<(\w*)(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*(\/)?>/', $Line['text'], $matches))
687
+ {
688
+ $element = strtolower($matches[1]);
689
+
690
+ if (in_array($element, $this->textLevelElements))
691
+ {
692
+ return;
693
+ }
694
+
695
+ $Block = array(
696
+ 'name' => $matches[1],
697
+ 'depth' => 0,
698
+ 'markup' => $Line['text'],
699
+ );
700
+
701
+ $length = strlen($matches[0]);
702
+
703
+ $remainder = substr($Line['text'], $length);
704
+
705
+ if (trim($remainder) === '')
706
+ {
707
+ if (isset($matches[2]) or in_array($matches[1], $this->voidElements))
708
+ {
709
+ $Block['closed'] = true;
710
+
711
+ $Block['void'] = true;
712
+ }
713
+ }
714
+ else
715
+ {
716
+ if (isset($matches[2]) or in_array($matches[1], $this->voidElements))
717
+ {
718
+ return;
719
+ }
720
+
721
+ if (preg_match('/<\/'.$matches[1].'>[ ]*$/i', $remainder))
722
+ {
723
+ $Block['closed'] = true;
724
+ }
725
+ }
726
+
727
+ return $Block;
728
+ }
729
+ }
730
+
731
+ protected function blockMarkupContinue($Line, array $Block)
732
+ {
733
+ if (isset($Block['closed']))
734
+ {
735
+ return;
736
+ }
737
+
738
+ if (preg_match('/^<'.$Block['name'].'(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*>/i', $Line['text'])) # open
739
+ {
740
+ $Block['depth'] ++;
741
+ }
742
+
743
+ if (preg_match('/(.*?)<\/'.$Block['name'].'>[ ]*$/i', $Line['text'], $matches)) # close
744
+ {
745
+ if ($Block['depth'] > 0)
746
+ {
747
+ $Block['depth'] --;
748
+ }
749
+ else
750
+ {
751
+ $Block['closed'] = true;
752
+ }
753
+ }
754
+
755
+ if (isset($Block['interrupted']))
756
+ {
757
+ $Block['markup'] .= "\n";
758
+
759
+ unset($Block['interrupted']);
760
+ }
761
+
762
+ $Block['markup'] .= "\n".$Line['body'];
763
+
764
+ return $Block;
765
+ }
766
+
767
+ #
768
+ # Reference
769
+
770
+ protected function blockReference($Line)
771
+ {
772
+ if (preg_match('/^\[(.+?)\]:[ ]*<?(\S+?)>?(?:[ ]+["\'(](.+)["\')])?[ ]*$/', $Line['text'], $matches))
773
+ {
774
+ $id = strtolower($matches[1]);
775
+
776
+ $Data = array(
777
+ 'url' => $matches[2],
778
+ 'title' => null,
779
+ );
780
+
781
+ if (isset($matches[3]))
782
+ {
783
+ $Data['title'] = $matches[3];
784
+ }
785
+
786
+ $this->DefinitionData['Reference'][$id] = $Data;
787
+
788
+ $Block = array(
789
+ 'hidden' => true,
790
+ );
791
+
792
+ return $Block;
793
+ }
794
+ }
795
+
796
+ #
797
+ # Table
798
+
799
+ protected function blockTable($Line, array $Block = null)
800
+ {
801
+ if ( ! isset($Block) or isset($Block['type']) or isset($Block['interrupted']))
802
+ {
803
+ return;
804
+ }
805
+
806
+ if (strpos($Block['element']['text'], '|') !== false and chop($Line['text'], ' -:|') === '')
807
+ {
808
+ $alignments = array();
809
+
810
+ $divider = $Line['text'];
811
+
812
+ $divider = trim($divider);
813
+ $divider = trim($divider, '|');
814
+
815
+ $dividerCells = explode('|', $divider);
816
+
817
+ foreach ($dividerCells as $dividerCell)
818
+ {
819
+ $dividerCell = trim($dividerCell);
820
+
821
+ if ($dividerCell === '')
822
+ {
823
+ continue;
824
+ }
825
+
826
+ $alignment = null;
827
+
828
+ if ($dividerCell[0] === ':')
829
+ {
830
+ $alignment = 'left';
831
+ }
832
+
833
+ if (substr($dividerCell, - 1) === ':')
834
+ {
835
+ $alignment = $alignment === 'left' ? 'center' : 'right';
836
+ }
837
+
838
+ $alignments []= $alignment;
839
+ }
840
+
841
+ # ~
842
+
843
+ $HeaderElements = array();
844
+
845
+ $header = $Block['element']['text'];
846
+
847
+ $header = trim($header);
848
+ $header = trim($header, '|');
849
+
850
+ $headerCells = explode('|', $header);
851
+
852
+ foreach ($headerCells as $index => $headerCell)
853
+ {
854
+ $headerCell = trim($headerCell);
855
+
856
+ $HeaderElement = array(
857
+ 'name' => 'th',
858
+ 'text' => $headerCell,
859
+ 'handler' => 'line',
860
+ );
861
+
862
+ if (isset($alignments[$index]))
863
+ {
864
+ $alignment = $alignments[$index];
865
+
866
+ $HeaderElement['attributes'] = array(
867
+ 'style' => 'text-align: '.$alignment.';',
868
+ );
869
+ }
870
+
871
+ $HeaderElements []= $HeaderElement;
872
+ }
873
+
874
+ # ~
875
+
876
+ $Block = array(
877
+ 'alignments' => $alignments,
878
+ 'identified' => true,
879
+ 'element' => array(
880
+ 'name' => 'table',
881
+ 'handler' => 'elements',
882
+ ),
883
+ );
884
+
885
+ $Block['element']['text'] []= array(
886
+ 'name' => 'thead',
887
+ 'handler' => 'elements',
888
+ );
889
+
890
+ $Block['element']['text'] []= array(
891
+ 'name' => 'tbody',
892
+ 'handler' => 'elements',
893
+ 'text' => array(),
894
+ );
895
+
896
+ $Block['element']['text'][0]['text'] []= array(
897
+ 'name' => 'tr',
898
+ 'handler' => 'elements',
899
+ 'text' => $HeaderElements,
900
+ );
901
+
902
+ return $Block;
903
+ }
904
+ }
905
+
906
+ protected function blockTableContinue($Line, array $Block)
907
+ {
908
+ if (isset($Block['interrupted']))
909
+ {
910
+ return;
911
+ }
912
+
913
+ if ($Line['text'][0] === '|' or strpos($Line['text'], '|'))
914
+ {
915
+ $Elements = array();
916
+
917
+ $row = $Line['text'];
918
+
919
+ $row = trim($row);
920
+ $row = trim($row, '|');
921
+
922
+ preg_match_all('/(?:(\\\\[|])|[^|`]|`[^`]+`|`)+/', $row, $matches);
923
+
924
+ foreach ($matches[0] as $index => $cell)
925
+ {
926
+ $cell = trim($cell);
927
+
928
+ $Element = array(
929
+ 'name' => 'td',
930
+ 'handler' => 'line',
931
+ 'text' => $cell,
932
+ );
933
+
934
+ if (isset($Block['alignments'][$index]))
935
+ {
936
+ $Element['attributes'] = array(
937
+ 'style' => 'text-align: '.$Block['alignments'][$index].';',
938
+ );
939
+ }
940
+
941
+ $Elements []= $Element;
942
+ }
943
+
944
+ $Element = array(
945
+ 'name' => 'tr',
946
+ 'handler' => 'elements',
947
+ 'text' => $Elements,
948
+ );
949
+
950
+ $Block['element']['text'][1]['text'] []= $Element;
951
+
952
+ return $Block;
953
+ }
954
+ }
955
+
956
+ #
957
+ # ~
958
+ #
959
+
960
+ protected function paragraph($Line)
961
+ {
962
+ $Block = array(
963
+ 'element' => array(
964
+ 'name' => 'p',
965
+ 'text' => $Line['text'],
966
+ 'handler' => 'line',
967
+ ),
968
+ );
969
+
970
+ return $Block;
971
+ }
972
+
973
+ #
974
+ # Inline Elements
975
+ #
976
+
977
+ protected $InlineTypes = array(
978
+ '"' => array('SpecialCharacter'),
979
+ '!' => array('Image'),
980
+ '&' => array('SpecialCharacter'),
981
+ '*' => array('Emphasis'),
982
+ ':' => array('Url'),
983
+ '<' => array('UrlTag', 'EmailTag', 'Markup', 'SpecialCharacter'),
984
+ '>' => array('SpecialCharacter'),
985
+ '[' => array('Link'),
986
+ '_' => array('Emphasis'),
987
+ '`' => array('Code'),
988
+ '~' => array('Strikethrough'),
989
+ '\\' => array('EscapeSequence'),
990
+ );
991
+
992
+ # ~
993
+
994
+ protected $inlineMarkerList = '!"*_&[:<>`~\\';
995
+
996
+ #
997
+ # ~
998
+ #
999
+
1000
+ public function line($text)
1001
+ {
1002
+ $markup = '';
1003
+
1004
+ # $excerpt is based on the first occurrence of a marker
1005
+
1006
+ while ($excerpt = strpbrk($text, $this->inlineMarkerList))
1007
+ {
1008
+ $marker = $excerpt[0];
1009
+
1010
+ $markerPosition = strpos($text, $marker);
1011
+
1012
+ $Excerpt = array('text' => $excerpt, 'context' => $text);
1013
+
1014
+ foreach ($this->InlineTypes[$marker] as $inlineType)
1015
+ {
1016
+ $Inline = $this->{'inline'.$inlineType}($Excerpt);
1017
+
1018
+ if ( ! isset($Inline))
1019
+ {
1020
+ continue;
1021
+ }
1022
+
1023
+ # makes sure that the inline belongs to "our" marker
1024
+
1025
+ if (isset($Inline['position']) and $Inline['position'] > $markerPosition)
1026
+ {
1027
+ continue;
1028
+ }
1029
+
1030
+ # sets a default inline position
1031
+
1032
+ if ( ! isset($Inline['position']))
1033
+ {
1034
+ $Inline['position'] = $markerPosition;
1035
+ }
1036
+
1037
+ # the text that comes before the inline
1038
+ $unmarkedText = substr($text, 0, $Inline['position']);
1039
+
1040
+ # compile the unmarked text
1041
+ $markup .= $this->unmarkedText($unmarkedText);
1042
+
1043
+ # compile the inline
1044
+ $markup .= isset($Inline['markup']) ? $Inline['markup'] : $this->element($Inline['element']);
1045
+
1046
+ # remove the examined text
1047
+ $text = substr($text, $Inline['position'] + $Inline['extent']);
1048
+
1049
+ continue 2;
1050
+ }
1051
+
1052
+ # the marker does not belong to an inline
1053
+
1054
+ $unmarkedText = substr($text, 0, $markerPosition + 1);
1055
+
1056
+ $markup .= $this->unmarkedText($unmarkedText);
1057
+
1058
+ $text = substr($text, $markerPosition + 1);
1059
+ }
1060
+
1061
+ $markup .= $this->unmarkedText($text);
1062
+
1063
+ return $markup;
1064
+ }
1065
+
1066
+ #
1067
+ # ~
1068
+ #
1069
+
1070
+ protected function inlineCode($Excerpt)
1071
+ {
1072
+ $marker = $Excerpt['text'][0];
1073
+
1074
+ if (preg_match('/^('.$marker.'+)[ ]*(.+?)[ ]*(?<!'.$marker.')\1(?!'.$marker.')/s', $Excerpt['text'], $matches))
1075
+ {
1076
+ $text = $matches[2];
1077
+ $text = htmlspecialchars($text, ENT_NOQUOTES, 'UTF-8');
1078
+ $text = preg_replace("/[ ]*\n/", ' ', $text);
1079
+
1080
+ return array(
1081
+ 'extent' => strlen($matches[0]),
1082
+ 'element' => array(
1083
+ 'name' => 'code',
1084
+ 'text' => $text,
1085
+ ),
1086
+ );
1087
+ }
1088
+ }
1089
+
1090
+ protected function inlineEmailTag($Excerpt)
1091
+ {
1092
+ if (strpos($Excerpt['text'], '>') !== false and preg_match('/^<((mailto:)?\S+?@\S+?)>/i', $Excerpt['text'], $matches))
1093
+ {
1094
+ $url = $matches[1];
1095
+
1096
+ if ( ! isset($matches[2]))
1097
+ {
1098
+ $url = 'mailto:' . $url;
1099
+ }
1100
+
1101
+ return array(
1102
+ 'extent' => strlen($matches[0]),
1103
+ 'element' => array(
1104
+ 'name' => 'a',
1105
+ 'text' => $matches[1],
1106
+ 'attributes' => array(
1107
+ 'href' => $url,
1108
+ ),
1109
+ ),
1110
+ );
1111
+ }
1112
+ }
1113
+
1114
+ protected function inlineEmphasis($Excerpt)
1115
+ {
1116
+ if ( ! isset($Excerpt['text'][1]))
1117
+ {
1118
+ return;
1119
+ }
1120
+
1121
+ $marker = $Excerpt['text'][0];
1122
+
1123
+ if ($Excerpt['text'][1] === $marker and preg_match($this->StrongRegex[$marker], $Excerpt['text'], $matches))
1124
+ {
1125
+ $emphasis = 'strong';
1126
+ }
1127
+ elseif (preg_match($this->EmRegex[$marker], $Excerpt['text'], $matches))
1128
+ {
1129
+ $emphasis = 'em';
1130
+ }
1131
+ else
1132
+ {
1133
+ return;
1134
+ }
1135
+
1136
+ return array(
1137
+ 'extent' => strlen($matches[0]),
1138
+ 'element' => array(
1139
+ 'name' => $emphasis,
1140
+ 'handler' => 'line',
1141
+ 'text' => $matches[1],
1142
+ ),
1143
+ );
1144
+ }
1145
+
1146
+ protected function inlineEscapeSequence($Excerpt)
1147
+ {
1148
+ if (isset($Excerpt['text'][1]) and in_array($Excerpt['text'][1], $this->specialCharacters))
1149
+ {
1150
+ return array(
1151
+ 'markup' => $Excerpt['text'][1],
1152
+ 'extent' => 2,
1153
+ );
1154
+ }
1155
+ }
1156
+
1157
+ protected function inlineImage($Excerpt)
1158
+ {
1159
+ if ( ! isset($Excerpt['text'][1]) or $Excerpt['text'][1] !== '[')
1160
+ {
1161