diff --git a/webcompat/static/js/lib/bugform.js b/webcompat/static/js/lib/bugform.js
index f8daca676..9ceb9feff 100644
--- a/webcompat/static/js/lib/bugform.js
+++ b/webcompat/static/js/lib/bugform.js
@@ -5,6 +5,24 @@
/*eslint new-cap: ["error", { "capIsNewExceptions": ["Deferred"] }]*/
function BugForm() {
+ // Set up listener for message events from screenshot-enabled add-ons
+ // Runs only when the DOM is ready
+ var me = this;
+ window.addEventListener(
+ "message",
+ function(event) {
+ $(function() {
+ me.onReceiveMessage(event);
+ });
+ },
+ false
+ );
+
+ // the initialization of the rest of the form happens when the DOM is ready
+ $(BugForm.prototype.onDOMReadyInit.bind(this));
+}
+
+BugForm.prototype.onDOMReadyInit = function() {
this.clickedButton = null;
this.detailsInput = $("#details:hidden");
this.errorLabel = $(".js-error-upload");
@@ -49,7 +67,7 @@ function BugForm() {
valid: true,
helpText:
"Image must be one of the following: jpe, jpg, jpeg, png, gif, or bmp.",
- altHelpText: "Please choose a smaller image (< 4MB)"
+ altHelpText: "Please choose a smaller image (< 4MB)"
},
browser: {
el: $("#browser"),
@@ -84,627 +102,611 @@ function BugForm() {
this.stepsToReproduceField = this.inputs.steps_reproduce.el;
this.contactField = this.inputs.contact.el;
- this.init = function() {
- this.checkURLValidity = this.checkURLValidity.bind(this);
- this.checkDescriptionValidity = this.checkDescriptionValidity.bind(this);
- this.checkProblemTypeValidity = this.checkProblemTypeValidity.bind(this);
- this.checkImageTypeValidity = this.checkImageTypeValidity.bind(this);
- this.checkGitHubUsername = this.checkGitHubUsername.bind(this);
- this.checkOptionalNonEmpty = this.checkOptionalNonEmpty.bind(this);
- this.storeClickedButton = this.storeClickedButton.bind(this);
- this.onFormSubmit = this.onFormSubmit.bind(this);
- this.onReceiveMessage = this.onReceiveMessage.bind(this);
- this.preventSubmitByEnter = this.preventSubmitByEnter.bind(this);
-
- // Make sure we're not getting a report
- // about our own site before checking params.
- if (!this.isSelfReport()) {
- this.checkParams();
- }
-
- this.disableSubmits();
- this.urlField.on("blur input", this.checkURLValidity);
- this.descField.on("blur input", this.checkDescriptionValidity);
- this.problemType.on("change", this.checkProblemTypeValidity);
- this.uploadField.on("change", this.checkImageTypeValidity);
- this.osField.on(
- "blur",
- this.checkOptionalNonEmpty.bind(this, this.osField)
- );
- this.browserField.on(
- "blur",
- this.checkOptionalNonEmpty.bind(this, this.browserField)
- );
- this.contactField.on("blur input", this.checkGitHubUsername);
- this.submitButtons.on("click", this.storeClickedButton);
- this.form.on("submit", this.onFormSubmit);
-
- // prevent submit by hitting enter key for single line input fields
- this.form.on("keypress", ":input:not(textarea)", this.preventSubmitByEnter);
-
- // See if the user already has a valid form
- // (after a page refresh, back button, etc.)
- this.checkForm();
-
- // Set up listener for message events from screenshot-enabled add-ons
- window.addEventListener("message", this.onReceiveMessage, false);
- };
-
- this.onReceiveMessage = function(event) {
- // We're getting a report about our own site, so let's bail.
- if (this.isSelfReport()) {
- return false;
- }
-
- // Make sure the data is coming from a trusted source.
- // (i.e., our add-on or some other priviledged code sent it)
- if (location.origin === event.origin) {
- // See https://github.com/webcompat/webcompat.com/issues/1252 to track
- // the work of only accepting blobs, which should simplify things.
- if (event.data instanceof Blob) {
- // convertToDataURI sends the resulting string to the upload
- // callback.
- this.convertToDataURI(event.data, this.showUploadPreview);
- } else {
- // ...the data is already a data URI string
- this.showUploadPreview(event.data);
- }
- }
- };
-
- this.preventSubmitByEnter = function(event) {
- if (event.key === "Enter") {
- event.preventDefault();
- }
- };
-
- this.showUploadPreview = function(dataURI) {
- // The final size of Base64-encoded binary data is ~equal to
- // 1.37 times the original data size + 814 bytes (for headers).
- // so, bytes = (encoded_string.length - 814) / 1.37)
- // see https://en.wikipedia.org/wiki/Base64#MIME
- if (String(dataURI).length - 814 / 1.37 > this.UPLOAD_LIMIT) {
- this.downsampleImage(
- dataURI,
- _.bind(function(downsampledData) {
- // Recurse until it's small enough for us to upload.
- this.showUploadPreview(downsampledData);
- }, this)
- );
- } else {
- this.makeValid("image");
- this.addPreviewBackground(dataURI);
- }
- };
-
- this.downsampleImage = function(dataURI, callback) {
- var img = document.createElement("img");
- var canvas = document.createElement("canvas");
- var ctx = canvas.getContext("2d");
-
- img.onload = function() {
- // scale the tmp canvas to 50%
- canvas.width = Math.floor(img.width / 2);
- canvas.height = Math.floor(img.height / 2);
- ctx.scale(0.5, 0.5);
- // draw back in the screenshot (at 50% scale)
- // and re-serialize to data URI
- ctx.drawImage(img, 0, 0);
- // Note: this will convert GIFs to JPEG, which breaks
- // animated GIFs. However, this only will happen if they
- // were above the upload limit size. So... sorry?
- var screenshotData = canvas.toDataURL("image/jpeg", 0.8);
- (img = null), (canvas = null);
-
- callback(screenshotData);
- };
-
- img.src = dataURI;
- };
-
- // Is the user trying to report a site against webcompat.com itself?
- this.isSelfReport = function(href) {
- href = href || location.href;
- var url = href.match(this.urlParamRegExp);
- if (url !== null) {
- if (_.includes(decodeURIComponent(url[0]), location.origin)) {
- return true;
- }
- }
+ return this.init();
+};
+
+BugForm.prototype.init = function() {
+ // Make sure we're not getting a report
+ // about our own site before checking params.
+ if (!this.isSelfReport()) {
+ this.checkParams();
+ }
+
+ this.disableSubmits();
+ this.urlField.on("blur input", this.checkURLValidity.bind(this));
+ this.descField.on("blur input", this.checkDescriptionValidity.bind(this));
+ this.problemType.on("change", this.checkProblemTypeValidity.bind(this));
+ this.uploadField.on("change", this.checkImageTypeValidity.bind(this));
+ this.osField.on("blur", this.checkOptionalNonEmpty.bind(this, this.osField));
+ this.browserField.on(
+ "blur",
+ this.checkOptionalNonEmpty.bind(this, this.browserField)
+ );
+ this.contactField.on("blur input", this.checkGitHubUsername.bind(this));
+ this.submitButtons.on("click", this.storeClickedButton.bind(this));
+ this.form.on("submit", this.onFormSubmit.bind(this));
+
+ // prevent submit by hitting enter key for single line input fields
+ this.form.on(
+ "keypress",
+ ":input:not(textarea)",
+ this.preventSubmitByEnter.bind(this)
+ );
+
+ // See if the user already has a valid form
+ // (after a page refresh, back button, etc.)
+ this.checkForm();
+};
+
+BugForm.prototype.onReceiveMessage = function(event) {
+ // We're getting a report about our own site, so let's bail.
+ if (this.isSelfReport()) {
return false;
- };
-
- // Do some extra work based on the GET params that come with the request
- this.checkParams = function() {
- // Don't bother doing any work for bare requests.
- if (!location.search) {
- return;
- }
-
- var url = location.href.match(this.urlParamRegExp);
- if (url !== null) {
- url = this.trimWyciwyg(decodeURIComponent(url[1]));
- this.urlField.val(url);
- this.makeValid("url");
- }
-
- // If we have a problem_type param, and it matches the value, select it for
- // the user. see https://github.com/webcompat/webcompat.com/blob/34c3b6b1a1116b401a9a442685131ae747045f67/webcompat/form.py#L38
- // for possible matching values
- var problemType = location.href.match(/problem_type=([^&]*)/);
- if (problemType !== null) {
- $("[value=" + problemType[1] + "]").click();
- }
-
- // If we have details, put it inside a hidden input and append it to the
- // form.
- var details = location.href.match(/details=([^&]*)/);
- if (details) {
- this.addDetails(details[1]);
- }
- };
-
- this.addDetails = function(detailsParam) {
- // The content of the details param may be encoded via
- // application/x-www-form-urlencoded, so we need to change the
- // + (SPACE) to %20 before decoding
- this.detailsInput.val(
- decodeURIComponent(detailsParam.replace(/\+/g, "%20"))
- );
- };
-
- this.storeClickedButton = function(event) {
- this.clickedButton = event.target.name;
- };
-
- this.trimWyciwyg = function(url) {
- // Trim wyciwyg://N/ from URL, if found.
- // See https://bugzilla.mozilla.org/show_bug.cgi?id=1098037 &
- // https://en.wikipedia.org/wiki/WYCIWYG
- var wyciwygRe = /(wyciwyg:\/\/\d+\/)/i;
- if (url.search(wyciwygRe) !== 0) {
- return url;
- } else {
- return url.replace(wyciwygRe, "");
- }
- };
-
- this.disableSubmits = function() {
- this.submitButtons.prop("disabled", true);
- this.submitButtons.addClass("is-disabled");
- };
-
- this.enableSubmits = function() {
- this.submitButtons.prop("disabled", false);
- this.submitButtons.removeClass("is-disabled");
- };
-
- this.checkProblemTypeValidity = function() {
- if (!$("[name=problem_category]:checked").length) {
- this.makeInvalid("problem_type");
- } else {
- this.makeValid("problem_type");
- }
- };
-
- this.checkImageTypeValidity = function(event) {
- var splitImg = this.uploadField.val().split(".");
- var ext = splitImg[splitImg.length - 1].toLowerCase();
- var allowed = ["jpg", "jpeg", "jpe", "png", "gif", "bmp"];
- // Bail if there's no image.
- if (!this.uploadField.val()) {
- return;
- }
-
- if (!_.includes(allowed, ext)) {
- this.makeInvalid("image");
- } else {
- this.makeValid("image");
- if (event) {
- // We can just grab the 0th one, because we only allow uploading
- // a single image at a time (for now)
- this.convertToDataURI(event.target.files[0], this.showUploadPreview);
- }
- }
-
- // null out input.value so we get a consistent
- // change event across browsers
- event.target.value = null;
- };
-
- this.isReportableURL = function(url) {
- var ok = url && (_.startsWith(url, "http:") || _.startsWith(url, "https:"));
- ok &= !(_.startsWith(url, "http:// ") || _.startsWith(url, "https:// "));
- return ok;
- };
-
- /* Check to see that the URL input element is not empty,
- or if it's a non-webby scheme. */
- this.checkURLValidity = function() {
- var val = this.urlField.val();
- if ($.trim(val) === "" || !this.isReportableURL(val)) {
- this.makeInvalid("url");
- } else {
- this.makeValid("url");
- }
- };
-
- /* Check to see that the description input element is not empty. */
- this.checkDescriptionValidity = function() {
- var val = this.descField.val();
- if ($.trim(val) === "") {
- this.makeInvalid("description");
- } else {
- this.makeValid("description");
- }
- };
-
- /* Check if Browser and OS are empty or not, only
- so we can set them to valid (there is no invalid state) */
- this.checkOptionalNonEmpty = function(input) {
- var inputId = input.prop("id");
-
- if (input.val()) {
- this.makeValid(inputId);
- } else {
- this.makeInvalid(inputId);
- }
- };
-
- /*
- Check a string is a valid GitHub username
- - maximum 39 chars
- - alphanumerical characters and hyphens
- - no two consecutive hyphens
- */
- this.isValidGitHubUsername = function(contact) {
- return this.githubRegexp.test(contact);
- };
-
- /* Check to see if the GitHub username has the right syntax. */
- this.checkGitHubUsername = function() {
- var contact = this.contactField.val();
- if (this.isValidGitHubUsername(contact) || $.trim(contact) === "") {
- this.makeValid("contact");
+ }
+
+ // Make sure the data is coming from a trusted source.
+ // (i.e., our add-on or some other priviledged code sent it)
+ if (location.origin === event.origin) {
+ // See https://github.com/webcompat/webcompat.com/issues/1252 to track
+ // the work of only accepting blobs, which should simplify things.
+ if (event.data instanceof Blob) {
+ // convertToDataURI sends the resulting string to the upload
+ // callback.
+ this.convertToDataURI(event.data, this.showUploadPreview.bind(this));
} else {
- this.makeInvalid("contact");
- }
- };
-
- this.checkForm = function() {
- // Run through and see if there's any user input in the
- // required inputs
- var inputs = [
- this.problemType.filter(":checked").length,
- this.urlField.val(),
- this.descField.val(),
- this.uploadField.val()
- ];
- if (_.some(inputs, Boolean)) {
- // then, check validity
- this.checkURLValidity();
- this.checkDescriptionValidity();
- this.checkProblemTypeValidity();
- this.checkImageTypeValidity();
- this.checkGitHubUsername();
- // and open the form, if it's not already open
- if (!this.reportButton.hasClass("is-open")) {
- this.reportButton.click();
- }
- }
- // Make sure we only do this if the inputs exist on the page
- if (this.browserField.length) {
- this.checkOptionalNonEmpty(this.browserField);
- }
- if (this.osField.length) {
- this.checkOptionalNonEmpty(this.osField);
- }
- };
-
- /* makeInvalid can take an {altHelp: true} options argument to select
- alternate helpText to display */
- this.makeInvalid = function(id, opts) {
- // Early return if inline help is already in place.
- if (this.inputs[id].valid === false) {
- return;
- }
-
- var inlineHelp = $("", {
- class: "label-icon-message form-message-error",
- text:
- opts && opts.altHelp
- ? this.inputs[id].altHelpText
- : this.inputs[id].helpText
- });
-
- this.inputs[id].valid = false;
- this.inputs[id].el
- .parents(".js-Form-group")
- .removeClass("is-validated js-no-error")
- .addClass("is-error js-form-error");
-
- switch (id) {
- case "os":
- case "browser":
- // remove error classes, because these inputs are optional
- this.inputs[id].el
- .parents(".js-Form-group")
- .removeClass("is-error js-form-error");
- break;
- case "url":
- case "contact":
- case "description":
- case "problem_type":
- inlineHelp.insertAfter("label[for=" + id + "]");
- break;
- case "image":
- // hide the error in case we already saw one
- $(".form-upload-error").remove();
-
- inlineHelp
- .removeClass("form-message-error")
- .addClass("form-upload-error")
- .appendTo(".js-error-upload");
-
- $(".js-label-upload").addClass("is-hidden");
- $(".js-remove-upload").addClass("is-hidden");
- $(".js-error-upload").removeClass("is-hidden");
-
- $(".form-message-error").hide();
- $(".form-input-validation .error").hide();
- // "reset" the form field, because the file would get rejected
- // from the server anyways.
- this.uploadField.val(this.uploadField.get(0).defaultValue);
- // return early because we just cleared out the input.
- // someone might decide to just not select an image.
- return;
- }
-
- this.disableSubmits();
- };
-
- this.makeValid = function(id) {
- this.inputs[id].valid = true;
- this.inputs[id].el
- .parents(".js-Form-group")
- .removeClass("is-error js-form-error")
- .addClass("is-validated js-no-error");
-
- this.inputs[id].el
- .parents(".js-Form-group")
- .find(".form-message-error")
- .remove();
-
- if (
- this.inputs["url"].valid &&
- this.inputs["problem_type"].valid &&
- this.inputs["image"].valid &&
- this.inputs["description"].valid
- ) {
- this.enableSubmits();
- }
- };
- /*
- If the users browser understands the FileReader API, show a preview
- of the image they're about to load, then invoke the passed in callback
- with the result of reading the blobOrFile as a dataURI.
- */
- this.convertToDataURI = function(blobOrFile, callback) {
- if (!(window.FileReader && window.File)) {
- return;
- }
-
- // One last image type validation check.
- if (!blobOrFile.type.match("image.*")) {
- this.makeInvalid("image");
- return;
- }
-
- var reader = new FileReader();
- reader.onload = function(event) {
- var dataURI = event.target.result;
- callback(dataURI);
- };
- reader.readAsDataURL(blobOrFile);
- };
-
- this.addPreviewBackground = function(dataURI) {
- if (!_.startsWith(dataURI, "data:image/")) {
- return;
+ // ...the data is already a data URI string
+ this.showUploadPreview(event.data);
}
+ }
+};
- this.previewEl.css({
- background: "url(" + dataURI + ") no-repeat center / contain"
- });
-
- this.hasImage = true;
- this.showRemoveUpload();
- };
- /*
- Allow users to remove an image from the form upload.
- */
- this.showRemoveUpload = function() {
- // hide upload image errors (this will no-op if the user never saw one)
- $(".form-upload-error").remove();
-
- this.errorLabel.addClass("is-hidden");
- this.uploadLabel.removeClass("visually-hidden");
-
- this.removeBanner.removeClass("is-hidden");
- this.removeBanner.attr("tabIndex", "0");
- this.uploadLabel.addClass("visually-hidden");
- this.removeBanner.on("click", this.removeUploadPreview);
- };
-
- /*
- Remove the upload image preview and hide the banner.
- */
- this.removeUploadPreview = function() {
- this.previewEl.css("background", "none");
- this.removeBanner.addClass("is-hidden");
- this.removeBanner.attr("tabIndex", "-1");
- this.uploadLabel.removeClass("visually-hidden").removeClass("is-hidden");
- this.removeBanner.off("click");
- this.removeBanner.get(0).blur();
-
- this.hasImage = false;
-
- // clear out the input[type=file] as well
- this.uploadField.val(this.uploadField.get(0).defaultValue);
- };
-
- this.showLoadingIndicator = function() {
- this.loadingIndicator.addClass("is-active");
- };
-
- this.hideLoadingIndicator = function() {
- this.loadingIndicator.removeClass("is-active");
- };
-
- this.onFormSubmit = function(event) {
- this.showLoadingIndicator();
- this.maybeUploadImage(event).then(this.submitForm, this.handleUploadError);
- };
-
- /*
- If we have an image, kick off the uploadImage promise, otherwise
- resolve immediately.
- */
- this.maybeUploadImage = function(event) {
+BugForm.prototype.preventSubmitByEnter = function(event) {
+ if (event.key === "Enter") {
event.preventDefault();
- var dfd = $.Deferred();
-
- if (!this.hasImage) {
- return dfd.resolve();
- }
-
- this.uploadImage(this.getDataURIFromPreviewEl())
- .then(this.addImageURL)
- .then(dfd.resolve, dfd.reject);
-
- return dfd.promise();
- };
-
- /*
- Upload the image before form submission so we can
- put an image link in the bug description.
- */
- this.uploadImage = function(dataURI) {
- var dfd = $.Deferred();
- this.disableSubmits();
-
- $(".js-remove-upload").addClass("is-hidden");
-
- var formdata = new FormData();
- formdata.append("image", dataURI);
-
- $.ajax({
- contentType: false,
- processData: false,
- data: formdata,
- method: "POST",
- url: "/upload/"
- }).then(dfd.resolve, dfd.reject);
-
- return dfd.promise();
- };
-
- /*
- React to server-side errors related to images by showing a flash
- message to the user, and clearing out the bad image and preview.
-
- If we're here, the attempted form submission failed.
- */
- this.handleUploadError = function(response) {
- if (response && response.status === 415) {
- wcEvents.trigger("flash:error", {
- message: this.inputs.image.helpText,
- timeout: 5000
- });
- }
-
- if (response && response.status === 413) {
- wcEvents.trigger("flash:error", {
- message:
- "The image is too big! Please choose something smaller than 4MB.",
- timeout: 5000
- });
+ }
+};
+
+BugForm.prototype.showUploadPreview = function(dataURI) {
+ // The final size of Base64-encoded binary data is ~equal to
+ // 1.37 times the original data size + 814 bytes (for headers).
+ // so, bytes = (encoded_string.length - 814) / 1.37)
+ // see https://en.wikipedia.org/wiki/Base64#MIME
+ if (String(dataURI).length - 814 / 1.37 > this.UPLOAD_LIMIT) {
+ this.downsampleImage(
+ dataURI,
+ _.bind(function(downsampledData) {
+ // Recurse until it's small enough for us to upload.
+ this.showUploadPreview(downsampledData);
+ }, this)
+ );
+ } else {
+ this.makeValid("image");
+ this.addPreviewBackground(dataURI);
+ }
+};
+
+BugForm.prototype.downsampleImage = function(dataURI, callback) {
+ var img = document.createElement("img");
+ var canvas = document.createElement("canvas");
+ var ctx = canvas.getContext("2d");
+
+ img.onload = function() {
+ // scale the tmp canvas to 50%
+ canvas.width = Math.floor(img.width / 2);
+ canvas.height = Math.floor(img.height / 2);
+ ctx.scale(0.5, 0.5);
+ // draw back in the screenshot (at 50% scale)
+ // and re-serialize to data URI
+ ctx.drawImage(img, 0, 0);
+ // Note: this will convert GIFs to JPEG, which breaks
+ // animated GIFs. However, this only will happen if they
+ // were above the upload limit size. So... sorry?
+ var screenshotData = canvas.toDataURL("image/jpeg", 0.8);
+ (img = null), (canvas = null);
+
+ callback(screenshotData);
+ };
+
+ img.src = dataURI;
+};
+
+// Is the user trying to report a site against webcompat.com itself?
+BugForm.prototype.isSelfReport = function(href) {
+ href = href || location.href;
+ var url = href.match(this.urlParamRegExp);
+ if (url !== null) {
+ if (_.includes(decodeURIComponent(url[0]), location.origin)) {
+ return true;
+ }
+ }
+ return false;
+};
+
+// Do some extra work based on the GET params that come with the request
+BugForm.prototype.checkParams = function() {
+ // Don't bother doing any work for bare requests.
+ if (!location.search) {
+ return;
+ }
+
+ var url = location.href.match(this.urlParamRegExp);
+ if (url !== null) {
+ url = this.trimWyciwyg(decodeURIComponent(url[1]));
+ this.urlField.val(url);
+ this.makeValid("url");
+ }
+
+ // If we have a problem_type param, and it matches the value, select it for
+ // the user. see https://github.com/webcompat/webcompat.com/blob/34c3b6b1a1116b401a9a442685131ae747045f67/webcompat/form.py#L38
+ // for possible matching values
+ var problemType = location.href.match(/problem_type=([^&]*)/);
+ if (problemType !== null) {
+ $("[value=" + problemType[1] + "]").click();
+ }
+
+ // If we have details, put it inside a hidden input and append it to the
+ // form.
+ var details = location.href.match(/details=([^&]*)/);
+ if (details) {
+ this.addDetails(details[1]);
+ }
+};
+
+BugForm.prototype.addDetails = function(detailsParam) {
+ // The content of the details param may be encoded via
+ // application/x-www-form-urlencoded, so we need to change the
+ // + (SPACE) to %20 before decoding
+ this.detailsInput.val(decodeURIComponent(detailsParam.replace(/\+/g, "%20")));
+};
+
+BugForm.prototype.storeClickedButton = function(event) {
+ this.clickedButton = event.target.name;
+};
+
+BugForm.prototype.trimWyciwyg = function(url) {
+ // Trim wyciwyg://N/ from URL, if found.
+ // See https://bugzilla.mozilla.org/show_bug.cgi?id=1098037 &
+ // https://en.wikipedia.org/wiki/WYCIWYG
+ var wyciwygRe = /(wyciwyg:\/\/\d+\/)/i;
+ if (url.search(wyciwygRe) !== 0) {
+ return url;
+ } else {
+ return url.replace(wyciwygRe, "");
+ }
+};
+
+BugForm.prototype.disableSubmits = function() {
+ this.submitButtons.prop("disabled", true);
+ this.submitButtons.addClass("is-disabled");
+};
+
+BugForm.prototype.enableSubmits = function() {
+ this.submitButtons.prop("disabled", false);
+ this.submitButtons.removeClass("is-disabled");
+};
+
+BugForm.prototype.checkProblemTypeValidity = function() {
+ if (!$("[name=problem_category]:checked").length) {
+ this.makeInvalid("problem_type");
+ } else {
+ this.makeValid("problem_type");
+ }
+};
+
+BugForm.prototype.checkImageTypeValidity = function(event) {
+ var splitImg = this.uploadField.val().split(".");
+ var ext = splitImg[splitImg.length - 1].toLowerCase();
+ var allowed = ["jpg", "jpeg", "jpe", "png", "gif", "bmp"];
+ // Bail if there's no image.
+ if (!this.uploadField.val()) {
+ return;
+ }
+
+ if (!_.includes(allowed, ext)) {
+ this.makeInvalid("image");
+ } else {
+ this.makeValid("image");
+ if (event) {
+ // We can just grab the 0th one, because we only allow uploading
+ // a single image at a time (for now)
+ this.convertToDataURI(
+ event.target.files[0],
+ this.showUploadPreview.bind(this)
+ );
}
-
- this.hideLoadingIndicator();
- this.removeUploadPreview();
- };
-
- this.submitForm = function() {
- var dfd = $.Deferred();
- var formEl = this.form.get(0);
- // Calling submit() manually on the form won't contain details
- // about which