1<?php 2/** 3 * Image Map 4 * 5 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 6 * @author Tom N Harris <tools@inetsoftware.de> 7 * 8 * Based upon the non public version by Tom N Harris 9 */ 10 11// must be run within Dokuwiki 12if (!defined('DOKU_INC')) die(); 13 14if (!defined('DOKU_LF')) define('DOKU_LF', "\n"); 15if (!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN', DOKU_INC.'lib/plugins/'); 16 17require_once(DOKU_PLUGIN.'syntax.php'); 18 19class syntax_plugin_imagemapping extends DokuWiki_Syntax_Plugin { 20 21 function __construct() { 22 } 23 24 function getType(){ return 'container'; } 25 function getSort(){ return 316; } 26 function getPType(){ return 'block';} 27 function getAllowedTypes() { 28 return array('formatting','substition','disabled','protected','container','paragraphs'); 29 } 30 31 function connectTo($mode) { 32 $this->Lexer->addEntryPattern('\{\{map>[^\}]+\}\}', $mode, 'plugin_imagemapping'); 33 } 34 function postConnect() { 35 $this->Lexer->addExitPattern('\{\{<map\}\}', 'plugin_imagemapping'); 36 } 37 38 function handle($match, $state, $pos, Doku_Handler $handler){ 39 global $conf; 40 global $ID; 41 $args = array($state); 42 43 switch ($state) { 44 case DOKU_LEXER_ENTER: 45 $img = Doku_Handler_Parse_Media(substr($match, 6, -2)); 46 if ($img['title']) { 47 $mapname = str_replace(':','',cleanID($img['title'])); 48 $mapname = ltrim($mapname, '0123456789._-'); 49 } 50 if (empty($mapname)) { 51 if ($img['type'] == 'internalmedia') { 52 $src = $img['src']; 53 resolve_mediaid(getNS($ID),$src, $exists); 54 $nssep = ($conf['useslash']) ? '[:;/]' : '[:;]'; 55 $mapname = preg_replace('!.*'.$nssep.'!','',$src); 56 } else { 57 $src = parse_url($img['src']); 58 $mapname = str_replace(':','',cleanID($src['host'].$src['path'].$src['query'])); 59 $mapname = ltrim($mapname, '0123456789._-'); 60 } 61 if (empty($mapname)) { 62 $mapname = 'imagemap'.$pos; 63 } 64 } 65 $args = array($state, $img['type'], $img['src'], $img['title'], $mapname, 66 $img['align'], $img['width'], $img['height'], 67 $img['cache']); 68 69 if ( $handler->CallWriter ) { 70 $ReWriter = new ImageMap_Handler($mapname, $handler->CallWriter); 71 $handler->CallWriter =& $ReWriter; 72 } else { 73 $ReWriter = new ImageMap_Handler($mapname, $handler->getCallWriter()); 74 $handler->setCallWriter( $ReWriter ); 75 } 76 break; 77 case DOKU_LEXER_EXIT: 78 if ( $handler->CallWriter ) { 79 $handler->CallWriter->process(); 80 $ReWriter = $handler->CallWriter; 81 $handler->CallWriter =& $ReWriter->CallWriter; 82 } else { 83 $handler->getCallWriter()->process(); 84 $ReWriter = $handler->getCallWriter(); 85 $handler->setCallWriter( $ReWriter->CallWriter ); 86 } 87 break; 88 case DOKU_LEXER_MATCHED: 89 break; 90 case DOKU_LEXER_UNMATCHED: 91 $args[] = $match; 92 break; 93 } 94 return $args; 95 } 96 97 function render($format, Doku_Renderer $renderer, $data) { 98 global $conf; 99 global $ID; 100 static $has_content=false; 101 $state = $data[0]; 102 if (substr($format,0,5) == 'xhtml') { 103 switch ($state) { 104 case DOKU_LEXER_ENTER: 105 list($state,$type,$src,$title,$name,$align,$width,$height,$cache) = $data; 106 if ($type=='internalmedia') { 107 resolve_mediaid(getNS($ID),$src, $exists); 108 } 109 $renderer->doc .= '<p>'.DOKU_LF; 110 $src = ml($src,array('w'=>$width,'h'=>$height,'cache'=>$cache)); 111 $renderer->doc .= ' <img src="'.$src.'" class="media'.$align.' imap" usemap="#'.$name.'"'; 112 if($align == 'right' || $align == 'left') 113 $renderer->doc .= ' align="'.$align.'"'; 114 if (!is_null($title)) { 115 $title = $renderer->_xmlEntities($title); 116 $renderer->doc .= ' title="'.$title.'"'; 117 $renderer->doc .= ' alt="'.$title.'"'; 118 } else { 119 $renderer->doc .= ' alt=""'; 120 } 121 if (!is_null($width)) 122 $renderer->doc .= ' width="'.$renderer->_xmlEntities($width).'"'; 123 if (!is_null($height)) 124 $renderer->doc .= ' height="'.$renderer->_xmlEntities($height).'"'; 125 $renderer->doc .= ' />'.DOKU_LF; 126 $renderer->doc .= '</p>'.DOKU_LF; 127 $renderer->doc .= '<map name="'.$name.'" id="'.$name.'">'.DOKU_LF; 128 $has_content = false; 129 break; 130 case DOKU_LEXER_MATCHED: 131 if ($data[1]=='area') { 132 @list($state,$match,$shape,$coords,$type,$title,$url,$wiki) = $data; 133 $target = ''; 134 switch ($type) { 135 case 'internallink': 136 if ($url === '') $url = $ID; 137 $default = $renderer->_simpleTitle($url); 138 resolve_pageid(getNS($ID),$url,$exists); 139 $title = $renderer->_getLinkTitle($title, $default, $isImg, $url); 140 list($url,$hash) = explode('#',$url,2); 141 if (!empty($hash)) $hash = $renderer->_headerToLink($hash); 142 $url = wl($url); 143 if ($hash) $url .= '#'.$hash; 144 $target = $conf['target']['wiki']; 145 break; 146 case 'locallink': 147 $title = $renderer->_getLinkTitle($title, $url, $isImg); 148 $url = $renderer->_headerToLink($url); 149 $url = '#'.$url; 150 break; 151 case 'externallink': 152 $title = $renderer->_getLinkTitle($title, $url, $isImg); 153 // url might be an attack vector, only allow registered protocols 154 if(is_null($this->schemes)) $this->schemes = getSchemes(); 155 list($scheme) = explode('://',$url); 156 $scheme = strtolower($scheme); 157 if(!in_array($scheme,$this->schemes)) $url = ''; 158 $target = $conf['target']['extern']; 159 break; 160 case 'interwikilink': 161 $title = $renderer->_getLinkTitle($title, $url, $isImg); 162 $url = $renderer->_resolveInterWiki($wiki,$url); 163 if (strpos($url,DOKU_URL) === 0) 164 $target = $conf['target']['wiki']; 165 else 166 $target = $conf['target']['interwiki']; 167 break; 168 case 'emaillink': 169 $url = $renderer->_xmlEntities($url); 170 $url = obfuscate($url); 171 $title = $renderer->_getLinkTitle($title, $url, $isImg); 172 if ($conf['mailguard'] == 'visible') 173 $url = rawurlencode($url); 174 $url = 'mailto:'.$url; 175 break; 176 case 'windowssharelink': 177 $title = $renderer->_getLinkTitle($title, $url, $isImg); 178 $url = str_replace('\\','/',$url); 179 $url = 'file:///'.$url; 180 $target = $conf['target']['windows']; 181 break; 182 case 'internalmedia': 183 list($url,$hash) = explode('#',$url,2); 184 resolve_mediaid(getNS($ID), $url, $exists); 185 $title = $renderer->_media($url, $title, null,null,null,null, false); 186 $url = ml($url, ($extra[1]=='direct')); 187 if ($hash) 188 $url .= '#'.$hash; 189 break; 190 } 191 if($url){ 192 $renderer->doc .= '<area href="'.$url.'"'; 193 if (!empty($target)) 194 $renderer->doc .= ' target="'.$target.'"'; 195 $renderer->doc .= ' title="'.$title.'" alt="'.$title.'"'; 196 197 $renderer->doc .= ' shape="'.$shape.'" coords="'.$coords.'"/>'; 198 } 199 } elseif ($data[1]=='divstart') { 200 $renderer->doc .= DOKU_LF.'<div class="imapcontent">'.DOKU_LF; 201 $has_content = true; 202 } elseif ($data[1]=='divend') { 203 $renderer->doc .= DOKU_LF;//.'</div>'.DOKU_LF; 204 } 205 break; 206 case DOKU_LEXER_EXIT: 207 if ($has_content) $renderer->doc .= '</div>'.DOKU_LF; 208 $renderer->doc .= '</map>'; 209 break; 210 case DOKU_LEXER_UNMATCHED: 211 $renderer->doc .= $renderer->_xmlEntities($data[1]); 212 break; 213 } 214 return true; 215 } 216 elseif ($format == 'metadata') { 217 switch ($state) { 218 case DOKU_LEXER_ENTER: 219 list($state,$type,$src,$title,$name) = $data; 220 if ($type=='internalmedia') { 221 resolve_mediaid(getNS($ID),$src, $exists); 222 $renderer->meta['relation']['media'][$src] = $exists; 223 } 224 if (is_null($title)) 225 $title = $name; 226 if ($renderer->capture && $title) 227 $renderer->doc .= '['.$title.']'; 228 break; 229 case DOKU_LEXER_EXIT: 230 break; 231 case DOKU_LEXER_UNMATCHED: 232 if ($renderer->capture) 233 $renderer->doc .= $data[1]; 234 break; 235 } 236 return true; 237 } 238 return false; 239 } 240 241} 242 243if ( interface_exists( "dokuwiki\Parsing\Handler\CallWriterInterface", true ) ) { 244 // interface does not exist. DW too old?! 245 interface InternalCallWriterInterface extends dokuwiki\Parsing\Handler\CallWriterInterface { 246 } 247 248} else { 249 if ( !interface_exists("Doku_Handler_CallWriter_Interface") ) { 250 interface Doku_Handler_CallWriter_Interface {} 251 } 252 253 // interface does not exist. DW too old?! 254 interface InternalCallWriterInterface extends Doku_Handler_CallWriter_Interface { 255 } 256} 257 258class ImageMap_Handler implements InternalCallWriterInterface { 259 260 public $CallWriter; 261 262 private $calls = array(); 263 private $areas = array(); 264 private $mapname; 265 266 function __construct($name, &$CallWriter) { 267 $this->CallWriter =& $CallWriter; 268 $this->mapname = $name; 269 } 270 271 function writeCall($call) { 272 $this->calls[] = $call; 273 } 274 275 function writeCalls($calls) { 276 $this->calls = array_merge($this->calls, $calls); 277 } 278 279 function finalise() { 280 $last_call = end($this->calls); 281 $this->process(); 282 $this->_addPluginCall(array(DOKU_LEXER_EXIT), $last_call[2]); 283 $this->CallWriter->finalise(); 284 } 285 286 function process() { 287 $last_call = end($this->calls); 288 $first_call = array_shift($this->calls); 289 290 $this->CallWriter->writeCall($first_call); 291 $this->_processLinks($first_call[2]); 292 293 if (!empty($this->calls)) { 294 $this->_addPluginCall(array(DOKU_LEXER_MATCHED,'divstart'), $first_call[2]); 295 //Force a new paragraph 296 $this->CallWriter->writeCall(array('eol',array(),$this->calls[0][2])); 297 $this->CallWriter->writeCalls($this->calls); 298 $this->_addPluginCall(array(DOKU_LEXER_MATCHED,'divend'), $last_call[2]); 299 } 300 } 301 302 function _addPluginCall($args, $pos) { 303 $this->CallWriter->writeCall(array('plugin', 304 array('imagemapping', $args, $args[0]), 305 $pos)); 306 } 307 308 function _addArea($pos, $type, $title, $url, $wiki=null) { 309 if (preg_match('/^(.*)@([^@]+)$/u', $title, $match)) { 310 $coords = explode(',',$match[2]); 311 if (count($coords) == 3) { 312 $shape = 'circle'; 313 } elseif (count($coords) == 4) { 314 $shape = 'rect'; 315 } elseif (count($coords) >= 6) { 316 $shape = 'poly'; 317 } else { 318 return $title; 319 } 320 $coords = array_map('trim', $coords); 321 $title = trim($match[1]); 322 323 $coords = join(',',$coords); 324 $coords = trim( $coords ); 325 326 $this->_addPluginCall(array(DOKU_LEXER_MATCHED, 'area', $shape, $coords, 327 $type, $title, $url, $wiki), $pos); 328 } 329 return $title; 330 } 331 332 function _processLinks($pos) { 333 for ($n=0;$n<count($this->calls);$n++) { 334 $data =& $this->calls[$n][1]; 335 $type = $this->calls[$n][0]; 336 switch ($type) { 337 case 'plugin': 338 339 $plugin = plugin_load('syntax', $data[0]); 340 if ( $plugin != null && method_exists($plugin, 'convertToImageMapArea')) { 341 $plugin->convertToImageMapArea($this, $data[1], $pos); 342 break; 343 } 344 case 'internallink': 345 case 'locallink': 346 case 'externallink': 347 case 'emaillink': 348 case 'windowssharelink': 349 if (is_array($data[1])) { 350 $title = $data[1]['title']; 351 } else { 352 $title = $data[1]; 353 } 354 $title = $this->_addArea($pos, $type, $title, $data[0]); 355 if (is_array($data[1])) { 356 $data[1]['title'] = $title; 357 } else { 358 $data[1] = $title; 359 } 360 break; 361 case 'interwikilink': 362 if (is_array($data[1])) { 363 $title = $data[1]['title']; 364 } else { 365 $title = $data[1]; 366 } 367 $title = $this->_addArea($pos, $type, $title, $data[3], $data[2]); 368 if (is_array($data[1])) { 369 $data[1]['title'] = $title; 370 } else { 371 $data[1] = $title; 372 } 373 break; 374 } 375 } 376 } 377 378} 379 380//Setup VIM: ex: et ts=4 : 381