Changeset View
Changeset View
Standalone View
Standalone View
webroot/rsrc/js/core/behavior-phabricator-remarkup-assist.js
| /** | /** | ||||
| * @provides javelin-behavior-phabricator-remarkup-assist | * @provides javelin-behavior-phabricator-remarkup-assist | ||||
| * @requires javelin-behavior | * @requires javelin-behavior | ||||
| * javelin-stratcom | * javelin-stratcom | ||||
| * javelin-dom | * javelin-dom | ||||
| * phabricator-phtize | * phabricator-phtize | ||||
| * phabricator-textareautils | * phabricator-textareautils | ||||
| * javelin-workflow | * javelin-workflow | ||||
| * javelin-vector | * javelin-vector | ||||
| */ | */ | ||||
| JX.behavior('phabricator-remarkup-assist', function(config) { | JX.behavior('phabricator-remarkup-assist', function(config) { | ||||
| var pht = JX.phtize(config.pht); | var pht = JX.phtize(config.pht); | ||||
| var root = JX.$(config.rootID); | |||||
| var area = JX.DOM.find(root, 'textarea'); | |||||
| var edit_mode = 'normal'; | var edit_mode = 'normal'; | ||||
| var edit_root = null; | var edit_root = null; | ||||
| var preview = null; | |||||
| function set_edit_mode(root, mode) { | function set_edit_mode(root, mode) { | ||||
| if (mode == edit_mode) { | if (mode == edit_mode) { | ||||
| return; | return; | ||||
| } | } | ||||
| // First, disable any active mode. | // First, disable any active mode. | ||||
| if (edit_root) { | if (edit_root) { | ||||
| if (edit_mode == 'fa-arrows-alt') { | if (edit_mode == 'fa-arrows-alt') { | ||||
| JX.DOM.alterClass(edit_root, 'remarkup-control-fullscreen-mode', false); | JX.DOM.alterClass(edit_root, 'remarkup-control-fullscreen-mode', false); | ||||
| JX.DOM.alterClass(document.body, 'remarkup-fullscreen-mode', false); | JX.DOM.alterClass(document.body, 'remarkup-fullscreen-mode', false); | ||||
| } | } | ||||
| JX.DOM.find(edit_root, 'textarea').style.height = ''; | |||||
| area.style.height = ''; | |||||
| // If we're in preview mode, kick the preview back down to default | |||||
| // size. | |||||
| if (preview) { | |||||
| JX.DOM.show(area); | |||||
| resize_preview(); | |||||
| JX.DOM.hide(area); | |||||
| } | |||||
| } | } | ||||
| edit_root = root; | edit_root = root; | ||||
| edit_mode = mode; | edit_mode = mode; | ||||
| // Now, apply the new mode. | // Now, apply the new mode. | ||||
| if (mode == 'fa-arrows-alt') { | if (mode == 'fa-arrows-alt') { | ||||
| JX.DOM.alterClass(edit_root, 'remarkup-control-fullscreen-mode', true); | JX.DOM.alterClass(edit_root, 'remarkup-control-fullscreen-mode', true); | ||||
| JX.DOM.alterClass(document.body, 'remarkup-fullscreen-mode', true); | JX.DOM.alterClass(document.body, 'remarkup-fullscreen-mode', true); | ||||
| // If we're in preview mode, expand the preview to full-size. | |||||
| if (preview) { | |||||
| JX.DOM.show(area); | |||||
| } | |||||
| resizearea(); | resizearea(); | ||||
| if (preview) { | |||||
| resize_preview(); | |||||
| JX.DOM.hide(area); | |||||
| } | |||||
| } | } | ||||
| JX.DOM.focus(JX.DOM.find(edit_root, 'textarea')); | JX.DOM.focus(area); | ||||
| } | } | ||||
| function resizearea() { | function resizearea() { | ||||
| if (!edit_root) { | if (!edit_root) { | ||||
| return; | return; | ||||
| } | } | ||||
| if (edit_mode != 'fa-arrows-alt') { | if (edit_mode != 'fa-arrows-alt') { | ||||
| return; | return; | ||||
| } | } | ||||
| // In Firefox, a textarea with position "absolute" or "fixed", anchored | // In Firefox, a textarea with position "absolute" or "fixed", anchored | ||||
| // "top" and "bottom", and height "auto" renders as two lines high. Force | // "top" and "bottom", and height "auto" renders as two lines high. Force | ||||
| // it to the correct height with Javascript. | // it to the correct height with Javascript. | ||||
| var area = JX.DOM.find(edit_root, 'textarea'); | |||||
| var v = JX.Vector.getViewport(); | var v = JX.Vector.getViewport(); | ||||
| v.x = null; | v.x = null; | ||||
| v.y -= 26; | v.y -= 26; | ||||
| v.setDim(area); | v.setDim(area); | ||||
| } | } | ||||
| JX.Stratcom.listen('resize', null, resizearea); | JX.Stratcom.listen('resize', null, resizearea); | ||||
| JX.Stratcom.listen('keydown', null, function(e) { | JX.Stratcom.listen('keydown', null, function(e) { | ||||
| if (e.getSpecialKey() != 'esc') { | if (e.getSpecialKey() != 'esc') { | ||||
| return; | return; | ||||
| } | } | ||||
| if (edit_mode != 'fa-arrows-alt') { | if (edit_mode != 'fa-arrows-alt') { | ||||
| return; | return; | ||||
| } | } | ||||
| Show All 33 Lines | if (ch === '>') { | ||||
| sel[i] = ch + sel[i]; | sel[i] = ch + sel[i]; | ||||
| } | } | ||||
| return sel.join('\n'); | return sel.join('\n'); | ||||
| } | } | ||||
| return sel.join('\n' + ch); | return sel.join('\n' + ch); | ||||
| } | } | ||||
| function assist(area, action, root) { | function assist(area, action, root, button) { | ||||
| // If the user has some text selected, we'll try to use that (for example, | // If the user has some text selected, we'll try to use that (for example, | ||||
| // if they have a word selected and want to bold it). Otherwise we'll insert | // if they have a word selected and want to bold it). Otherwise we'll insert | ||||
| // generic text. | // generic text. | ||||
| var sel = JX.TextAreaUtils.getSelectionText(area); | var sel = JX.TextAreaUtils.getSelectionText(area); | ||||
| var r = JX.TextAreaUtils.getSelectionRange(area); | var r = JX.TextAreaUtils.getSelectionRange(area); | ||||
| var ch; | var ch; | ||||
| switch (action) { | switch (action) { | ||||
| ▲ Show 20 Lines • Show All 50 Lines • ▼ Show 20 Lines | switch (action) { | ||||
| break; | break; | ||||
| case 'fa-arrows-alt': | case 'fa-arrows-alt': | ||||
| if (edit_mode == 'fa-arrows-alt') { | if (edit_mode == 'fa-arrows-alt') { | ||||
| set_edit_mode(root, 'normal'); | set_edit_mode(root, 'normal'); | ||||
| } else { | } else { | ||||
| set_edit_mode(root, 'fa-arrows-alt'); | set_edit_mode(root, 'fa-arrows-alt'); | ||||
| } | } | ||||
| break; | break; | ||||
| case 'fa-eye': | |||||
| if (!preview) { | |||||
| preview = JX.$N( | |||||
| 'div', | |||||
| { | |||||
| className: 'remarkup-inline-preview' | |||||
| }, | |||||
| null); | |||||
| area.parentNode.insertBefore(preview, area); | |||||
| JX.DOM.alterClass(button, 'preview-active', true); | |||||
| resize_preview(); | |||||
| JX.DOM.hide(area); | |||||
| update_preview(); | |||||
| } else { | |||||
| JX.DOM.show(area); | |||||
| resize_preview(true); | |||||
| JX.DOM.remove(preview); | |||||
| preview = null; | |||||
| JX.DOM.alterClass(button, 'preview-active', false); | |||||
| } | |||||
| break; | |||||
| } | } | ||||
| } | } | ||||
| JX.Stratcom.listen( | function resize_preview(restore) { | ||||
| ['click'], | if (!preview) { | ||||
| return; | |||||
| } | |||||
| var src; | |||||
| var dst; | |||||
| if (restore) { | |||||
| src = preview; | |||||
| dst = area; | |||||
| } else { | |||||
| src = area; | |||||
| dst = preview; | |||||
| } | |||||
| var d = JX.Vector.getDim(src); | |||||
| d.x = null; | |||||
| d.setDim(dst); | |||||
| } | |||||
| function update_preview() { | |||||
| var value = area.value; | |||||
| var data = { | |||||
| corpus: value | |||||
| }; | |||||
| var onupdate = function(r) { | |||||
| if (area.value !== value) { | |||||
| return; | |||||
| } | |||||
| if (!preview) { | |||||
| return; | |||||
| } | |||||
| JX.DOM.setContent(preview, JX.$H(r.content).getFragment()); | |||||
| }; | |||||
| new JX.Workflow('/transactions/remarkuppreview/', data) | |||||
| .setHandler(onupdate) | |||||
| .start(); | |||||
| } | |||||
| JX.DOM.listen( | |||||
| root, | |||||
| 'click', | |||||
| 'remarkup-assist', | 'remarkup-assist', | ||||
| function(e) { | function(e) { | ||||
| var data = e.getNodeData('remarkup-assist'); | var data = e.getNodeData('remarkup-assist'); | ||||
| if (!data.action) { | if (!data.action) { | ||||
| return; | return; | ||||
| } | } | ||||
| e.kill(); | e.kill(); | ||||
| if (config.disabled) { | if (config.disabled) { | ||||
| return; | return; | ||||
| } | } | ||||
| var root = e.getNode('remarkup-assist-control'); | assist(area, data.action, root, e.getNode('remarkup-assist')); | ||||
| var area = JX.DOM.find(root, 'textarea'); | |||||
| assist(area, data.action, root); | |||||
| }); | }); | ||||
| }); | }); | ||||