PageRenderTime 54ms CodeModel.GetById 29ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/widget/opWidgetFormRichTextareaOpenPNE.class.php

https://github.com/Kazuhiro-Murota/OpenPNE3
PHP | 372 lines | 304 code | 47 blank | 21 comment | 34 complexity | 18f3a05d0bd24c85d006e2552ca833ea MD5 | raw file
  1. <?php
  2. /**
  3. * This file is part of the OpenPNE package.
  4. * (c) OpenPNE Project (http://www.openpne.jp/)
  5. *
  6. * For the full copyright and license information, please view the LICENSE
  7. * file and the NOTICE file that were distributed with this source code.
  8. */
  9. /**
  10. * opWidgetFormRichTextareaOpenPNE
  11. *
  12. * @package OpenPNE
  13. * @subpackage widget
  14. * @author Shogo Kawahara <kawahara@ejimaya.net>
  15. */
  16. class opWidgetFormRichTextareaOpenPNE extends opWidgetFormRichTextarea
  17. {
  18. static protected $isFirstRenderOpenPNE = true;
  19. static protected $isConfiguredTinyMCE = false;
  20. static protected $plugins = array('inlinepopups', 'openpne');
  21. static protected $buttons = array('op_b', 'op_u', 'op_s', 'op_i', 'op_large', 'op_small', 'op_color', 'op_emoji_docomo');
  22. static protected $buttonOnclickActions = array(
  23. 'op_emoji_docomo' => 'opEmoji.getInstance("%id%").togglePallet("epDocomo");',
  24. 'op_large' => 'op_mce_insert_tagname("%id%", "op:font", \' size="5"\');',
  25. 'op_small' => 'op_mce_insert_tagname("%id%", "op:font", \' size="1"\');',
  26. 'op_color' => 'op_mce_show_color_table("%id%", "op:font");'
  27. );
  28. static protected $convertCallbackList = array(
  29. 'op:color' => array(__CLASS__, 'opColorToHtml'),
  30. 'op:font' => array(__CLASS__, 'opFontToHtml')
  31. );
  32. static protected $htmlConvertList = array(
  33. 'op:b' => array('b'),
  34. 'op:u' => array('u'),
  35. 'op:i' => array('i'),
  36. 'op:s' => array('s'),
  37. 'op:large' => array('font', array('size' => 5)),
  38. 'op:small' => array('font', array('size' => 1)),
  39. );
  40. static protected $extensions = array();
  41. protected $tinyMCEConfigs = array(
  42. 'mode' => 'textareas',
  43. 'theme' => 'advanced',
  44. 'editor_selector' => 'mceEditor_dummy_selector',
  45. 'plugins' => '',
  46. 'theme_advanced_toolbar_location' => 'top',
  47. 'theme_advanced_toolbar_align' => 'left',
  48. 'theme_advanced_buttons1' => '',
  49. 'theme_advanced_buttons2' => '',
  50. 'theme_advanced_buttons3' => '',
  51. 'valid_elements' => 'b/strong,u,s/strike,i,font[color|size],span[style],br',
  52. 'forced_root_block' => false,
  53. 'force_p_newlines' => false,
  54. 'force_br_newlines' => true,
  55. 'inline_styles' => false,
  56. 'entity_encoding' => 'raw',
  57. 'remove_linebreaks' => false,
  58. 'custom_undo_redo_levels' => 0,
  59. 'custom_undo_redo' => false,
  60. 'convert_fonts_to_spans' => true,
  61. );
  62. protected $loadPluginList = array();
  63. static public function addExtension($extension)
  64. {
  65. self::$extensions[] = $extension;
  66. }
  67. public function __construct($options = array(), $attributes = array())
  68. {
  69. parent::__construct($options, $attributes);
  70. sfProjectConfiguration::getActive()->loadHelpers('Asset');
  71. foreach (self::$extensions as $extension)
  72. {
  73. if (!self::$isConfiguredTinyMCE)
  74. {
  75. self::$plugins = array_merge(self::$plugins, call_user_func(array($extension, 'getPlugins')));
  76. self::$buttons = array_merge_recursive(self::$buttons, call_user_func(array($extension, 'getButtons')));
  77. self::$buttonOnclickActions = array_merge(self::$buttonOnclickActions, call_user_func(array($extension, 'getButtonOnClickActions')));
  78. self::$convertCallbackList = array_merge(self::$convertCallbackList, call_user_func(array($extension, 'getConvertCallbacks')));
  79. self::$htmlConvertList = array_merge(self::$htmlConvertList, call_user_func(array($extension, 'getHtmlConverts')));
  80. }
  81. call_user_func_array(array($extension, 'configure'), array(&$this->tinyMCEConfigs));
  82. }
  83. if (!empty($this->tinyMCEConfigs['plugins']))
  84. {
  85. $this->tinyMCEConfigs['plugins'] .= ',';
  86. }
  87. $plugins = array();
  88. foreach (self::$plugins as $name => $path)
  89. {
  90. if (is_numeric($name))
  91. {
  92. $plugins[] = $path;
  93. }
  94. else
  95. {
  96. $plugins[] = '-'.$name;
  97. $this->loadPluginList[$name] = $path;
  98. }
  99. }
  100. $this->tinyMCEConfigs['plugins'] .= implode(',', $plugins);
  101. if (!empty($this->tinyMCEConfigs['theme_advanced_buttons1']))
  102. {
  103. $this->tinyMCEConfigs['theme_advanced_buttons1'] .= ',';
  104. }
  105. $buttons = array();
  106. foreach (self::$buttons as $key => $button)
  107. {
  108. if (is_numeric($key))
  109. {
  110. $buttons[] = $button;
  111. }
  112. else
  113. {
  114. $buttons[] = $key;
  115. }
  116. }
  117. $this->tinyMCEConfigs['theme_advanced_buttons1'] .= implode(',', $buttons);
  118. self::$isConfiguredTinyMCE = true;
  119. }
  120. public function render($name, $value = null, $attributes = array(), $errors = array())
  121. {
  122. if (sfConfig::get('sf_app') == 'mobile_frontend')
  123. {
  124. return parent::render($name, $value, $attributes, $errors);
  125. }
  126. $js = '';
  127. foreach (self::$buttons as $key => $button)
  128. {
  129. if (is_numeric($key))
  130. {
  131. $buttonName = $button;
  132. $buttonConfig = array('isEnabled' => 1, 'imageURL' => image_path('deco_'.$buttonName.'.gif'));
  133. }
  134. else
  135. {
  136. $buttonName = $key;
  137. $buttonConfig = $button;
  138. }
  139. $config[$buttonName] = $buttonConfig;
  140. }
  141. if (self::$isFirstRenderOpenPNE)
  142. {
  143. sfProjectConfiguration::getActive()->loadHelpers('Partial');
  144. sfContext::getInstance()->getResponse()->addJavascript('/sfProtoculousPlugin/js/prototype');
  145. sfContext::getInstance()->getResponse()->addJavascript('op_emoji');
  146. sfContext::getInstance()->getResponse()->addJavascript('Selection');
  147. sfContext::getInstance()->getResponse()->addJavascript('decoration');
  148. $relativeUrlRoot = sfContext::getInstance()->getRequest()->getRelativeUrlRoot();
  149. foreach ($this->loadPluginList as $key => $path)
  150. {
  151. $js .= sprintf('tinymce.PluginManager.load("%s", "%s");'."\n", $key, $path);
  152. }
  153. $js .= sprintf("function op_mce_editor_get_config() { return %s; }\n", json_encode($config));
  154. $js .= sprintf('function op_get_relative_uri_root() { return "%s"; }', $relativeUrlRoot);
  155. self::$isFirstRenderOpenPNE = false;
  156. }
  157. if ($js)
  158. {
  159. sfProjectConfiguration::getActive()->loadHelpers('Javascript');
  160. $js = javascript_tag($js);
  161. }
  162. $id = $this->getId($name, $attributes);
  163. $this->setOption('textarea_template', '<div id="'.$id.'_buttonmenu" class="'.$id.'">'
  164. .get_partial('global/richTextareaOpenPNEButton', array(
  165. 'id' => $id,
  166. 'configs' => $config,
  167. 'onclick_actions' => self::$buttonOnclickActions
  168. )).
  169. '</div>'.$this->getOption('textarea_template'));
  170. return $js.parent::render($name, $value, $attributes, $errors);
  171. }
  172. /**
  173. * original tag to html
  174. *
  175. * @param string $string
  176. * @param boolean $isStrip true if original tag is stripped from the string, false original tag convert html tag.
  177. * @param boolean $isUseStylesheet
  178. */
  179. static public function toHtml($string, $isStrip, $isUseStylesheet)
  180. {
  181. new self();
  182. $regexp = '/(?:&lt;|<)(\/?)(op:.+?)(?:\s+(.*?))?(?:&gt;|>)/i';
  183. if ($isStrip)
  184. {
  185. $converted = preg_replace($regexp, '', $string);
  186. }
  187. else
  188. {
  189. if ($isUseStylesheet)
  190. {
  191. $converted = preg_replace_callback($regexp, array(__CLASS__, 'toHtmlUseStylesheet'), $string);
  192. }
  193. else
  194. {
  195. $converted = preg_replace_callback($regexp, array(__CLASS__, 'toHtmlNoStylesheet'), $string);
  196. }
  197. }
  198. return $converted;
  199. }
  200. static protected function getHtmlAttribute($matches)
  201. {
  202. $result = array();
  203. if (count($matches) <= 3)
  204. {
  205. return $result;
  206. }
  207. preg_match_all('/([^\s]*?)=(?:&quot;|")(.*?)(?:&quot;|")/', $matches[3], $matchAttributes);
  208. for ($i = 0; count($matchAttributes[0]) > $i; $i++)
  209. {
  210. $result[$matchAttributes[1][$i]] = $matchAttributes[2][$i];
  211. }
  212. return $result;
  213. }
  214. static public function toHtmlUseStylesheet($matches)
  215. {
  216. $isEndtag = $matches[1];
  217. $tagname = strtolower($matches[2]);
  218. $attributes = self::getHtmlAttribute($matches);
  219. if (isset(self::$convertCallbackList[$tagname]))
  220. {
  221. return call_user_func(self::$convertCallbackList[$tagname], $isEndtag, $tagname, $attributes, true);
  222. }
  223. $options = array();
  224. $options['class'] = strtr($tagname, ':', '_');
  225. if ($isEndtag) {
  226. return '</span>';
  227. }
  228. return tag('span', $options, true);
  229. }
  230. static public function toHtmlNoStylesheet($matches)
  231. {
  232. $isEndtag = $matches[1];
  233. $tagname = strtolower($matches[2]);
  234. $attributes = self::getHtmlAttribute($matches);
  235. if (isset(self::$convertCallbackList[$tagname]))
  236. {
  237. return call_user_func(self::$convertCallbackList[$tagname], $isEndtag, $tagname, $attributes, false);
  238. }
  239. $options = array();
  240. if (!array_key_exists($tagname, self::$htmlConvertList)) {
  241. return '';
  242. }
  243. $htmlTagInfo = self::$htmlConvertList[$tagname];
  244. $htmlTagName = $htmlTagInfo[0];
  245. if ($isEndtag) {
  246. return '</' . $htmlTagName . '>';
  247. }
  248. if (isset($htmlTagInfo[1]) && is_array($htmlTagInfo[1]))
  249. {
  250. $options = array_merge($options, $htmlTagInfo[1]);
  251. }
  252. return tag($htmlTagName, $options, true);
  253. }
  254. static public function opColorToHtml($isEndtag, $tagname, $attributes, $isUseStylesheet)
  255. {
  256. $options = array();
  257. if ($isUseStylesheet)
  258. {
  259. if ($isEndtag) {
  260. return '</span>';
  261. }
  262. $options['class'] = strtr($tagname, ':', '_');
  263. if (isset($attributes['code'])) {
  264. $options['style'] = 'color:'.$attributes['code'];
  265. }
  266. return tag('span', $options, true);
  267. }
  268. else
  269. {
  270. if ($isEndtag)
  271. {
  272. return '</font>';
  273. }
  274. if (isset($attributes['code'])) {
  275. $options['color'] = $attributes['code'];
  276. }
  277. return tag('font', $options, true);
  278. }
  279. }
  280. static public function opFontToHtml($isEndtag, $tagname, $attributes, $isUseStylesheet)
  281. {
  282. $options = array();
  283. if ($isUseStylesheet)
  284. {
  285. if ($isEndtag) {
  286. return '</span>';
  287. }
  288. $options['class'] = 'op_font';
  289. $options['style'] = '';
  290. if (isset($attributes['color'])) {
  291. $options['style'] .= 'color:'.$attributes['color'].';';
  292. }
  293. $size = isset($attributes['size']) ? (int)$attributes['size'] : 0;
  294. $fontSizeMap = array(
  295. 1 => 'xx-small',
  296. 2 => 'x-small',
  297. 3 => 'small',
  298. 4 => 'medium',
  299. 5 => 'large',
  300. 6 => 'x-large',
  301. 7 => 'xx-large'
  302. );
  303. if (isset($fontSizeMap[$size])) {
  304. $options['style'] .= 'font-size:'.$fontSizeMap[$size];
  305. }
  306. return tag('span', $options, true);
  307. }
  308. else
  309. {
  310. if ($isEndtag)
  311. {
  312. return '</font>';
  313. }
  314. if (isset($attributes['color'])) {
  315. $options['color'] = $attributes['color'];
  316. }
  317. $size = isset($attributes['size']) ? (int)$attributes['size'] : 0;
  318. if ($size >= 1 && $size <= 7)
  319. {
  320. $options['size'] = $attributes['size'];
  321. }
  322. return tag('font', $options, true);
  323. }
  324. }
  325. }