xref: /dokuwiki/inc/Parsing/ParserMode/GfmMedia.php (revision 685560eb3044321b3bdd0be40985871ced5f1d05)
1<?php
2
3namespace dokuwiki\Parsing\ParserMode;
4
5use dokuwiki\Parsing\Handler;
6use dokuwiki\Parsing\Helpers\Media as MediaHelper;
7
8/**
9 * GFM inline image ![alt](url) with optional title ![alt](url "title").
10 *
11 * Emits the same internalmedia/externalmedia handler calls as DokuWiki's
12 * {{...}} media mode so renderers, indexers, and reverse renderers need no
13 * changes. Width, height, cache, linking, and alignment directives are
14 * accepted via the same URL-parameter vocabulary as DW media
15 * (?100x200&nolink&recache, ?right, ?center, etc.) through shared parsing
16 * in Helpers\Media::parseParameters() — the last `?` in the URL delimits
17 * the DW parameter block, so query-bearing URLs like
18 * https://example.com/img?v=2?100x100&right still work. GFM has no native
19 * alignment syntax, so the `?left`/`?right`/`?center` keywords are the
20 * canonical way to align an inline GFM image.
21 *
22 * Deliberately not supported (see skip.php for the affected spec examples):
23 *
24 *   - Reference-style images ![text][id] / ![text][] / ![foo] — the
25 *     single-pass lexer cannot resolve forward references to [foo]: url
26 *     definitions.
27 *   - Pointy-bracket destinations ![alt](<foo bar>) — rarely used.
28 *   - Nested brackets in alt text (![foo ![bar](x)](y), ![foo [bar](x)](y))
29 *     — leftmost-match cannot reorder; outer falls back to literal.
30 *   - Title HTML attribute — DokuWiki media instructions have no separate
31 *     title-attribute slot (alt is used as the caption). The title parses
32 *     cleanly but is discarded.
33 *   - Mixed syntax: ![alt](url) inside [[dw|link]] or {{dw|media}} inside
34 *     [gfm](link) — cross-syntax nesting is out of scope.
35 */
36class GfmMedia extends AbstractMode
37{
38    /** @inheritdoc */
39    public function getSort()
40    {
41        return 310;
42    }
43
44    /** @inheritdoc */
45    public function connectTo($mode)
46    {
47        // Outer shape: `![alt](url)`. Alt class forbids brackets and
48        // newlines; URL slot is permissive (`[^)\n]+`) — handle() does
49        // URL / title splitting post-entry, mirroring how GfmLink and DW
50        // Internallink work.
51        $this->Lexer->addSpecialPattern('!\[[^\[\]\n]*\]\([^)\n]+\)', $mode, 'gfm_media');
52    }
53
54    /** @inheritdoc */
55    public function handle($match, $state, $pos, Handler $handler)
56    {
57        $sep    = strpos($match, '](');
58        $alt    = substr($match, 2, $sep - 2);
59        $inside = trim(substr($match, $sep + 2, -1));
60        $url    = substr($inside, 0, strcspn($inside, " \t\n"));
61
62        $p = MediaHelper::parseParameters($url);
63
64        $call = (media_isexternal($p['src']) || link_isinterwiki($p['src']))
65            ? 'externalmedia'
66            : 'internalmedia';
67
68        $handler->addCall(
69            $call,
70            [$p['src'], $alt !== '' ? $alt : null, $p['align'], $p['width'], $p['height'], $p['cache'], $p['linking']],
71            $pos
72        );
73        return true;
74    }
75}
76