diff --git a/static/js/copy_and_paste.js b/static/js/copy_and_paste.js new file mode 100644 index 0000000000..2f9653b032 --- /dev/null +++ b/static/js/copy_and_paste.js @@ -0,0 +1,142 @@ +var copy_and_paste = (function () { + +var exports = {}; // we don't actually export anything yet, but that's ok + +function find_boundary_tr(initial_tr, iterate_row) { + var j, skip_same_td_check = false; + var tr = initial_tr; + + // If the selection boundary is somewhere that does not have a + // parent tr, we should let the browser handle the copy-paste + // entirely on its own + if (tr.length === 0) { + return undefined; + } + + // If the selection boundary is on a table row that does not have an + // associated message id (because the user clicked between messages), + // then scan downwards until we hit a table row with a message id. + // To ensure we can't enter an infinite loop, bail out (and let the + // browser handle the copy-paste on its own) if we don't hit what we + // are looking for within 10 rows. + for (j = 0; (!tr.is('.message_row')) && j < 10; j++) { + tr = iterate_row(tr); + } + if (j === 10) { + return undefined; + } else if (j !== 0) { + // If we updated tr, then we are not dealing with a selection + // that is entirely within one td, and we can skip the same td + // check (In fact, we need to because it won't work correctly + // in this case) + skip_same_td_check = true; + } + return [rows.id(tr), skip_same_td_check]; +} + + +function copy_handler(e) { + var selection = window.getSelection(); + var i, range, ranges = [], startc, endc, initial_end_tr, start_id, end_id, row, message; + var start_data, end_data; + var skip_same_td_check = false; + var div = $('
').append($('').text(content.text())));
+ }
+
+ message = current_msg_list.get(rows.id(row));
+
+ var message_firstp = $(message.content).slice(0, 1);
+ message_firstp.prepend(message.sender_full_name + ": ");
+ div.append(message_firstp);
+ div.append($(message.content).slice(1));
+ }
+ }
+ }
+
+ if (window.bridge !== undefined) {
+ // If the user is running the desktop app,
+ // convert emoji images to plain text for
+ // copy-paste purposes.
+ ui.replace_emoji_with_text(div);
+ }
+
+ // Select div so that the browser will copy it
+ // instead of copying the original selection
+ div.css({position: 'absolute', 'left': '-99999px'})
+ .attr('id', 'copytempdiv');
+ $('body').append(div);
+ selection.selectAllChildren(div[0]);
+
+ // After the copy has happened, delete the div and
+ // change the selection back to the original selection
+ window.setTimeout(function () {
+ selection = window.getSelection();
+ selection.removeAllRanges();
+ _.each(ranges, function (range) {
+ selection.addRange(range);
+ });
+ $('#copytempdiv').remove();
+ },0);
+}
+
+$(function () {
+ $(document).bind('copy', copy_handler);
+});
+
+
+return exports;
+}());
diff --git a/static/js/ui.js b/static/js/ui.js
index f1e651ed37..021517f6e1 100644
--- a/static/js/ui.js
+++ b/static/js/ui.js
@@ -79,146 +79,12 @@ exports.page_down_the_right_amount = function () {
viewport.scrollTop(viewport.scrollTop() + delta);
};
-function find_boundary_tr(initial_tr, iterate_row) {
- var j, skip_same_td_check = false;
- var tr = initial_tr;
-
- // If the selection boundary is somewhere that does not have a
- // parent tr, we should let the browser handle the copy-paste
- // entirely on its own
- if (tr.length === 0) {
- return undefined;
- }
-
- // If the selection bounary is on a table row that does not have an
- // associated message id (because the user clicked between messages),
- // then scan downwards until we hit a table row with a message id.
- // To ensure we can't enter an infinite loop, bail out (and let the
- // browser handle the copy-paste on its own) if we don't hit what we
- // are looking for within 10 rows.
- for (j = 0; (!tr.is('.message_row')) && j < 10; j++) {
- tr = iterate_row(tr);
- }
- if (j === 10) {
- return undefined;
- } else if (j !== 0) {
- // If we updated tr, then we are not dealing with a selection
- // that is entirely within one td, and we can skip the same td
- // check (In fact, we need to because it won't work correctly
- // in this case)
- skip_same_td_check = true;
- }
- return [rows.id(tr), skip_same_td_check];
-}
-
exports.replace_emoji_with_text = function (element) {
element.find(".emoji").replaceWith(function () {
return $(this).attr("alt");
});
};
-function copy_handler(e) {
- var selection = window.getSelection();
- var i, range, ranges = [], startc, endc, initial_end_tr, start_id, end_id, row, message;
- var start_data, end_data;
- var skip_same_td_check = false;
- var div = $(' ').append($('').text(content.text())));
- }
-
- message = current_msg_list.get(rows.id(row));
-
- var message_firstp = $(message.content).slice(0, 1);
- message_firstp.prepend(message.sender_full_name + ": ");
- div.append(message_firstp);
- div.append($(message.content).slice(1));
- }
- }
- }
-
- if (window.bridge !== undefined) {
- // If the user is running the desktop app,
- // convert emoji images to plain text for
- // copy-paste purposes.
- exports.replace_emoji_with_text(div);
- }
-
- // Select div so that the browser will copy it
- // instead of copying the original selection
- div.css({position: 'absolute', 'left': '-99999px'})
- .attr('id', 'copytempdiv');
- $('body').append(div);
- selection.selectAllChildren(div[0]);
-
- // After the copy has happened, delete the div and
- // change the selection back to the original selection
- window.setTimeout(function () {
- selection = window.getSelection();
- selection.removeAllRanges();
- _.each(ranges, function (range) {
- selection.addRange(range);
- });
- $('#copytempdiv').remove();
- },0);
-}
-
-$(function () {
- $(document).bind('copy', copy_handler);
-});
-
/* We use 'visibility' rather than 'display' and jQuery's show() / hide(),
because we want to reserve space for the email address. This avoids
things jumping around slightly when the email address is shown. */
diff --git a/tools/jslint/check-all.js b/tools/jslint/check-all.js
index 7cdebc8cbe..984f9bf0bf 100644
--- a/tools/jslint/check-all.js
+++ b/tools/jslint/check-all.js
@@ -29,6 +29,7 @@ var globals =
+ ' avatar feature_flags search_suggestion referral stream_color Dict'
+ ' Filter summary admin stream_data muting WinChan muting_ui Socket channel gear_menu'
+ ' message_flags bot_data loading favicon resize scroll_bar condense floating_recipient_bar'
+ + ' copy_and_paste'
// colorspace.js
+ ' colorspace'
diff --git a/zproject/settings.py b/zproject/settings.py
index 34a6a948b6..e911bd64c3 100644
--- a/zproject/settings.py
+++ b/zproject/settings.py
@@ -548,6 +548,7 @@ JS_SPECS = {
'js/ui.js',
'js/scroll_bar.js',
'js/gear_menu.js',
+ 'js/copy_and_paste.js',
'js/popovers.js',
'js/typeahead_helper.js',
'js/search_suggestion.js',