xref: /plugin/autotooltip/helper.php (revision 4cbd2578e15d5abe799472d234291cb1237d1880)
1<?php
2if(!defined('DOKU_INC')) die();
3
4/**
5 * Auto-Tooltip DokuWiki plugin
6 *
7 * @license    MIT
8 * @author     Eli Fenton
9 */
10class helper_plugin_autotooltip extends DokuWiki_Admin_Plugin {
11	private $localRenderer;
12
13	public function __construct() {
14		$this->localRenderer = new Doku_Renderer_xhtml;
15	}
16
17
18	/**
19	 * Return methods of this helper
20	 *
21	 * @return array with methods description
22	 */
23	function getMethods() {
24		$result = array();
25		$result[] = array(
26			'name' => 'forText',
27			'desc' => 'Manually construct a tooltip',
28			'params' => array(
29				'content' => 'string',
30				'tooltip' => 'string',
31				'title (optional)' => 'string',
32				'preTitle (optional)' => 'string',
33				'classes (optional)' => 'string',
34				'textStyles (optional)' => 'string',
35			),
36			'return' => array('result' => 'string')
37		);
38		$result[] = array(
39			'name' => 'forWikilink',
40			'desc' => 'Generate a tooltip from a wikilink',
41			'params' => array(
42				'id' => 'string',
43				'content (optional)' => 'string',
44				'preTitle (optional)' => 'string',
45				'classes (optional)' => 'string',
46				'textStyles (optional)' => 'string',
47			),
48			'return' => array('result' => 'string')
49		);
50		return $result;
51	}
52
53
54	/**
55	 * Return a simple tooltip.
56	 *
57	 * @param string $content - The on-page content. May contain newlines.
58	 * @param string $tooltip - The tooltip content. Newlines will be rendered as line breaks.
59	 * @param string $title - The title inside the tooltip.
60	 * @param string $preTitle - Text to display before the title. Newlines will be rendered as line breaks.
61	 * @param string $classes - CSS classes to add to this tooltip.
62	 * @param string $textStyle - CSS styles for the linked content
63	 * @return string
64	 */
65	function forText($content, $tooltip, $title='', $preTitle = '', $classes = '', $textStyle = '') {
66		if (empty($classes)) {
67			$classes = $this->getConf('style');
68		}
69		if (empty($classes)) {
70			$classes = 'default';
71		}
72		$delay = $this->getConf('delay') ?: 0;
73
74		// Sanitize
75		$classes = htmlspecialchars($classes);
76		// Add the plugin prefix to all classes.
77		$classes = preg_replace('/(\w+)/', 'plugin-autotooltip__$1', $classes);
78
79		$partCount = (empty($title) ? 0 : 1) + (empty($preTitle) ? 0 : 1) + (empty($tooltip) ? 0 : 1);
80		if ($partCount > 1 || strchr($tooltip, "\n") !== FALSE || strlen($tooltip) > 40) {
81			$classes .= ' plugin-autotooltip_big';
82		}
83
84		$textClass = '';
85		if (empty($textStyle)) {
86			$textClass = 'plugin-autotooltip_linked';
87			if (strstr($content, '<a ') === FALSE) {
88				$textClass .= ' plugin-autotooltip_simple';
89			}
90		}
91
92		$contentParts = [];
93		if (!empty($preTitle)) {
94			$contentParts[] = $this->_formatTT($preTitle);
95		}
96		if (!empty($title)) {
97			$contentParts[] = '<span class="plugin-autotooltip-title">' . $title . '</span>';
98		}
99		if (!empty($tooltip)) {
100			$contentParts[] = $this->_formatTT($tooltip);
101		}
102
103		return '<span class="' . $textClass . '" style="' . $textStyle . '" onmouseover="autotooltip.show(this)" onmouseout="autotooltip.hide()" data-delay="' . $delay . '">' .
104			$content .
105			'<span class="plugin-autotooltip-hidden-classes">' . $classes . '</span>' .
106			'<span class="plugin-autotooltip-hidden-tip">' .
107			implode('<br><br>', $contentParts) .
108			'</span>' .
109		'</span>';
110	}
111
112
113	/**
114	 * Render a tooltip, with the title and abstract of a page.
115	 *
116	 * @param string $id - A page id.
117	 * @param string $content - The on-page content. Newlines will be rendered as line breaks. Omit to use the page's title.
118	 * @param string $preTitle - Text to display before the title in the tooltip. Newlines will be rendered as line breaks.
119	 * @param string $classes - CSS classes to add to this tooltip.
120	 * @param string $linkStyle - Style attribute for the link.
121	 * @return string
122	 */
123	function forWikilink($id, $content = null, $preTitle = '', $classes = '', $linkStyle = '') {
124		$title = p_get_metadata($id, 'title');
125
126		$link = $this->localRenderer->internallink($id, $content ?: $title, null, true);
127
128		if (!empty($linkStyle)) {
129			$link = preg_replace('/<a /', '<a style="' . $linkStyle . '" ', $link);
130		}
131
132		if (page_exists($id)) {
133			$abstract = $this->getAbstract($id, $title);
134			$link = $this->stripNativeTooltip($link);
135			return $this->forText($link, $abstract, $title, $preTitle, $classes);
136		}
137		else {
138			return $link;
139		}
140	}
141
142
143	/**
144	 * Get a formatted abstract.
145	 *
146	 * @param string $id
147	 * @param string $title
148	 * @return string
149	 */
150	function getAbstract($id, $title) {
151		// Check the "description" plugin.
152		$abstract = p_get_metadata($id, 'plugin_description keywords');
153		// Default doku abstract is the first part of the page.
154		if (empty($abstract)) {
155			$abstract = p_get_metadata($id, 'description abstract');
156		}
157
158
159		// By default, the abstract starts with the title. Remove it so it's not displayed twice, but still fetch
160		// both pieces of metadata, in case another plugin rewrote the abstract.
161		return preg_replace('/^' . $this->_pregEscape($title) . '(\r?\n)+/', '', $abstract);
162	}
163
164
165	/**
166	 * Strip the native title= tooltip from an anchor tag.
167	 *
168	 * @param string $link
169	 * @return string
170	 */
171	function stripNativeTooltip($link) {
172		return preg_replace('/title="[^"]*"/', '', $link);
173	}
174
175
176	/**
177	 * Format tooltip text.
178	 *
179	 * @param string $tt - Tooltip text.
180	 * @return string
181	 */
182	private function _formatTT($tt) {
183		// Convert double-newlines into vertical space.
184		$tt = preg_replace('/(\r?\n){2,}/', '<br><br>', $tt);
185		// Single newlines get collapsed, just like in HTML.
186		return preg_replace('/(\r?\n)/', ' ', $tt);
187	}
188
189
190	/**
191	 * Escape a string for inclusion in a regular expression, assuming forward slash is used as the delimiter.
192	 *
193	 * @param string $r - The regex string, without delimiters.
194	 * @return string
195	 */
196	private function _pregEscape($r) {
197		return preg_replace('/\//', '\\/', preg_quote($r));
198	}
199}
200