From 26c836e1e14ed7f387d1a7d974bb199bd632ff0a Mon Sep 17 00:00:00 2001 From: Ben Alpert Date: Thu, 3 Apr 2014 09:20:55 -0700 Subject: [PATCH] Simplify oncopy behavior Summary: Firefox has supported clipboardData since version 22 (Jul 2013), and even IE8 supports it if you look at `window.clipboardData` instead of `e.clipboardData`. As a result, we can simplify this code significantly. I also used (or at least, attempted to) Javelin so that we can get the event object and preventDefault more easily. Plus, this way we don't assign to document.body.oncopy. Test Plan: Copied a selection including a line number in Chrome, Firefox, and IE8. The line number didn't get copied. Reviewers: #blessed_reviewers, epriestley Reviewed By: #blessed_reviewers, epriestley Subscribers: epriestley, Korvin Differential Revision: https://secure.phabricator.com/D8688 --- resources/celerity/map.php | 16 ++--- webroot/rsrc/js/core/behavior-oncopy.js | 91 +++++++++++-------------- 2 files changed, 46 insertions(+), 61 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index b1620238b2..1ab999c4eb 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -8,7 +8,7 @@ return array( 'names' => array( 'core.pkg.css' => 'fb144113', - 'core.pkg.js' => 'd3fecc57', + 'core.pkg.js' => 'e39d336b', 'darkconsole.pkg.js' => 'ca8671ce', 'differential.pkg.css' => 'cc216438', 'differential.pkg.js' => '11a5b750', @@ -463,7 +463,7 @@ return array( 'rsrc/js/core/behavior-line-linker.js' => 'bc778103', 'rsrc/js/core/behavior-more.js' => '9b9197be', 'rsrc/js/core/behavior-object-selector.js' => 'b4eef37b', - 'rsrc/js/core/behavior-oncopy.js' => 'dab9253e', + 'rsrc/js/core/behavior-oncopy.js' => 'c3e218fe', 'rsrc/js/core/behavior-phabricator-nav.js' => 'b5842a5e', 'rsrc/js/core/behavior-phabricator-remarkup-assist.js' => 'c021950a', 'rsrc/js/core/behavior-refresh-csrf.js' => 'c4b31646', @@ -596,7 +596,7 @@ return array( 'javelin-behavior-phabricator-nav' => 'b5842a5e', 'javelin-behavior-phabricator-notification-example' => 'c51a6616', 'javelin-behavior-phabricator-object-selector' => 'b4eef37b', - 'javelin-behavior-phabricator-oncopy' => 'dab9253e', + 'javelin-behavior-phabricator-oncopy' => 'c3e218fe', 'javelin-behavior-phabricator-remarkup-assist' => 'c021950a', 'javelin-behavior-phabricator-reveal-content' => '8f24abfc', 'javelin-behavior-phabricator-search-typeahead' => 'f6b56f7a', @@ -1634,6 +1634,11 @@ return array( 2 => 'javelin-util', 3 => 'javelin-magical-init', ), + 'c3e218fe' => + array( + 0 => 'javelin-behavior', + 1 => 'javelin-dom', + ), 'c4b31646' => array( 0 => 'javelin-request', @@ -1768,11 +1773,6 @@ return array( 1 => 'javelin-util', 2 => 'javelin-stratcom', ), - 'dab9253e' => - array( - 0 => 'javelin-behavior', - 1 => 'javelin-dom', - ), 'dd7e8ef5' => array( 0 => 'javelin-behavior', diff --git a/webroot/rsrc/js/core/behavior-oncopy.js b/webroot/rsrc/js/core/behavior-oncopy.js index e048c442b5..445761510b 100644 --- a/webroot/rsrc/js/core/behavior-oncopy.js +++ b/webroot/rsrc/js/core/behavior-oncopy.js @@ -18,63 +18,48 @@ JX.behavior('phabricator-oncopy', function() { var zws = "\u200B"; // Unicode Zero-Width Space - document.body.oncopy = function(e) { + JX.enableDispatch(document.body, 'copy'); + JX.Stratcom.listen( + ['copy'], + null, + 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; + var selection; + var text; + if (window.getSelection) { + selection = window.getSelection(); + text = selection.toString(); + } else { + selection = document.selection; + text = selection.createRange().text; } - 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 { + if (text.indexOf(zws) == -1) { + // If there's no marker in the text, just let it copy normally. + return; + } - // In Firefox, we have to create a
 and select the text in it, then
-      // let the copy event fire. It has to be a 
 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 result = [];
 
-      var style = {
-        position: 'absolute',
-        left:     '-10000px'
-      };
-      var pre = JX.$N('pre', {style: style}, result);
-      document.body.appendChild(pre);
+      // 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");
 
-      // Select the text in the 
.
-      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?
-    }
-  };
+      var rawEvent = e.getRawEvent();
+      var clipboardData = 'clipboardData' in rawEvent ?
+        rawEvent.clipboardData :
+        window.clipboardData;
+      clipboardData.setData('Text', result);
+      e.prevent();
+    });
 });