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