1<?php 2/** 3 * EmojiOne extension (Helper Component) 4 * 5 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html 6 * @author Patrick Brown <ptbrown@whoopdedo.org></ptbrown> 7 */ 8 9// must be run within Dokuwiki 10if(!defined('DOKU_INC')) die(); 11 12require_once 'emojione/RulesetInterface.php'; 13require_once 'emojione/ClientInterface.php'; 14require_once 'emojione/Client.php'; 15require_once 'emojione/Ruleset.php'; 16 17class dwplugin_emoji_ruleset extends Emojione\Ruleset { 18 protected static $smileys = array( 19 '8-O' => '1F62F', 20 '8-o' => '1F62F', 21 ':-\\' => '1F615', 22 ':-?' => '1F616', 23 ':-|' => '1F601', 24 '^_^' => '1F604', 25 ':?:' => '2753', 26 ':!:' => '26A0', 27 ); 28 29 public function __construct() { 30 $this->ascii_replace = array_merge($this->ascii_replace, static::$smileys); 31 $smileys = array_keys($this->ascii_replace); 32 $this->asciiRegexp = '(?:'.join('|',array_map('preg_quote_cb', $smileys)).')'; 33 /* Inserts smileys into the shortcode list so I can use a single callback to handle both. */ 34 $this->shortcode_replace = array_merge($this->shortcode_replace, $this->ascii_replace); 35 } 36 37 public function isShortCode($match) { 38 return isset($this->shortcode_replace[$match]); 39 } 40 41 public function replaceShortCode($match) { 42 return $this->shortcode_replace[$match]; 43 } 44} 45 46class syntax_plugin_emoji extends DokuWiki_Syntax_Plugin { 47 48 /** 49 * Match emoji code points: 50 * - Most characters followed by variant selector 16 51 * - No characters followed by variant selector 15 52 * - Characters above U+1F000 53 * - Numbers with combining keycap U+20E3 54 * - Miscellaneous Technical 55 * - Control Pictures 56 * - Miscellaneous Symbols and Dingbats 57 * - Miscellaneous Symbols and Arrows 58 */ 59 public $unicodeRegexp = '(?:[*#0-9](?>\\xEF\\xB8\\x8F)?\\xE2\\x83\\xA3(?!\\xEF\\xB8\\x8E)|[*#0-9]\\xEF\\xB8\\x8F|\\xC2[\\xA9\\xAE]\\xEF\\xB8\\x8F|\\xE2..(?>\\xF0\\x9F\\x8F[\\xBB-\\xBF])?\\xEF\\xB8\\x8F|\\xE2[\\x8C-\\x90\\x98-\\x9E\\xAC-\\xAF].(?>\\xF0\\x9F\\x8F[\\xBB-\\xBF])?(?!\\xEF\\xB8\\x8E)|\\xE3(?>\\x80[\\xB0\\xBD]|\\x8A[\\x97\\x99])\\xEF\\xB8\\x8F|\\xF0\\x9F(?>\\x87.\\xF0\\x9F\\x87.|..(?>\\xEF\\xB8\\x8F)?)(?!\\xEF\\xB8\\x8E))'; 60 61 protected $client; 62 protected $ruleset; 63 64 public function __construct() { 65 $this->ruleset = new dwplugin_emoji_ruleset(); 66 67 $this->client = new Emojione\Client($this->ruleset); 68 $this->client->unicodeAlt = true; 69 $this->client->sprites = false; 70 $this->client->imageType = 'png'; 71 $assetsrc = DOKU_BASE.'lib/plugins/emoji/'; 72 switch($this->getConf('assetsrc')) { 73 case 'cdn': 74 $assetsrc = 'https://cdn.jsdelivr.net/emojione/'; 75 break; 76 case 'external': 77 /* really should be called "asseturl", oops. Too late now */ 78 $asseturi = $this->getConf('asseturi'); 79 if($asseturi) 80 $assetsrc = $asseturi; 81 break; 82 } 83 $this->client->imagePathPNG = $assetsrc.'assets/png/'; 84 } 85 86 public function getType() { 87 return 'substition'; 88 } 89 90 public function getSort() { 91 return 229; 92 } 93 94 public function connectTo($mode) { 95 $this->Lexer->addSpecialPattern($this->getUnicodeRegexp(), $mode, 'plugin_emoji'); 96 $this->Lexer->addSpecialPattern('(?<=\W|^)'.$this->getShortnameRegexp().'(?=\W|$)', $mode, 'plugin_emoji'); 97 $this->Lexer->addSpecialPattern('(?<=\W|^)'.$this->getSmileyRegexp().'(?=\W|$)', $mode, 'plugin_emoji'); 98 } 99 100 public function handle($match, $state, $pos, Doku_Handler $handler) { 101 /* Clean up variant selector, I don't trust the library to do this. */ 102 $match = str_replace("\xEF\xB8\x8F", "", $match); 103 $unicode = $this->toUnicode($match); 104 return array($match,$unicode); 105 } 106 107 public function render($mode, Doku_Renderer $renderer, $data) { 108 list($match,$unicode) = $data; 109 switch($mode) { 110 case 'xhtml': 111 if($this->ruleset->isShortCode($match)) 112 $renderer->doc .= $this->shortnameToImage($match); 113 else 114 $renderer->doc .= $this->unicodeToImage($unicode); 115 break; 116 case 'odt': 117 if($this->ruleset->isShortCode($match)) 118 $link = $this->shortnameToImage($match); 119 else 120 $link = $this->unicodeToImage($unicode); 121 122 if (preg_match('#[^/]\w*\.png#', $link, $matches) == 1) { 123 $path = DOKU_PLUGIN.'emoji/assets/png/'.$matches[0]; 124 list($width, $height) = $renderer->_odtGetImageSize ($path, '20', '20'); 125 $renderer->_odtAddImage($path, $width.'cm', $height.'cm'); 126 } 127 break; 128 default: 129 /* Adds the text variant selector */ 130 $renderer->cdata($unicode . "\xEF\xB8\x8E"); 131 break; 132 } 133 return true; 134 } 135 136 private function getUnicodeRegexp() { 137 return $this->unicodeRegexp; 138 } 139 140 private function getShortnameRegexp() { 141 return preg_replace('/\((?!\?)/', '(?:', $this->client->shortcodeRegexp); 142 } 143 144 private function getSmileyRegexp() { 145 return $this->ruleset->getAsciiRegexp(); 146 } 147 148 private function toUnicode($shortname) { 149 if(isset($this->smileys[$shortname])) { 150 $unicode = $this->smileys[$shortname]; 151 } 152 elseif($this->ruleset->isShortCode($shortname)) { 153 $unicode = $this->ruleset->replaceShortCode($shortname); 154 } 155 else { 156 return $shortname; 157 } 158 if(stristr($unicode,'-')) { 159 $pairs = explode('-',$unicode); 160 } 161 else { 162 $pairs = array($unicode); 163 } 164 return unicode_to_utf8(array_map('hexdec', $pairs)); 165 } 166 167 private function toShortname($unicode) { 168 return $this->client->toShortCallback(array($unicode,$unicode)); 169 } 170 171 private function shortnameToImage($shortname) { 172 return $this->client->shortnameToImageCallback(array($shortname,$shortname)); 173 } 174 175 private function unicodeToImage($unicode) { 176 return $this->client->unicodeToImageCallback(array($unicode,$unicode)); 177 } 178 179} 180