diff --git a/README.md b/README.md index 5904951..a00e12e 100644 --- a/README.md +++ b/README.md @@ -152,16 +152,19 @@ easyMDE.value('New input for **EasyMDE**'); - **imageMaxSize**: Maximum image size in bytes, checked before upload (note: never trust client, always check image size at server-side). Defaults to `1024*1024*2` (2Mb). - **imageAccept**: A comma-separated list of mime-types used to check image type before upload (note: never trust client, always check file types at server-side). Defaults to `image/png, image/jpeg`. - **imageUploadEndpoint**: The endpoint where the images data will be sent, via an asynchronous *POST* request. The server is supposed to save this image, and if it's successful, return a 200-OK HTTP response containing the relative path of the image. No default value. -- **imageTexts**: Several string literals used in image-upload features: - - **sbInit**: Status message displayed initially if `uploadImage` is set to `true`. Defaults to `Attach files by drag and dropping or pasting from clipboard.`, - - **sbOnDragEnter**: Status message displayed when the user drags a file to the text area. Defaults to `Drop image to upload it.`, +- **imageTexts**: Texts displayed to the user (mainly on the status bar) for the import image feature, where `#image_name#`, `#image_size#` and `#image_max_size#` will replaced by their respective values, that can be used for customization or internationalization: + - **sbInit**: Status message displayed initially if `uploadImage` is set to `true`. Defaults to `Attach files by drag and dropping or pasting from clipboard.`. + - **sbOnDragEnter**: Status message displayed when the user drags a file to the text area. Defaults to `Drop image to upload it.`. - **sbOnDrop**: Status message displayed when the user drops a file in the text area. Defaults to `Uploading images #images_names#`. - - **sbProgress**: Status message displayed to show uploading progress. Defaults to `Uploading #file_name#: #progress#%`, - - **sbOnUploaded**: Status message displayed when the image has been uploaded. Defaults to `Uploaded #image_name#`, - - **errorImport**: Error message prompted when the served did not return a 200 response code. Defaults to `Can not import #image_name#`, - - **errorImageTooBig**: Error message prompted to the user when the size of the image being imported is bigger than the `imageMaxSize`, where `#image_name#`, `#image_size#` and `#image_max_size#` will replaced by their respective values. Defaults to `Image #image_name# is too big (#image_size#).\n' + - 'Maximum file size is #image_max_size#.`. + - **sbProgress**: Status message displayed to show uploading progress. Defaults to `Uploading #file_name#: #progress#%`. + - **sbOnUploaded**: Status message displayed when the image has been uploaded. Defaults to `Uploaded #image_name#`. - **sizeUnits**: A comma-separated list of units used to display messages with human-readable file sizes. Defaults to `b,Kb,Mb`. +- **errorMessages**: Errors displayed to the user, mainly on alert popups, where `#image_name#`, `#image_size#` and `#image_max_size#` will replaced by their respective values, that can be used for customization or internationalization: + - **noFileGiven**: The server did not receive any file from the user. Defaults to `You must select a file.`. + - **imageTypeNotAllowed**: The user send a file type which doesn't match the `imageAccept` list, or the server returned this error code. Defaults to `This image type is not allowed.`. + - **imageTooLarge**: The size of the image being imported is bigger than the `imageMaxSize`, or if the server returned this error code. Defaults to `Image #image_name# is too big (#image_size#).\nMaximum file size is #image_max_size#.`. + - **imageImportError**: An unexpected error occurred when uploading the image. Defaults to `Something went wrong when uploading the image #image_name#.`. + - **renderingConfig**: Adjust settings for parsing the Markdown during previewing (not editing). - **codeSyntaxHighlighting**: If set to `true`, will highlight using [highlight.js](https://github.com/isagalaev/highlight.js). Defaults to `false`. To use this feature you must include highlight.js on your page or pass in using the `hljs` option. For example, include the script and the CSS files like:
``
`` - **hljs**: An injectible instance of [highlight.js](https://github.com/isagalaev/highlight.js). If you don't want to rely on the global namespace (`window.hljs`), you can provide an instance here. Defaults to `undefined`. diff --git a/src/js/easymde.js b/src/js/easymde.js index b74aa6f..51fd564 100644 --- a/src/js/easymde.js +++ b/src/js/easymde.js @@ -705,6 +705,7 @@ function drawImage(editor) { /** * Action for opening the browse-file window to upload an image to a server. + * @param editor {EasyMDE} The EasyMDE object */ function drawUploadedImage(editor) { // TODO: Draw the image template with a fake url, ie: '![](importing foo.png...)' @@ -714,7 +715,7 @@ function drawUploadedImage(editor) { /** * Action executed after an image have been successfully imported on the server. - * @param editor The EasyMDE object + * @param editor {EasyMDE} The EasyMDE object * @param url {string} The url of the uploaded image */ function afterImageUploaded(editor, url) { @@ -1411,18 +1412,31 @@ var blockStyles = { 'italic': '*', }; +/** + * Texts displayed to the user (mainly on the status bar) for the import image + * feature. Can be used for customization or internationalization. + */ var imageTexts = { sbInit: 'Attach files by drag and dropping or pasting from clipboard.', sbOnDragEnter: 'Drop image to upload it.', sbOnDrop: 'Uploading image #images_names#...', sbProgress: 'Uploading #file_name#: #progress#%', sbOnUploaded: 'Uploaded #image_name#', - errorImport: 'Can not import #image_name#', - errorImageTooBig: 'Image #image_name# is too big (#image_size#).\n' + - 'Maximum file size is #image_max_size#.', sizeUnits: 'b,Kb,Mb', }; +/** + * Errors displayed to the user, mainly on alert popups. Can be used for + * customization or internationalization. + */ +var errorMessages = { + noFileGiven: 'You must select a file.', + imageTypeNotAllowed: 'This image type is not allowed.', + imageTooLarge: 'Image #image_name# is too big (#image_size#).\n' + + 'Maximum file size is #image_max_size#.', + imageImportError: 'Something went wrong when uploading the image #image_name#.', +}; + /** * Interface of EasyMDE. */ @@ -1534,11 +1548,12 @@ function EasyMDE(options) { options.minHeight = options.minHeight || '300px'; - // import-image default configuration + // Import-image default configuration options.uploadImage = options.uploadImage || false; options.imageMaxSize = options.imageMaxSize || 1024*1024*2; - options.imageTexts = extend({}, imageTexts, options.imageTexts || {}); options.imageAccept = options.imageAccept || 'image/png, image/jpeg'; + options.imageTexts = extend({}, imageTexts, options.imageTexts || {}); + options.errorMessages = extend({}, errorMessages, options.errorMessages || {}); // Change unique_id to uniqueId for backwards compatibility @@ -1605,18 +1620,17 @@ EasyMDE.prototype.uploadImages = function(files) { this.uploadImage(files[i], function onSuccess(imageUrl) { afterImageUploaded(self, imageUrl); - }, function onFailure(imageName, errorStatus, errorStatusText) { - alert(self.options.imageTexts.errorImport.replace('#image_name#', imageName)); - console.log('EasyMDE: error ' + errorStatus + ' when importing image ' + imageName + ': ' + errorStatusText); + }, function onFailure(error) { + alert(error); }); } this.updateStatusBar('upload-image', self.options.imageTexts.sbOnDrop.replace('#images_names#', names.join(', '))); }; /** - * - * @param itemName - * @param content + * Update an item in the status bar. + * @param itemName {string} The name of the item to update (ie. 'upload-image', 'autosave', etc.). + * @param content {string} the new content of the item to write in the status bar. */ EasyMDE.prototype.updateStatusBar = function(itemName, content) { var matchingClasses = this.gui.statusbar.getElementsByClassName(itemName); @@ -1890,23 +1904,23 @@ EasyMDE.prototype.clearAutosavedValue = function () { /** * Upload an image to the server. * - * @param file {File} The image to upload as a HTML5 File object (https://developer.mozilla.org/en-US/docs/Web/API/File) - * @param onSuccess {function} A callback function to execute after the image have been successfully uploaded, with parameters: + * @param file {File} The image to upload, as a HTML5 File object (https://developer.mozilla.org/en-US/docs/Web/API/File) + * @param onSuccess {function} A callback function to execute after the image has been successfully uploaded, with one parameter: * - url (string): The URL of the uploaded image. - * @param onError {function} A callback function to execute when the image upload fails, with parameters: - * - fileName: the name of the image file provided by the user. - * - errorStatus (number): The status of the response of the request, provided by XMLHttpRequest (see https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/status). - * - errorStatusText (string): the response string returned by the HTTP server, provided by XMLHttpRequest (see https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/statusText). + * @param onError {function} A callback function to execute when the image upload fails, with one parameter: + * - error (string): the detailed error to display to the user (based on messages from options.errorMessages). */ EasyMDE.prototype.uploadImage = function(file, onSuccess, onError) { - if (file.size >= this.options.imageMaxSize) { - var units = this.options.imageTexts.sizeUnits.split(','); - alert(this.options.imageTexts.errorImageTooBig - .replace('#image_name#', file.name) - .replace('#image_size#', humanFileSize(file.size, units)) - .replace('#image_max_size#', humanFileSize(this.options.imageMaxSize, units)) - ); - return; + function fillErrorMessage(errorMessage) { + var units = self.options.imageTexts.sizeUnits.split(','); + return errorMessage + .replace('#image_name#', file.name) + .replace('#image_size#', humanFileSize(file.size, units)) + .replace('#image_max_size#', humanFileSize(self.options.imageMaxSize, units)); + } + + if (file.size > this.options.imageMaxSize) { + onError(fillErrorMessage(this.options.errorMessages.imageTooLarge)); } var formData = new FormData(); @@ -1914,24 +1928,42 @@ EasyMDE.prototype.uploadImage = function(file, onSuccess, onError) { var request = new XMLHttpRequest(); request.open('POST', this.options.imageUploadEndpoint); request.send(formData); - var self = this; + request.onprogress = function (event) { if (event.lengthComputable) { - // TODO: test with a big image on a remote web server + // FIXME: progress doesn't work well var progress = '' + Math.round((event.loaded * 100) / event.total); self.updateStatusBar('upload-image', self.options.imageTexts.sbProgress.replace('#file_name#', file.name).replace('#progress#', progress)); } }; request.onload = function () { - if(this.status === 200) { + try { + var response = JSON.parse(this.responseText); + } catch (error) { + console.log('EasyMDE: The server did not return a valid json.'); + onError(fillErrorMessage(self.options.errorMessages.imageImportError)); + return; + } + if(this.status === 200 && response && response.data && !response.error) { onSuccess(window.location.origin + '/' + this.responseText); } else { - onError(file.name, this.status, this.statusText.toString()); - // TODO: handle several errors defined by the server (bad type, file too large, etc.) + if(response.error && response.error in self.options.errorMessages) { + onError(fillErrorMessage(self.options.errorMessages[response.error])); + } else { + console.log('EasyMDE: Received an unexpected response after uploading the image.' + + this.status + ' (' + this.statusText + ')'); + onError(fillErrorMessage(self.options.errorMessages.imageImportError)); + } } }; + + request.onerror = function (event) { + console.log('EasyMDE: An unexpected error occurred when trying to upload the image.' + + event.target.status + ' (' + event.target.statusText + ')'); + onError(self.options.errorMessages.imageImportError); + }; }; EasyMDE.prototype.createSideBySide = function () {