1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2025-01-20 11:41:08 +01:00

Kind-of-terrible (?) oncopy handler

Summary: Works in Safari, Firefox, Chrome.

Test Plan: Copied some text, threw up a little in my mouth.

Reviewers: aran, tuomaspelkonen, tomo, rstout, btrahan

Reviewed By: aran

CC: aran, epriestley, ddfisher

Maniphest Tasks: T145, T995

Differential Revision: https://secure.phabricator.com/D244
This commit is contained in:
epriestley 2012-03-15 19:04:59 -07:00
parent fffc1e51d0
commit 57fd4bc68b
6 changed files with 103 additions and 2 deletions

View file

@ -839,6 +839,17 @@ celerity_register_resource_map(array(
), ),
'disk' => '/rsrc/js/application/core/behavior-object-selector.js', 'disk' => '/rsrc/js/application/core/behavior-object-selector.js',
), ),
'javelin-behavior-phabricator-oncopy' =>
array(
'uri' => '/res/70b9b75e/rsrc/js/application/core/behavior-oncopy.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-behavior',
1 => 'javelin-dom',
),
'disk' => '/rsrc/js/application/core/behavior-oncopy.js',
),
'javelin-behavior-phabricator-tooltips' => 'javelin-behavior-phabricator-tooltips' =>
array( array(
'uri' => '/res/49f92a92/rsrc/js/application/core/behavior-tooltip.js', 'uri' => '/res/49f92a92/rsrc/js/application/core/behavior-tooltip.js',

View file

@ -1417,7 +1417,10 @@ final class DifferentialChangesetParser {
'<th'.$o_id.'>'.$o_num.'</th>'. '<th'.$o_id.'>'.$o_num.'</th>'.
'<td'.$o_attr.'>'.$o_text.'</td>'. '<td'.$o_attr.'>'.$o_text.'</td>'.
'<th'.$n_id.'>'.$n_num.'</th>'. '<th'.$n_id.'>'.$n_num.'</th>'.
'<td'.$n_attr.'>'.$n_text.'</td>'. // NOTE: This is a unicode zero-width space, which we use as a hint
// when intercepting 'copy' events to make sure sensible text ends
// up on the clipboard. See the 'phabricator-oncopy' behavior.
'<td'.$n_attr.'>'."\xE2\x80\x8B".$n_text.'</td>'.
$n_cov. $n_cov.
'</tr>'; '</tr>';

View file

@ -65,6 +65,8 @@ final class DifferentialChangesetDetailView extends AphrontView {
require_celerity_resource('differential-changeset-view-css'); require_celerity_resource('differential-changeset-view-css');
require_celerity_resource('syntax-highlighting-css'); require_celerity_resource('syntax-highlighting-css');
Javelin::initBehavior('phabricator-oncopy', array());
if ($this->revisionID) { if ($this->revisionID) {
$edit = true; $edit = true;
} else { } else {

View file

@ -142,6 +142,7 @@ final class PhabricatorPasteViewController extends PhabricatorPasteController {
$text_list = explode("\n", $source); $text_list = explode("\n", $source);
Javelin::initBehavior('phabricator-oncopy', array());
$rows = $this->buildDisplayRows($text_list); $rows = $this->buildDisplayRows($text_list);
$corpus_table = phutil_render_tag( $corpus_table = phutil_render_tag(
@ -179,7 +180,10 @@ final class PhabricatorPasteViewController extends PhabricatorPasteController {
), ),
$n); $n);
$rows[] = '<tr id="'.$anchor.'"><th>'.$link.'</th>'. $rows[] = '<tr id="'.$anchor.'"><th>'.$link.'</th>'.
'<td style="white-space: pre-wrap;">'.$line.'</td></tr>'; '<td style="white-space: pre-wrap;">'.
// NOTE: See the 'phabricator-oncopy' behavior.
"\xE2\x80\x8B".
$line.'</td></tr>';
++$n; ++$n;
} }

View file

@ -13,6 +13,7 @@ phutil_require_module('phabricator', 'applications/markup/syntax');
phutil_require_module('phabricator', 'applications/paste/controller/base'); phutil_require_module('phabricator', 'applications/paste/controller/base');
phutil_require_module('phabricator', 'applications/paste/storage/paste'); phutil_require_module('phabricator', 'applications/paste/storage/paste');
phutil_require_module('phabricator', 'infrastructure/celerity/api'); phutil_require_module('phabricator', 'infrastructure/celerity/api');
phutil_require_module('phabricator', 'infrastructure/javelin/api');
phutil_require_module('phabricator', 'view/control/table'); phutil_require_module('phabricator', 'view/control/table');
phutil_require_module('phabricator', 'view/layout/panel'); phutil_require_module('phabricator', 'view/layout/panel');

View file

@ -0,0 +1,80 @@
/**
* @provides javelin-behavior-phabricator-oncopy
* @requires javelin-behavior
* javelin-dom
*/
/**
* Tools like Paste and Differential don't normally respond to the clipboard
* 'copy' operation well, because when a user copies text they'll get line
* numbers and other metadata.
*
* To improve this behavior, applications can embed markers that delimit
* metadata (left of the marker) from content (right of the marker). When
* we get a copy event, we strip out all the metadata and just copy the
* actual text.
*/
JX.behavior('phabricator-oncopy', function() {
var zws = "\u200B"; // Unicode Zero-Width Space
document.body.oncopy = function(e) {
var selection = window.getSelection();
var text = selection.toString();
if (text.indexOf(zws) == -1) {
// If there's no marker in the text, just let it copy normally.
return;
}
var result = [];
// Strip everything before the marker (and the marker itself) out of the
// text. If a line doesn't have the marker, throw it away (the assumption
// is that it's a line number or part of some other meta-text).
var lines = text.split("\n");
var pos;
for (var ii = 0; ii < lines.length; ii++) {
pos = lines[ii].indexOf(zws);
if (pos == -1 && ii != 0) {
continue;
}
result.push(lines[ii].substring(pos + 1));
}
result = result.join("\n");
if (e.clipboardData) {
// Safari and Chrome support this easy, straightforward mechanism.
e.clipboardData.setData('Text', result);
e.preventDefault();
} else {
// In Firefox, we have to create a <pre> and select the text in it, then
// let the copy event fire. It has to be a <pre> because Firefox won't
// copy returns properly out of a div, even if it has 'whitespace: pre'.
// There's been a bug open for 10 (!) years:
//
// https://bugzilla.mozilla.org/show_bug.cgi?id=116083
var style = {
position: 'absolute',
left: '-10000px'
};
var pre = JX.$N('pre', style, result);
document.body.appendChild(pre);
// Select the text in the <pre>.
var range = document.createRange();
range.selectNodeContents(pre);
selection.removeAllRanges();
selection.addRange(range);
setTimeout(function() { JX.DOM.remove(pre) }, 0);
// TODO: I tried to restore the old selection range but it doesn't seem
// to work or give me any errors. So you lose your selection when you
// copy. Oh well?
}
}
});