Page MenuHomestyx hydra

No OneTemporary

diff --git a/src/view/form/control/PhabricatorRemarkupControl.php b/src/view/form/control/PhabricatorRemarkupControl.php
index b2335d302f..7466547fba 100644
--- a/src/view/form/control/PhabricatorRemarkupControl.php
+++ b/src/view/form/control/PhabricatorRemarkupControl.php
@@ -1,42 +1,129 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
final class PhabricatorRemarkupControl extends AphrontFormTextAreaControl {
- public function getCaption() {
+ protected function renderInput() {
- $caption = parent::getCaption();
- if ($caption) {
- $caption_suffix = '<br />'.$caption;
- } else {
- $caption_suffix = '';
- }
+ Javelin::initBehavior('phabricator-remarkup-assist', array());
- return phutil_render_tag(
- 'a',
+ $actions = array(
+ 'b' => array(
+ 'text' => 'B',
+ ),
+ 'i' => array(
+ 'text' => 'I',
+ ),
+ 'tt' => array(
+ 'text' => 'T',
+ ),
+ 's' => array(
+ 'text' => 'S',
+ ),
+ array(
+ 'spacer' => true,
+ ),
+ 'ul' => array(
+ 'text' => "\xE2\x80\xA2",
+ ),
+ 'ol' => array(
+ 'text' => '1.',
+ ),
+ 'code' => array(
+ 'text' => '{}',
+ ),
+ array(
+ 'spacer' => true,
+ ),
+ 'mention' => array(
+ 'text' => '@',
+ ),
array(
- 'href' => PhabricatorEnv::getDoclink(
+ 'spacer' => true,
+ ),
+ 'h1' => array(
+ 'text' => 'H',
+ ),
+ array(
+ 'spacer' => true,
+ ),
+ 'help' => array(
+ 'align' => 'right',
+ 'text' => '?',
+ 'href' => PhabricatorEnv::getDoclink(
'article/Remarkup_Reference.html'),
- 'tabindex' => '-1',
- 'target' => '_blank',
),
- 'Formatting Reference') .
- $caption_suffix;
+ );
+
+ $buttons = array();
+ foreach ($actions as $action => $spec) {
+ if (idx($spec, 'spacer')) {
+ $buttons[] = '<span> </span>';
+ continue;
+ }
+
+ $classes = array();
+ $classes[] = 'button';
+ $classes[] = 'grey';
+ $classes[] = 'remarkup-assist-button';
+ if (idx($spec, 'align') == 'right') {
+ $classes[] = 'remarkup-assist-right';
+ }
+
+ $href = idx($spec, 'href', '#');
+ if ($href == '#') {
+ $meta = array('action' => $action);
+ $mustcapture = true;
+ $target = null;
+ } else {
+ $meta = null;
+ $mustcapture = null;
+ $target = '_blank';
+ }
+
+ $buttons[] = javelin_render_tag(
+ 'a',
+ array(
+ 'class' => implode(' ', $classes),
+ 'href' => $href,
+ 'sigil' => 'remarkup-assist',
+ 'meta' => $meta,
+ 'mustcapture' => $mustcapture,
+ 'target' => $target,
+ 'tabindex' => -1,
+ ),
+ phutil_render_tag(
+ 'div',
+ array(
+ 'class' => 'remarkup-assist remarkup-assist-'.$action,
+ ),
+ idx($spec, 'text', '')));
+ }
+
+ $buttons = implode('', $buttons);
+
+ return javelin_render_tag(
+ 'div',
+ array(
+ 'sigil' => 'remarkup-assist-control',
+ ),
+ $buttons.
+ parent::renderInput());
}
}
diff --git a/webroot/rsrc/css/aphront/form-view.css b/webroot/rsrc/css/aphront/form-view.css
index 242f979757..0b36bb4bb9 100644
--- a/webroot/rsrc/css/aphront/form-view.css
+++ b/webroot/rsrc/css/aphront/form-view.css
@@ -1,306 +1,308 @@
/**
* @provides aphront-form-view-css
*/
/**
* These styles are overrides for .aphront-form-view
*/
.aphront-form-view-shaded {
border: 1px solid #c4c4c4;
background: #e7e7e7;
}
.aphront-form-view-padded {
padding: 1em;
}
.aphront-form-view label.aphront-form-label {
padding-top: 4px;
width: 19%;
float: left;
text-align: right;
font-weight: bold;
font-size: 13px;
color: #666666;
}
.aphront-form-input {
margin-left: 20%;
margin-right: 25%;
width: 55%;
}
.aphront-form-error {
width: 23%;
float: right;
color: #aa0000;
font-weight: bold;
padding-top: 4px;
}
.aphront-form-required {
font-weight: normal;
color: #888888;
font-size: 11px;
}
.aphront-form-input input,
.aphront-form-input textarea {
font-size: 12px;
+ display: block;
width: 100%;
+ box-sizing: border-box;
}
.aphront-form-input textarea {
height: 12em;
}
.aphront-form-control {
padding: 4px;
}
.aphront-form-control-submit button,
.aphront-form-control-submit a.button {
float: right;
margin: 0.5em 0 0em 2%;
}
.aphront-form-control-textarea textarea.aphront-textarea-very-short {
height: 3em;
}
.aphront-form-control-textarea textarea.aphront-textarea-very-tall {
height: 24em;
}
.aphront-form-control-select .aphront-form-input {
padding-top: 2px;
}
.aphront-form-view .aphront-form-caption {
font-size: 11px;
color: #444444;
text-align: right;
margin-right: 25%;
margin-left: 20%;
}
/* override for when inside an aphront-panel-view */
.aphront-panel-view .aphront-form-view h1 {
padding: 0em 0em .8em 0em;
}
.aphront-form-instructions {
margin: 0.75em 3% 1.25em;
}
.aphront-form-important {
margin: .5em 0;
background: #ffffdd;
padding: .5em 1em;
}
.aphront-form-important code {
display: block;
padding: .25em;
margin: .5em 2em;
}
.aphront-form-control-static .aphront-form-input,
.aphront-form-control-markup .aphront-form-input {
padding-top: 4px;
font-size: 13px;
}
.aphront-form-control-togglebuttons .aphront-form-input {
padding-top: 5px;
}
table.aphront-form-control-radio-layout,
table.aphront-form-control-checkbox-layout {
margin-top: 3px;
font-size: 13px;
}
table.aphront-form-control-radio-layout th,
table.aphront-form-control-checkbox-layout th {
padding-top: 2px;
padding-left: 0.35em;
padding-bottom: 4px;
}
.aphront-form-control-radio-layout td input,
.aphront-form-control-checkbox-layout td input {
margin-top: 4px;
width: auto;
}
.aphront-form-radio-caption {
font-size: 11px;
color: #444444;
max-width: 400px;
}
.aphront-form-control-image .image {
width: 164px;
}
.aphront-form-control-image span {
margin: 0px 4px 0px 2px;
}
.aphront-form-control-image .default-image {
width: 12px;
}
.aphront-form-input hr {
border: none;
background: #bbbbbb;
height: 1px;
position: relative;
}
.aphront-form-inset {
margin: 0 0 1em;
padding: .75em;
background: #f3f3f3;
border: 1px solid #afafaf;
}
.aphront-form-drag-and-drop-file-list {
width: 400px;
}
.drag-and-drop-instructions {
color: #333333;
font-size: 11px;
padding: 6px 8px;
}
.aphront-textarea-drag-and-drop {
background: #99ff99;
border-color: #669966;
}
.calendar-button {
padding: 11px;
right: -30px;
top: -3px;
background: url(/rsrc/image/icon/fatcow/calendar_edit.png)
no-repeat center center;
z-index: 2;
position: absolute;
border: 1px solid transparent;
}
.aphront-form-date-container {
position: relative;
display: inline;
}
.aphront-form-date-container select{
margin: 2px;
}
.fancy-datepicker {
position: absolute;
top: -10px;
right: -8px;
width: 240px;
padding-bottom: 6em;
}
.fancy-datepicker-core {
padding: 1px;
font-size: 11px;
font-family: Verdana;
text-align: center;
}
.fancy-datepicker-core .month-table,
.fancy-datepicker-core .day-table {
margin: 0 auto;
border-collapse: separate;
border-spacing: 1px;
width: 100%;
}
.fancy-datepicker-core .month-table {
margin-bottom: 6px;
}
.fancy-datepicker-core .month-table td.lrbutton {
width: 20%;
}
.fancy-datepicker-core .month-table td {
padding: 4px;
font-weight: bold;
color: #444444;
}
.fancy-datepicker-core .month-table td.lrbutton {
background: #e6e6e6;
border: 1px solid;
border-color: #a6a6a6 #969696 #868686 #a6a6a6;
}
.fancy-datepicker-core .day-table td {
overflow: hidden;
background: #f6f6f6;
vertical-align: center;
text-align: center;
border: 1px solid #d6d6d6;
padding: 4px 0;
}
.fancy-datepicker-core .day-table td.day-placeholder {
border-color: transparent;
background: transparent;
}
.fancy-datepicker-core .day-table td.weekend {
color: #666666;
border-color: #e6e6e6;
}
.fancy-datepicker-core .day-table td.day-name {
background: transparent;
border: 1px transparent;
vertical-align: bottom;
color: #888888;
}
.fancy-datepicker-core .day-table td.today {
background: #eeee99;
border-color: #aaaa66;
}
.fancy-datepicker-core .day-table td.datepicker-selected {
background: #0099ff;
border-color: #0066cc;
}
.fancy-datepicker-core td {
cursor: pointer;
}
.fancy-datepicker-core td.novalue {
cursor: inherit;
}
.picker-open .calendar-button,
.fancy-datepicker-core {
background-color: white;
border: 1px solid #777777;
box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.25);
}
.picker-open .calendar-button {
border-left: 1px solid white;
}
diff --git a/webroot/rsrc/css/core/remarkup.css b/webroot/rsrc/css/core/remarkup.css
index ed6c88e570..527b3b6185 100644
--- a/webroot/rsrc/css/core/remarkup.css
+++ b/webroot/rsrc/css/core/remarkup.css
@@ -1,248 +1,309 @@
/**
* @provides phabricator-remarkup-css
*/
.phabricator-remarkup {
line-height: 1.45em;
}
.phabricator-remarkup p {
margin: 0 0 1em;
}
.phabricator-remarkup p:last-child {
margin-bottom: 0;
}
.phabricator-remarkup .remarkup-code-block {
margin: 1em 2em;
white-space: pre;
font-family: "Monaco", monospace;
font-size: 10px;
}
.phabricator-remarkup .remarkup-code-header {
padding: .25em 1em;
background: #edead7;
color: #444444;
}
.phabricator-remarkup .remarkup-code-block pre {
background: #fdfae7;
border: 1px solid #f5e178;
display: block;
color: #000000;
overflow: auto;
padding: .5em 1em;
font-family: "Monaco", monospace;
}
.phabricator-remarkup pre.remarkup-counterexample {
border: 1px solid #aa0000;
background-color: #ffaaaa;
}
.phabricator-remarkup tt {
color: #333333;
background: #ebebeb;
padding: 0 .25em;
white-space: pre-wrap;
}
.phabricator-remarkup ul {
list-style: disc;
margin: 1em 0 1em 3em;
}
.phabricator-remarkup ol {
list-style: decimal;
margin: 1em 0 1em 3em;
}
.phabricator-remarkup ul ol,
.phabricator-remarkup ul ul,
.phabricator-remarkup ol ol,
.phabricator-remarkup ol ul {
margin: .25em 0 .25em 2em;
}
.phabricator-remarkup li.phantom-item,
.phabricator-remarkup li.phantom-item {
list-style-type: none;
}
.phabricator-remarkup h1:first-child,
.phabricator-remarkup h2:first-child,
.phabricator-remarkup h3:first-child,
.phabricator-remarkup h4:first-child,
.phabricator-remarkup h5:first-child,
.phabricator-remarkup h6:first-child {
margin-top: 0;
}
.phabricator-remarkup h1:last-child,
.phabricator-remarkup h2:last-child,
.phabricator-remarkup h3:last-child,
.phabricator-remarkup h4:last-child,
.phabricator-remarkup h5:last-child,
.phabricator-remarkup h6:last-child {
margin-bottom: 0;
}
.phabricator-remarkup h1 {
font-size: 1.625em;
line-height: 1.625em;
margin: .8em 0;
}
.phabricator-remarkup h2 {
font-size: 1.5em;
line-height: 1.5em;
margin: .75em 0;
}
.phabricator-remarkup h3 {
font-size: 1.375em;
line-height: 1.375em;
margin: .69em 0;
}
.phabricator-remarkup h4 {
font-size: 1.25em;
line-height: 1.25em;
margin: .63em 0;
}
.phabricator-remarkup h5 {
font-size: 1.125em;
line-height: 1.125em;
margin: .56em 0;
}
.phabricator-remarkup h6 {
font-size: 1em;
line-height: 1em;
margin: .5em 0;
}
.phabricator-remarkup blockquote {
border-left: 1px solid #AAAAAA;
color: #333333;
font-style: italic;
margin: .5em 0em;
padding: .25em 1em;
}
.phabricator-remarkup img.remarkup-proxy-image {
max-width: 640px;
max-height: 640px;
}
.phabricator-remarkup-mention-exists {
font-weight: bold;
background: #e6f3ff;
}
.phabricator-remarkup-mention-disabled {
font-weight: bold;
background: #dddddd;
}
.aphront-panel-preview .phabricator-remarkup-mention-unknown {
font-weight: bold;
background: #ffaaaa;
}
.phabricator-remarkup .remarkup-note {
margin: 1em 1.5em;
padding: 0.5em 1em;
border: 1px solid #ddddff;
background: #f3f3ff;
}
.phabricator-remarkup-toc {
float: right;
border: 1px solid #999999;
background: #f9f9f9;
padding: 4px 12px;
width: 220px;
}
.phabricator-remarkup-toc-header {
font-size: 11px;
color: #444444;
border-bottom: 1px solid #bbbbbb;
margin-bottom: 4px;
}
.phabricator-remarkup-toc ul {
padding: 0;
margin: 0;
list-style: none;
overflow: hidden;
padding-left: 0.25em;
}
.phabricator-remarkup-toc ul ul {
margin-left: 1.25em;
}
.phabricator-remarkup-toc ul li {
padding: 0;
margin: 0;
}
.phabricator-remarkup-embed-layout-right {
text-align: right;
}
.phabricator-remarkup-embed-layout-center {
text-align: center;
}
.phabricator-remarkup-embed-layout-inline {
display: inline;
}
.phabricator-remarkup-embed-float-right {
float: right;
margin: .5em 1em 0;
}
.phabricator-remarkup-embed-layout-link {
padding-left: 20px;
background: url(/rsrc/image/icon/fatcow/page_white_put.png) 0 0 no-repeat;
}
.phabricator-remarkup-embed-float-left {
float: left;
margin: .5em 1em 0;
}
img.phabricator-remarkup-embed-image {
display: inline;
border: 2px solid white;
background: white;
box-shadow: 1px 1px 6px rgba(0,0,0,.5);
-moz-box-shadow: 1px 1px 6px rgba(0,0,0,.5);
-webkit-box-shadow: 1px 1px 6px rgba(0,0,0,.5);
}
.phabricator-remarkup table.remarkup-table {
border-collapse: separate;
border-spacing: 1px;
background: #d3d3d3;
margin: 1em;
}
.phabricator-remarkup table.remarkup-table th {
font-weight: bold;
padding: 3px 6px;
background: #e3e3e3;
}
.phabricator-remarkup table.remarkup-table td {
background: #ffffff;
padding: 3px 6px;
}
+
+.remarkup-assist-bar {
+ padding: 2px 0;
+}
+
+a.remarkup-assist-button {
+ padding-left: 4px;
+ padding-right: 4px;
+ padding-bottom: 4px;
+ margin-bottom: 3px;
+
+ position: relative;
+ overflow: hidden;
+ height: 16px;
+ width: 16px;
+}
+
+a.remarkup-assist-button + a.remarkup-assist-button {
+ border-left-width: 0px;
+}
+
+.remarkup-assist {
+ float: left;
+ height: 16px;
+ width: 16px;
+ font-weight: normal;
+}
+
+.remarkup-assist-right {
+ float: right;
+}
+
+.remarkup-assist-b {
+ font-family: "Georgia", serif;
+ font-weight: bold;
+}
+
+.remarkup-assist-i {
+ font-family: "Georgia", serif;
+ font-style: italic;
+}
+
+.remarkup-assist-code,
+.remarkup-assist-tt {
+ text-align: center;
+ font-family: monospace;
+}
+
+.remarkup-assist-s {
+ font-family: "Georgia", serif;
+ text-decoration: line-through;
+}
+
+.remarkup-assist-ol {
+ font-family: "Georgia", serif;
+}
+
+.remarkup-assist-h1 {
+ font-family: "Georgia", serif;
+ font-weight: bold;
+}
diff --git a/webroot/rsrc/js/application/core/TextAreaUtils.js b/webroot/rsrc/js/application/core/TextAreaUtils.js
new file mode 100644
index 0000000000..9beac8b1d0
--- /dev/null
+++ b/webroot/rsrc/js/application/core/TextAreaUtils.js
@@ -0,0 +1,49 @@
+/**
+ * @requires javelin-install
+ * @provides phabricator-textareautils
+ * @javelin
+ */
+
+JX.install('TextAreaUtils', {
+ statics : {
+ getSelectionRange : function(area) {
+ var v = area.value;
+
+ // NOTE: This works well in Safari, Firefox and Chrome. We'll probably get
+ // less-good behavior on IE.
+
+ var s = v.length;
+ var e = v.length;
+
+ if ('selectionStart' in area) {
+ s = area.selectionStart;
+ e = area.selectionEnd;
+ }
+
+ return {start: s, end: e};
+ },
+
+ getSelectionText : function(area) {
+ var v = area.value;
+ var r = JX.TextAreaUtils.getSelectionRange(area);
+ return v.substring(r.start, r.end);
+ },
+
+ setSelectionRange : function(area, start, end) {
+ if ('setSelectionRange' in area) {
+ area.focus();
+ area.setSelectionRange(start, end);
+ }
+ },
+
+ setSelectionText : function(area, text) {
+ var v = area.value;
+ var r = JX.TextAreaUtils.getSelectionRange(area);
+
+ v = v.substring(0, r.start) + text + v.substring(r.end, v.length);
+ area.value = v;
+
+ JX.TextAreaUtils.setSelectionRange(area, r.start, r.start + text.length);
+ }
+ }
+});
diff --git a/webroot/rsrc/js/application/core/behavior-drag-and-drop-textarea.js b/webroot/rsrc/js/application/core/behavior-drag-and-drop-textarea.js
index 2b7a88f875..8e9e02d442 100644
--- a/webroot/rsrc/js/application/core/behavior-drag-and-drop-textarea.js
+++ b/webroot/rsrc/js/application/core/behavior-drag-and-drop-textarea.js
@@ -1,60 +1,34 @@
/**
* @provides javelin-behavior-aphront-drag-and-drop-textarea
* @requires javelin-behavior
* javelin-dom
* phabricator-drag-and-drop-file-upload
* phabricator-paste-file-upload
+ * phabricator-textareautils
*/
JX.behavior('aphront-drag-and-drop-textarea', function(config) {
var target = JX.$(config.target);
function onupload(f) {
- var v = target.value;
- var insert = '{F' + f.id + '}';
-
- // NOTE: This works well in Safari, Firefox and Chrome. We'll probably get
- // less-good behavior on IE, but I think IE doesn't support drag-and-drop
- // or paste uploads anyway.
-
- // Set the insert position to the end of the text, so we get reasonable
- // default behavior.
- var s = v.length;
- var e = v.length;
-
- // If possible, refine the insert position based on the current selection.
- if ('selectionStart' in target) {
- s = target.selectionStart;
- e = target.selectionEnd;
- }
-
- // Build the new text.
- v = v.substring(0, s) + insert + v.substring(e, v.length);
- // Replace the current value with the new text.
- target.value = v;
-
- // If possible, place the cursor after the inserted text.
- if ('setSelectionRange' in target) {
- target.focus();
- target.setSelectionRange(s + insert.length, s + insert.length);
- }
+ JX.TextAreaUtils.setSelectionText(target, '{F' + f.id + '}');
}
if (JX.PhabricatorDragAndDropFileUpload.isSupported()) {
var drop = new JX.PhabricatorDragAndDropFileUpload(target)
.setActivatedClass(config.activatedClass)
.setURI(config.uri);
drop.listen('didUpload', onupload);
drop.start();
}
if (JX.PhabricatorPasteFileUpload.isSupported()) {
var paste = new JX.PhabricatorPasteFileUpload(target)
.setURI(config.uri);
paste.listen('didUpload', onupload);
paste.start();
}
});
diff --git a/webroot/rsrc/js/application/core/behavior-phabricator-remarkup-assist.js b/webroot/rsrc/js/application/core/behavior-phabricator-remarkup-assist.js
new file mode 100644
index 0000000000..e5b56167d8
--- /dev/null
+++ b/webroot/rsrc/js/application/core/behavior-phabricator-remarkup-assist.js
@@ -0,0 +1,89 @@
+/**
+ * @provides javelin-behavior-phabricator-remarkup-assist
+ * @requires javelin-behavior
+ * javelin-stratcom
+ * javelin-dom
+ * phabricator-textareautils
+ */
+
+JX.behavior('phabricator-remarkup-assist', function(config) {
+
+ function update(area, l, m, r) {
+ // Replace the selection with the entire assisted text.
+ JX.TextAreaUtils.setSelectionText(area, l + m + r);
+
+ // Now, select just the middle part. For instance, if the user clicked
+ // "B" to create bold text, we insert '**bold**' but just select the word
+ // "bold" so if they type stuff they'll be editing the bold text.
+ var r = JX.TextAreaUtils.getSelectionRange(area);
+ JX.TextAreaUtils.setSelectionRange(
+ area,
+ r.start + l.length,
+ r.start + l.length + m.length);
+ }
+
+ function assist(area, action) {
+ // 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
+ // generic text.
+ var sel = JX.TextAreaUtils.getSelectionText(area);
+ var r = JX.TextAreaUtils.getSelectionRange(area);
+
+ switch (action) {
+ case 'b':
+ update(area, '**', sel || 'bold text', '**');
+ break;
+ case 'i':
+ update(area, '//', sel || 'italic text', '//');
+ break;
+ case 'tt':
+ update(area, '`', sel || 'monospaced text', '`');
+ break;
+ case 's':
+ update(area, '~~', sel || 'strikethrough text', '~~');
+ break;
+ case 'ul':
+ case 'ol':
+ var ch = (action == 'ol') ? ' # ' : ' - ';
+ if (sel) {
+ sel = sel.split("\n");
+ } else {
+ sel = ["List Item"];
+ }
+ sel = sel.join("\n" + ch);
+ update(area, ((r.start == 0) ? "" : "\n\n") + ch, sel, "\n\n");
+ break;
+ case 'code':
+ sel = sel || "foreach ($list as $item) {\n work_miracles($item);\n}";
+ sel = sel.split("\n");
+ sel = " " + sel.join("\n ");
+ update(area, ((r.start == 0) ? "" : "\n\n"), sel, "\n\n");
+ break;
+ case 'mention':
+ update(area, '@', sel || 'username', '');
+ break;
+ case 'h1':
+ sel = sel || 'Header';
+ update(area, ((r.start == 0) ? "" : "\n\n") + "= ", sel, " =\n\n");
+ break;
+ }
+ }
+
+ JX.Stratcom.listen(
+ ['click'],
+ 'remarkup-assist',
+ function(e) {
+ var data = e.getNodeData('remarkup-assist');
+ if (!data) {
+ return;
+ }
+
+ e.kill();
+
+ var root = e.getNode('remarkup-assist-control');
+ var area = JX.DOM.find(root, 'textarea');
+
+ assist(area, data.action);
+ });
+
+});

File Metadata

Mime Type
text/x-diff
Expires
Sun, Jul 27, 7:44 PM (1 w, 8 h ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
186106
Default Alt Text
(22 KB)

Event Timeline