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