1<?php 2 3namespace dokuwiki\plugin\imagemapping; 4 5use dokuwiki\Parsing\Handler\CallWriterInterface; 6 7/** 8 * Custom CallWriter for the imagemapping plugin 9 */ 10class ImageMapHandler implements CallWriterInterface 11{ 12 /** @var CallWriterInterface the parent call writer */ 13 public $CallWriter; 14 15 /** @var array the local call stack */ 16 protected $calls = []; 17 18 /** @var string Name of this imagemap FIXME currenlty unsused?*/ 19 private $mapname; 20 21 /** 22 * Constructor 23 * 24 * @param CallWriterInterface $CallWriter the "parent" call writer 25 */ 26 public function __construct($mapname, CallWriterInterface $CallWriter) 27 { 28 $this->mapname = $mapname; 29 $this->CallWriter = $CallWriter; 30 } 31 32 /** @inheritdoc */ 33 public function writeCall($call) 34 { 35 $this->calls[] = $call; 36 } 37 38 /** @inheritdoc */ 39 public function writeCalls($calls) 40 { 41 $this->calls = array_merge($this->calls, $calls); 42 } 43 44 /** @inheritdoc */ 45 public function finalise() 46 { 47 $last_call = end($this->calls); 48 $this->process(); 49 $this->addPluginCall([DOKU_LEXER_EXIT], $last_call[2]); 50 $this->CallWriter->finalise(); // FIXME do we really need to call it? 51 } 52 53 /** 54 * Get the parent call writer 55 * 56 * @return CallWriterInterface 57 */ 58 public function getCallWriter() 59 { 60 return $this->CallWriter; 61 } 62 63 /** 64 * Process the local call stack 65 * 66 * @return void 67 */ 68 public function process() 69 { 70 $last_call = end($this->calls); 71 $first_call = array_shift($this->calls); 72 73 $this->CallWriter->writeCall($first_call); 74 $this->processLinks($first_call[2]); 75 76 if (!empty($this->calls)) { 77 $this->addPluginCall([DOKU_LEXER_MATCHED, 'divstart'], $first_call[2]); 78 //Force a new paragraph 79 $this->CallWriter->writeCall(['eol', [], $this->calls[0][2]]); 80 $this->CallWriter->writeCalls($this->calls); 81 $this->addPluginCall([DOKU_LEXER_MATCHED, 'divend'], $last_call[2]); 82 } 83 } 84 85 /** 86 * Adds a call to the imagemap plugin with the given data 87 * 88 * The syntax component will be called with the given data and handle the various sub modes 89 * 90 * @param array $args [DOKU_LEXER_*, 'submode', params...] 91 * @param int $pos 92 * @return void 93 */ 94 protected function addPluginCall($args, $pos) 95 { 96 $this->CallWriter->writeCall(['plugin', ['imagemapping', $args, $args[0]], $pos]); 97 } 98 99 /** 100 * Add a new area call to the call stack 101 * 102 * @param int $pos The position in the source 103 * @param string $type The type of the link (internallink, externallink, ...) 104 * @param string $title The link title including the coordinates 105 * @param string $url The link part of the link 106 * @param string $wiki The interwiki identifier for interwiki links 107 * @return string The title without the coordinates 108 */ 109 protected function addArea($pos, $type, $title, $url, $wiki = null) 110 { 111 if (preg_match('/^(.*)@([^@]+)$/u', $title, $match)) { 112 $coords = explode(',', $match[2]); 113 if (count($coords) == 3) { 114 $shape = 'circle'; 115 } elseif (count($coords) == 4) { 116 $shape = 'rect'; 117 } elseif (count($coords) >= 6) { 118 $shape = 'poly'; 119 } else { 120 return $title; 121 } 122 $coords = array_map('trim', $coords); 123 $title = trim($match[1]); 124 125 $coords = join(',', $coords); 126 $coords = trim($coords); 127 128 $this->addPluginCall( 129 [DOKU_LEXER_MATCHED, 'area', $shape, $coords, $type, $title, $url, $wiki], 130 $pos 131 ); 132 } 133 return $title; 134 } 135 136 /** 137 * Walk through the call stack and process all links 138 * 139 * This will add the imagemap areas to the call stack and remove the coordinates from the link titles 140 * 141 * @todo simplify more and add tests 142 * @param int $pos The source position 143 * @return void 144 */ 145 protected function processLinks($pos) 146 { 147 for ($n = 0; $n < count($this->calls); $n++) { 148 $data =& $this->calls[$n][1]; 149 $type = $this->calls[$n][0]; 150 switch ($type) { 151 case 'plugin': 152 // support for other plugins that use the imagemap syntax (e.g. the popupviewer plugin) 153 $plugin = plugin_load('syntax', $data[0]); 154 if ($plugin != null && method_exists($plugin, 'convertToImageMapArea')) { 155 $plugin->convertToImageMapArea($this, $data[1], $pos); 156 } 157 break; 158 case 'internallink': 159 case 'locallink': 160 case 'externallink': 161 case 'emaillink': 162 case 'windowssharelink': 163 if (is_array($data[1])) { 164 $title = $data[1]['title'] ?? ''; 165 } else { 166 $title = $data[1]; 167 } 168 $title = $this->addArea($pos, $type, $title, $data[0]); 169 if (is_array($data[1])) { 170 $data[1]['title'] = $title; 171 } else { 172 $data[1] = $title; 173 } 174 break; 175 case 'interwikilink': 176 if (is_array($data[1])) { 177 $title = $data[1]['title']; 178 } else { 179 $title = $data[1]; 180 } 181 $title = $this->addArea($pos, $type, $title, $data[3], $data[2]); 182 if (is_array($data[1])) { 183 $data[1]['title'] = $title; 184 } else { 185 $data[1] = $title; 186 } 187 break; 188 } 189 } 190 } 191 192} 193