1<?php 2/* 3 * This file is part of the clockoon/dokuwiki-commonmark-plugin package. 4 * 5 * (c) Sungbin Jeon <clockoon@gmail.com> 6 * 7 * Original code based on the followings: 8 * - CommonMark JS reference parser (https://bitly.com/commonmark-js) (c) John MacFarlane 9 * - league/commonmark (https://github.com/thephpleague/commonmark) (c) Colin O'Dell <colinodell@gmail.com> 10 * 11 * For the full copyright and license information, please view the LICENSE 12 * file that was distributed with this source code. 13 */ 14 15 require_once __DIR__.'/src/bootstrap.php'; 16 17 use Dokuwiki\Plugin\Commonmark\Commonmark; 18 use Nette\Utils\Strings; 19 20 if(!defined('DOKU_INC')) die(); 21 22 class action_plugin_commonmark extends DokuWiki_Action_Plugin { 23 // array for heading positions 24 // [hid] => {[depth], [startline], [endline]} 25 public $headingInfo = []; 26 // positions of newline 27 public $linePosition = []; 28 // flag for checking first run only 29 public $firstRun = true; 30 31 /** 32 * pass text to Commonmark parser before DW parser 33 */ 34 public function register(Doku_Event_Handler $controller) { 35 $controller->register_hook('PARSER_WIKITEXT_PREPROCESS', 'BEFORE', $this, 36 '_commonmarkparse'); 37 $controller->register_hook('HTML_SECEDIT_BUTTON', 'BEFORE', $this, 38 '_editbutton'); 39 } 40 41 public function _editbutton(Doku_Event $event, $param) { 42 global $conf; 43 44 // get hid 45 $hid = $event->data['hid']; 46 // fetch range on original md 47 // check hid match 48 $keys = array_keys($this->headingInfo); 49 if (array_key_exists($hid,$keys)) { 50 // get max section editing level config 51 $maxsec = $conf['maxseclevel']; 52 // set start position 53 // first, check whether first heading 54 if ($hid == $keys[0]) { 55 $start = 1; 56 } else { 57 $lineStart = $this->headingInfo[$hid]['startline']; 58 // since CommonMark library finds heading marks, we have to declare 59 $start = $this->linePosition[$lineStart]; 60 } 61 // find end key & location; proceed while max level or less arrived 62 $endlevel = 52; 63 $index = array_search($hid,$keys); 64 $end = 0; // 0 means end of document 65 $stop = false; 66 while($stop == false) { 67 if (isset($keys[$index+1])) { // check for non-last element 68 $endlevel = $this->headingInfo[$keys[$index+1]]['level']; 69 $lineEnd = $this->headingInfo[$keys[$index+1]]['startline'] - 1; // go one line up 70 $end = $this->linePosition[$lineEnd]; 71 if($maxsec<=$endlevel) { $stop = true; } 72 } else { 73 $end = 0; 74 $stop = true; 75 } 76 $index = $index + 1; 77 } 78 if($end == 0) { 79 $event->data['range'] = (string)$start.'-'; 80 } else { 81 $event->data['range'] = (string)$start.'-'.$end; 82 } 83 84 } 85 // example: $event->data['range'] = '1-2'; 86 } 87 88 public function _commonmarkparse(Doku_Event $event, $param) { 89 $markdown = $event->data; 90 // check force_commonmark option; if 1, ignore doctype 91 if ($this->getConf('force_commonmark')) { 92 $markdown = ltrim($markdown); 93 $result = Commonmark::RendtoDW($markdown, $this->getConf('frontmatter_tag')); 94 } 95 elseif (preg_match('/\A<!DOCTYPE markdown>/',$markdown)) { 96 $markdown = preg_replace('/\A<!DOCTYPE markdown>\n/','',$markdown); 97 $markdown = ltrim($markdown); 98 $result = Commonmark::RendtoDW($markdown, $this->getConf('frontmatter_tag')); 99 $event->data = $result['text']; 100 } 101 $event->data = $result['text']; 102 if ($this->firstRun == true) { 103 // get position of each line 104 $lastPos = 0; 105 while(($lastPos = strpos($markdown,'\n',$lastPos)) !== false){ 106 $linePosition[] = $lastPos; 107 $lastPos = $lastPos + strlen('\n'); 108 } 109 $this->headingInfo = $this->CleanHeadingInfo($result['heading']); 110 $this->firstRun = false; 111 } 112 } 113 114 public function CleanHeadingInfo(array $input): array { 115 $keys = array_keys($input); 116 foreach($keys as $key) { 117 $new_key = sectionId($key, false); 118 if($new_key != $key) { 119 $input[$new_key] = $input[$key]; 120 unset($input[$key]); 121 } 122 } 123 return $input; 124 } 125} 126