1<?php 2/** 3 * Keyboard Syntax Plugin: Marks text as keyboard key presses. 4 * 5 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 6 * @author Gina Haeussge <osd@foosel.net> 7 * @author Christopher Arndt 8 */ 9 10/** 11 * All DokuWiki plugins to extend the parser/rendering mechanism 12 * need to inherit from this class 13 */ 14class syntax_plugin_keyboard extends DokuWiki_Syntax_Plugin { 15 protected $lastClass = null; 16 protected $styles = array ('__keyboard' => array ('display-name' => 'Keyboard', 17 'name' => null), 18 '__keyboard_keypress' => array ('display-name' => 'Keypress', 19 'name' => null)); 20 protected $stylesCreated = false; 21 22 function getType() { return 'formatting'; } 23 24 function getAllowedTypes() { 25 return array('formatting', 'substition', 'disabled'); 26 } 27 28 function getSort(){ return 444; } 29 30 function connectTo($mode) { 31 $this->Lexer->addEntryPattern('<key class="[^"]*">', $mode, 'plugin_keyboard'); 32 $this->Lexer->addEntryPattern('<kbd class="[^"]*">', $mode, 'plugin_keyboard'); 33 $this->Lexer->addEntryPattern('<key>', $mode, 'plugin_keyboard'); 34 $this->Lexer->addEntryPattern('<kbd>', $mode, 'plugin_keyboard'); 35 } 36 37 function postConnect() { 38 $this->Lexer->addExitPattern('</key>', 'plugin_keyboard'); 39 $this->Lexer->addExitPattern('</kbd>', 'plugin_keyboard'); 40 } 41 42 /** 43 * Handle the match 44 */ 45 function handle($match, $state, $pos, Doku_Handler $handler) { 46 switch ($state) { 47 case DOKU_LEXER_ENTER : 48 if (preg_match('/class="[^"]*"/', $match, $classString) === 1) { 49 $class = substr($classString[0], 6); 50 $class = trim($class, '"'); 51 } else { 52 $class = $this->getConf('css_class'); 53 } 54 $this->lastClass = $class; 55 return array($state, '', $this->lastClass); 56 case DOKU_LEXER_UNMATCHED : 57 $length = strlen($match); 58 if ($length > 1 && 59 !($match[0] == "'" && $match[$length-1] == "'")) { 60 $mpos = strpos($match, '-'); 61 $ppos = strpos($match, '+'); 62 if(!$mpos) 63 $separator = '+'; 64 else if(!$ppos) 65 $separator = '-'; 66 else 67 $separator = substr($match,($mpos<$ppos)?$mpos:$ppos, 1); 68 $keys = explode($separator, $match); 69 $keys = array_map('trim', $keys); 70 } else { 71 $keys = array($match); 72 } 73 return array($state, $keys, $this->lastClass); 74 case DOKU_LEXER_EXIT: 75 return array($state, '', ''); 76 } 77 return array($state, '', ''); 78 } 79 80 /** 81 * Create output 82 */ 83 function render($mode, Doku_Renderer $renderer, $data) { 84 if ($mode == 'xhtml') { 85 list($state, $match, $class) = $data; 86 switch ($state) { 87 case DOKU_LEXER_ENTER : 88 if (empty($class)) { 89 $renderer->doc .= '<kbd>'; 90 } else { 91 $renderer->doc .= '<kbd class="'.$class.'">'; 92 } 93 break; 94 case DOKU_LEXER_UNMATCHED : 95 foreach ($match as $key) { 96 if ($this->getConf('disable_translation')) { 97 $out[] = $renderer->_xmlEntities($key); 98 } else if (substr($key, 0, 1) == "'" and 99 substr($key, -1, 1) == "'" and 100 strlen($key) > 1) { 101 $out[] = $renderer->_xmlEntities(substr($key,1,-1)); 102 } else { 103 $subst = $this->getLang($key); 104 if ($subst) { 105 $out[] = $subst; 106 } else { 107 $out[] = $renderer->_xmlEntities(ucfirst($key)); 108 } 109 } 110 } 111 if (empty($class)) { 112 $renderer->doc .= implode('</kbd>+<kbd>', $out); 113 } else { 114 $renderer->doc .= implode('</kbd>+<kbd class="'.$class.'">', $out); 115 } 116 break; 117 case DOKU_LEXER_EXIT : 118 $renderer->doc .= '</kbd>'; 119 break; 120 } 121 return true; 122 } 123 if ($mode == 'odt') { 124 list($state, $match, $class) = $data; 125 switch ($state) { 126 case DOKU_LEXER_ENTER : 127 if ($this->stylesCreated == false || !array_key_exists ($class, $this->styles)) { 128 $this->createODTStyles($renderer, $class); 129 } 130 $this->renderODTOpenSpan($renderer, $this->styles[$class]['name']); 131 break; 132 case DOKU_LEXER_UNMATCHED : 133 foreach ($match as $key) { 134 if ($this->getConf('disable_translation')) { 135 $out[] = $key; 136 } else if (substr($key, 0, 1) == "'" and 137 substr($key, -1, 1) == "'" and 138 strlen($key) > 1) { 139 $out[] = substr($key,1,-1); 140 } else { 141 $subst = $this->getLang($key); 142 if ($subst) { 143 $out[] = $subst; 144 } else { 145 $out[] = ucfirst($key); 146 } 147 } 148 } 149 $max = count($out); 150 for ($index = 0 ; $index < $max ; $index++) { 151 $renderer->cdata ($out [$index]); 152 if ($index+1 < $max) { 153 $this->renderODTCloseSpan($renderer); 154 $renderer->cdata ('+'); 155 $this->renderODTOpenSpan($renderer, $this->styles[$class]['name']); 156 } 157 } 158 break; 159 case DOKU_LEXER_EXIT : 160 $this->renderODTCloseSpan($renderer); 161 break; 162 } 163 return true; 164 } 165 return false; 166 } 167 168 protected function createODTStyles (Doku_Renderer $renderer, $class = null) { 169 if ( method_exists ($renderer, 'getODTPropertiesFromElement') === true ) { 170 // Create parent style to group the others beneath it 171 if (!$renderer->styleExists('Plugin_Keyboard')) { 172 $parent_properties = array(); 173 $parent_properties ['style-parent'] = NULL; 174 $parent_properties ['style-class'] = 'Plugin Keyboard'; 175 $parent_properties ['style-name'] = 'Plugin_Keyboard'; 176 $parent_properties ['style-display-name'] = 'Plugin Keyboard'; 177 $renderer->createTextStyle($parent_properties); 178 } 179 180 if ($this->stylesCreated === false) { 181 $this->stylesCreated = true; 182 foreach ($this->styles as $class => $style) { 183 // Get CSS properties for ODT export. 184 // Set parameter $inherit=false to prevent changiung the font-size and family! 185 $properties = array(); 186 $renderer->getODTPropertiesNew ($properties, 'kbd', 'class="'.$class.'"', NULL, false); 187 if ($properties['font-family'] == 'inherit') { 188 unset ($properties['font-family']); 189 } 190 191 $style_name = 'Plugin_Keyboard_'.$class; 192 if (!$renderer->styleExists($style_name)) { 193 $this->styles[$class]['name'] = $style_name; 194 $properties ['style-parent'] = 'Plugin_Keyboard'; 195 $properties ['style-class'] = NULL; 196 $properties ['style-name'] = $style_name; 197 $properties ['style-display-name'] = $style['display-name']; 198 $renderer->createTextStyle($properties); 199 } 200 } 201 } 202 203 if (!empty($class) && !array_key_exists($class, $this->styles)) { 204 // Get CSS properties for ODT export. 205 // Set parameter $inherit=false to prevent changiung the font-size and family! 206 $properties = array(); 207 $renderer->getODTPropertiesNew ($properties, 'kbd', 'class="'.$class.'"', NULL, false); 208 if ($properties['font-family'] == 'inherit') { 209 unset ($properties['font-family']); 210 } 211 212 $style_name = 'Plugin_Keyboard_'.$class; 213 if (!$renderer->styleExists($style_name)) { 214 $display_name = ucfirst(trim($class, '_')); 215 $new = array ('name' => $style_name, 'display-name' => $display_name); 216 $this->styles[$class] = $new; 217 218 $properties ['style-parent'] = 'Plugin_Keyboard'; 219 $properties ['style-class'] = NULL; 220 $properties ['style-name'] = $style_name; 221 $properties ['style-display-name'] = $display_name; 222 $renderer->createTextStyle($properties); 223 } 224 } 225 } 226 } 227 228 protected function renderODTOpenSpan ($renderer, $class) { 229 if ( method_exists ($renderer, '_odtSpanOpen') === false ) { 230 // Function is not supported by installed ODT plugin version, return. 231 return; 232 } 233 $renderer->_odtSpanOpen($class); 234 } 235 236 protected function renderODTCloseSpan ($renderer) { 237 if ( method_exists ($renderer, '_odtSpanClose') === false ) { 238 // Function is not supported by installed ODT plugin version, return. 239 return; 240 } 241 $renderer->_odtSpanClose(); 242 } 243} 244