diff --git a/.eslintrc.json b/.eslintrc.json
index 793cef2f02..42bc9161b8 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -150,6 +150,7 @@
"recent_senders": false,
"unread_ui": false,
"unread_ops": false,
+ "upload": false,
"user_events": false,
"Plotly": false,
"emoji_codes": false,
diff --git a/frontend_tests/node_tests/compose.js b/frontend_tests/node_tests/compose.js
index 8a2ad8d691..aec44c8f5c 100644
--- a/frontend_tests/node_tests/compose.js
+++ b/frontend_tests/node_tests/compose.js
@@ -52,6 +52,7 @@ zrequire('stream_data');
zrequire('compose_state');
zrequire('people');
zrequire('compose');
+zrequire('upload');
page_params.use_websockets = false;
var me = {
@@ -1296,7 +1297,7 @@ function test_with_mock_socket(test_params) {
return 'fake-html';
};
- compose.uploadStarted();
+ upload.uploadStarted();
assert.equal($("#compose-send-button").attr("disabled"), '');
assert($("#send-status").hasClass("alert-info"));
@@ -1311,7 +1312,7 @@ function test_with_mock_socket(test_params) {
assert.equal(width_percent, '39%');
width_update_checked = true;
};
- compose.progressUpdated(1, '', 39);
+ upload.progressUpdated(1, '', 39);
assert(width_update_checked);
}());
@@ -1336,7 +1337,7 @@ function test_with_mock_socket(test_params) {
function test(err, file, msg) {
setup_test();
- compose.uploadError(err, file);
+ upload.uploadError(err, file);
// The text function and html function in zjquery is not in sync
// with each other. QuotaExceeded changes html while all other errors
// changes body.
@@ -1420,7 +1421,7 @@ function test_with_mock_socket(test_params) {
}
setup();
- compose.uploadFinished(i, {}, response);
+ upload.uploadFinished(i, {}, response);
assert_side_effects();
}
diff --git a/static/js/compose.js b/static/js/compose.js
index b5c1134c10..36504e8588 100644
--- a/static/js/compose.js
+++ b/static/js/compose.js
@@ -19,113 +19,11 @@ exports.uploads_path = '/user_uploads';
exports.uploads_re = new RegExp("\\]\\(" + exports.uploads_domain + "(" + exports.uploads_path + "[^\\)]+)\\)", 'g');
exports.clone_file_input = undefined;
-function make_upload_absolute(uri) {
- if (uri.indexOf(exports.uploads_path) === 0) {
- // Rewrite the URI to a usable link
- return exports.uploads_domain + uri;
- }
- return uri;
-}
-
function make_uploads_relative(content) {
// Rewrite uploads in markdown links back to domain-relative form
return content.replace(exports.uploads_re, "]($1)");
}
-// This function resets an input type="file". Pass in the
-// jquery object.
-function clear_out_file_list(jq_file_list) {
- if (exports.clone_file_input !== undefined) {
- jq_file_list.replaceWith(exports.clone_file_input.clone(true));
- }
- // Hack explanation:
- // IE won't let you do this (untested, but so says StackOverflow):
- // $("#file_input").val("");
-}
-
-exports.uploadStarted = function () {
- $("#compose-send-button").attr("disabled", "");
- $("#send-status").addClass("alert-info")
- .show();
- $(".send-status-close").one('click', exports.abort_xhr);
- $("#error-msg").html(
- $("
").text(i18n.t("Uploading…"))
- .after('
'));
-};
-
-exports.progressUpdated = function (i, file, progress) {
- $("#upload-bar").width(progress + "%");
-};
-
-exports.uploadError = function (err, file) {
- var msg;
- $("#send-status").addClass("alert-error")
- .removeClass("alert-info");
- $("#compose-send-button").prop("disabled", false);
- switch (err) {
- case 'BrowserNotSupported':
- msg = i18n.t("File upload is not yet available for your browser.");
- break;
- case 'TooManyFiles':
- msg = i18n.t("Unable to upload that many files at once.");
- break;
- case 'FileTooLarge':
- // sanitization not needed as the file name is not potentially parsed as HTML, etc.
- var context = { file_name: file.name };
- msg = i18n.t('"__file_name__" was too large; the maximum file size is 25MiB.', context);
- break;
- case 'REQUEST ENTITY TOO LARGE':
- msg = i18n.t("Sorry, the file was too large.");
- break;
- case 'QuotaExceeded':
- var translation_part1 = i18n.t('Upload would exceed your maximum quota. You can delete old attachments to free up space.');
- var translation_part2 = i18n.t('Click here');
- msg = translation_part1 + ' ' + translation_part2 + '';
- $("#error-msg").html(msg);
- return;
- default:
- msg = i18n.t("An unknown error occurred.");
- break;
- }
- $("#error-msg").text(msg);
-};
-
-exports.uploadFinished = function (i, file, response) {
- if (response.uri === undefined) {
- return;
- }
- var textbox = $("#new_message_content");
- var split_uri = response.uri.split("/");
- var filename = split_uri[split_uri.length - 1];
- // Urgh, yet another hack to make sure we're "composing"
- // when text gets added into the composebox.
- if (!compose_state.composing()) {
- compose_actions.start('stream');
- }
-
- var uri = make_upload_absolute(response.uri);
-
- if (i === -1) {
- // This is a paste, so there's no filename. Show the image directly
- textbox.val(textbox.val() + "[pasted image](" + uri + ") ");
- } else {
- // This is a dropped file, so make the filename a link to the image
- textbox.val(textbox.val() + "[" + filename + "](" + uri + ")" + " ");
- }
- compose_ui.autosize_textarea();
- $("#compose-send-button").prop("disabled", false);
- $("#send-status").removeClass("alert-info")
- .hide();
-
- // In order to upload the same file twice in a row, we need to clear out
- // the #file_input element, so that the next time we use the file dialog,
- // an actual change event is fired. This is extracted to a function
- // to abstract away some IE hacks.
- clear_out_file_list($("#file_input"));
-};
-
function show_all_everyone_warnings() {
var stream_count = stream_data.get_subscriber_count(compose_state.stream_name()) || 0;
@@ -872,29 +770,7 @@ exports.initialize = function () {
Dropbox.choose(options);
});
- $("#compose").filedrop({
- url: "/json/user_uploads",
- fallback_id: "file_input",
- paramname: "file",
- maxfilesize: page_params.maxfilesize,
- data: {
- // the token isn't automatically included in filedrop's post
- csrfmiddlewaretoken: csrf_token,
- },
- raw_droppable: ['text/uri-list', 'text/plain'],
- drop: exports.uploadStarted,
- progressUpdated: exports.progressUpdated,
- error: exports.uploadError,
- uploadFinished: exports.uploadFinished,
- rawDrop: function (contents) {
- var textbox = $("#new_message_content");
- if (!compose_state.composing()) {
- compose_actions.start('stream');
- }
- textbox.val(textbox.val() + contents);
- compose_ui.autosize_textarea();
- },
- });
+ upload.initialize();
if (page_params.narrow !== undefined) {
if (page_params.narrow_topic !== undefined) {
diff --git a/static/js/upload.js b/static/js/upload.js
new file mode 100644
index 0000000000..72a5eba320
--- /dev/null
+++ b/static/js/upload.js
@@ -0,0 +1,138 @@
+var upload = (function () {
+
+var exports = {};
+
+function make_upload_absolute(uri) {
+ if (uri.indexOf(compose.uploads_path) === 0) {
+ // Rewrite the URI to a usable link
+ return compose.uploads_domain + uri;
+ }
+ return uri;
+}
+
+// This function resets an input type="file". Pass in the
+// jquery object.
+function clear_out_file_list(jq_file_list) {
+ if (compose.clone_file_input !== undefined) {
+ jq_file_list.replaceWith(compose.clone_file_input.clone(true));
+ }
+ // Hack explanation:
+ // IE won't let you do this (untested, but so says StackOverflow):
+ // $("#file_input").val("");
+}
+
+exports.uploadStarted = function () {
+ $("#compose-send-button").attr("disabled", "");
+ $("#send-status").addClass("alert-info").show();
+ $(".send-status-close").one('click', compose.abort_xhr);
+ $("#error-msg").html($("").text(i18n.t("Uploading…"))
+ .after('
'));
+};
+
+exports.progressUpdated = function (i, file, progress) {
+ $("#upload-bar").width(progress + "%");
+};
+
+exports.uploadError = function (err, file) {
+ var msg;
+ $("#send-status").addClass("alert-error")
+ .removeClass("alert-info");
+ $("#compose-send-button").prop("disabled", false);
+ switch (err) {
+ case 'BrowserNotSupported':
+ msg = i18n.t("File upload is not yet available for your browser.");
+ break;
+ case 'TooManyFiles':
+ msg = i18n.t("Unable to upload that many files at once.");
+ break;
+ case 'FileTooLarge':
+ // sanitization not needed as the file name is not potentially parsed as HTML, etc.
+ var context = {
+ file_name: file.name,
+ };
+ msg = i18n.t('"__file_name__" was too large; the maximum file size is 25MiB.', context);
+ break;
+ case 'REQUEST ENTITY TOO LARGE':
+ msg = i18n.t("Sorry, the file was too large.");
+ break;
+ case 'QuotaExceeded':
+ var translation_part1 = i18n.t('Upload would exceed your maximum quota. You can delete old attachments to free up space.');
+ var translation_part2 = i18n.t('Click here');
+ msg = translation_part1 + ' ' + translation_part2 + '';
+ $("#error-msg").html(msg);
+ return;
+ default:
+ msg = i18n.t("An unknown error occurred.");
+ break;
+ }
+ $("#error-msg").text(msg);
+};
+
+exports.uploadFinished = function (i, file, response) {
+ if (response.uri === undefined) {
+ return;
+ }
+ var textbox = $("#new_message_content");
+ var split_uri = response.uri.split("/");
+ var filename = split_uri[split_uri.length - 1];
+ // Urgh, yet another hack to make sure we're "composing"
+ // when text gets added into the composebox.
+ if (!compose_state.composing()) {
+ compose_actions.start('stream');
+ }
+
+ var uri = make_upload_absolute(response.uri);
+
+ if (i === -1) {
+ // This is a paste, so there's no filename. Show the image directly
+ textbox.val(textbox.val() + "[pasted image](" + uri + ") ");
+ } else {
+ // This is a dropped file, so make the filename a link to the image
+ textbox.val(textbox.val() + "[" + filename + "](" + uri + ")" + " ");
+ }
+ compose_ui.autosize_textarea();
+ $("#compose-send-button").prop("disabled", false);
+ $("#send-status").removeClass("alert-info")
+ .hide();
+
+ // In order to upload the same file twice in a row, we need to clear out
+ // the #file_input element, so that the next time we use the file dialog,
+ // an actual change event is fired. This is extracted to a function
+ // to abstract away some IE hacks.
+ clear_out_file_list($("#file_input"));
+};
+
+exports.initialize = function () {
+ $("#compose").filedrop({
+ url: "/json/user_uploads",
+ fallback_id: "file_input",
+ paramname: "file",
+ maxfilesize: page_params.maxfilesize,
+ data: {
+ // the token isn't automatically included in filedrop's post
+ csrfmiddlewaretoken: csrf_token,
+ },
+ raw_droppable: ['text/uri-list', 'text/plain'],
+ drop: exports.uploadStarted,
+ progressUpdated: exports.progressUpdated,
+ error: exports.uploadError,
+ uploadFinished: exports.uploadFinished,
+ rawDrop: function (contents) {
+ var textbox = $("#new_message_content");
+ if (!compose_state.composing()) {
+ compose_actions.start('stream');
+ }
+ textbox.val(textbox.val() + contents);
+ compose_ui.autosize_textarea();
+ },
+ });
+};
+
+return exports;
+}());
+
+if (typeof module !== 'undefined') {
+ module.exports = upload;
+}
diff --git a/tools/linter_lib/custom_check.py b/tools/linter_lib/custom_check.py
index 66a5a65c4b..7d063d8ce8 100644
--- a/tools/linter_lib/custom_check.py
+++ b/tools/linter_lib/custom_check.py
@@ -236,7 +236,7 @@ def build_custom_checkers(by_lang):
'frontend_tests/node_tests/compose.js',
'frontend_tests/node_tests/copy_and_paste.js',
'frontend_tests/node_tests/templates.js',
- 'static/js/compose.js',
+ 'static/js/upload.js',
'static/js/dynamic_text.js',
'static/js/stream_color.js',
]),
diff --git a/zproject/settings.py b/zproject/settings.py
index 73e373b285..c7bcbfe387 100644
--- a/zproject/settings.py
+++ b/zproject/settings.py
@@ -977,6 +977,7 @@ JS_SPECS = {
'js/compose_state.js',
'js/compose_actions.js',
'js/compose.js',
+ 'js/upload.js',
'js/stream_color.js',
'js/stream_data.js',
'js/topic_data.js',