3 final class PhabricatorRemarkupControl
4 extends AphrontFormTextAreaControl
{
6 private $disableMacro = false;
7 private $disableFullScreen = false;
9 private $sendOnEnter = false;
10 private $remarkupMetadata = array();
12 public function setDisableMacros($disable) {
13 $this->disableMacro
= $disable;
17 public function setDisableFullScreen($disable) {
18 $this->disableFullScreen
= $disable;
22 public function setCanPin($can_pin) {
23 $this->canPin
= $can_pin;
27 public function getCanPin() {
31 public function setSendOnEnter($soe) {
32 $this->sendOnEnter
= $soe;
36 public function getSendOnEnter() {
37 return $this->sendOnEnter
;
40 public function setRemarkupMetadata(array $value) {
41 $this->remarkupMetadata
= $value;
45 public function getRemarkupMetadata() {
46 return $this->remarkupMetadata
;
49 public function setValue($value) {
50 if ($value instanceof RemarkupValue
) {
51 $this->setRemarkupMetadata($value->getMetadata());
52 $value = $value->getCorpus();
55 return parent
::setValue($value);
58 protected function renderInput() {
61 $id = celerity_generate_unique_node_id();
65 $viewer = $this->getUser();
67 throw new PhutilInvalidStateException('setUser');
70 // NOTE: Metadata is passed to Javascript in a structured way, and also
71 // dumped directly into the form as an encoded string. This makes it less
72 // likely that we'll lose server-provided metadata (for example, from a
73 // saved draft) if there is a client-side error.
75 $metadata_name = $this->getName().'_metadata';
76 $metadata_value = (object)$this->getRemarkupMetadata();
77 $metadata_string = phutil_json_encode($metadata_value);
79 $metadata_id = celerity_generate_unique_node_id();
80 $metadata_input = phutil_tag(
85 'name' => $metadata_name,
86 'value' => $metadata_string,
89 // We need to have this if previews render images, since Ajax can not
90 // currently ship JS or CSS.
91 require_celerity_resource('phui-lightbox-css');
93 if (!$this->getDisabled()) {
94 Javelin
::initBehavior(
95 'aphront-drag-and-drop-textarea',
98 'remarkupMetadataID' => $metadata_id,
99 'remarkupMetadataValue' => $metadata_value,
100 'activatedClass' => 'aphront-textarea-drag-and-drop',
101 'uri' => '/file/dropupload/',
102 'chunkThreshold' => PhabricatorFileStorageEngine
::getChunkThreshold(),
106 $root_id = celerity_generate_unique_node_id();
108 $user_datasource = new PhabricatorPeopleDatasource();
109 $emoji_datasource = new PhabricatorEmojiDatasource();
110 $proj_datasource = id(new PhabricatorProjectDatasource())
116 $phriction_datasource = new PhrictionDocumentDatasource();
117 $phurl_datasource = new PhabricatorPhurlURLDatasource();
119 Javelin
::initBehavior(
120 'phabricator-remarkup-assist',
123 'bold text' => pht('bold text'),
124 'italic text' => pht('italic text'),
125 'monospaced text' => pht('monospaced text'),
126 'List Item' => pht('List Item'),
127 'Quoted Text' => pht('Quoted Text'),
128 'data' => pht('data'),
129 'name' => pht('name'),
131 'key-help' => pht('Pin or unpin the comment form.'),
133 'canPin' => $this->getCanPin(),
134 'disabled' => $this->getDisabled(),
135 'sendOnEnter' => $this->getSendOnEnter(),
136 'rootID' => $root_id,
137 'autocompleteMap' => (object)array(
139 'datasourceURI' => $user_datasource->getDatasourceURI(),
140 'headerIcon' => 'fa-user',
141 'headerText' => pht('Find User:'),
142 'hintText' => $user_datasource->getPlaceholderText(),
145 'datasourceURI' => $proj_datasource->getDatasourceURI(),
146 'headerIcon' => 'fa-briefcase',
147 'headerText' => pht('Find Project:'),
148 'hintText' => $proj_datasource->getPlaceholderText(),
151 'datasourceURI' => $emoji_datasource->getDatasourceURI(),
152 'headerIcon' => 'fa-smile-o',
153 'headerText' => pht('Find Emoji:'),
154 'hintText' => $emoji_datasource->getPlaceholderText(),
156 // Cancel on emoticons like ":3".
166 'datasourceURI' => $phriction_datasource->getDatasourceURI(),
167 'headerIcon' => 'fa-book',
168 'headerText' => pht('Find Document:'),
169 'hintText' => $phriction_datasource->getPlaceholderText(),
171 ':', // Cancel on "http:" and similar.
178 'datasourceURI' => $phurl_datasource->getDatasourceURI(),
179 'headerIcon' => 'fa-compress',
180 'headerText' => pht('Find Phurl:'),
181 'hintText' => $phurl_datasource->getPlaceholderText(),
189 Javelin
::initBehavior('phabricator-tooltips', array());
193 'tip' => pht('Bold'),
196 'fa-italic' => array(
197 'tip' => pht('Italics'),
200 'fa-text-width' => array(
201 'tip' => pht('Monospaced'),
205 'tip' => pht('Link'),
212 'fa-list-ul' => array(
213 'tip' => pht('Bulleted List'),
216 'fa-list-ol' => array(
217 'tip' => pht('Numbered List'),
221 'tip' => pht('Code Block'),
224 'fa-quote-right' => array(
225 'tip' => pht('Quote'),
229 'tip' => pht('Table'),
232 'fa-cloud-upload' => array(
233 'tip' => pht('Upload File'),
238 (!$this->disableMacro
) &&
239 (function_exists('imagettftext'));
241 if ($can_use_macros) {
242 $can_use_macros = PhabricatorApplication
::isClassInstalledForViewer(
243 'PhabricatorMacroApplication',
247 if ($can_use_macros) {
251 $actions['fa-meh-o'] = array(
252 'tip' => pht('Meme'),
256 $actions['fa-eye'] = array(
257 'tip' => pht('Preview'),
261 $actions['fa-book'] = array(
262 'tip' => pht('Help'),
264 'href' => PhabricatorEnv
::getDoclink('Remarkup Reference'),
267 $mode_actions = array();
269 if (!$this->disableFullScreen
) {
270 $mode_actions['fa-arrows-alt'] = array(
271 'tip' => pht('Fullscreen Mode'),
276 if ($this->getCanPin()) {
277 $mode_actions['fa-thumb-tack'] = array(
278 'tip' => pht('Pin Form On Screen'),
284 $actions +
= $mode_actions;
288 foreach ($actions as $action => $spec) {
292 if (idx($spec, 'align') == 'right') {
293 $classes[] = 'remarkup-assist-right';
296 if (idx($spec, 'nodevice')) {
297 $classes[] = 'remarkup-assist-nodevice';
300 if (idx($spec, 'spacer')) {
301 $classes[] = 'remarkup-assist-separator';
302 $buttons[] = phutil_tag(
305 'class' => implode(' ', $classes),
310 $classes[] = 'remarkup-assist-button';
313 if ($action == 'fa-cloud-upload') {
314 $classes[] = 'remarkup-assist-upload';
317 $href = idx($spec, 'href', '#');
319 $meta = array('action' => $action);
330 $tip = idx($spec, 'tip');
333 $content = javelin_tag(
342 $sigils[] = 'remarkup-assist';
343 if (!$this->getDisabled()) {
344 $sigils[] = 'has-tooltip';
347 $buttons[] = javelin_tag(
350 'class' => implode(' ', $classes),
352 'sigil' => implode(' ', $sigils),
354 'mustcapture' => $mustcapture,
362 'remarkup-assist phui-icon-view phui-font-fa bluegrey '.$action,
367 $buttons = phutil_tag(
370 'class' => 'remarkup-assist-bar',
374 $use_monospaced = $viewer->compareUserSetting(
375 PhabricatorMonospacedTextareasSetting
::SETTINGKEY
,
376 PhabricatorMonospacedTextareasSetting
::VALUE_TEXT_MONOSPACED
);
378 if ($use_monospaced) {
379 $monospaced_textareas_class = 'PhabricatorMonospaced';
381 $monospaced_textareas_class = null;
384 $this->setCustomClass(
385 'remarkup-assist-textarea '.$monospaced_textareas_class);
390 'sigil' => 'remarkup-assist-control',
391 'class' => $this->getDisabled() ?
'disabled-control' : null,
396 parent
::renderInput(),