mirror of
https://github.com/Ionaru/easy-markdown-editor
synced 2025-07-16 22:44:29 -06:00
[wip upload-image] Better errors support and small code improvements
This commit is contained in:
parent
9f33099b1c
commit
d374c5faec
19
README.md
19
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).
|
- **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`.
|
- **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.
|
- **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:
|
- **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.`,
|
- **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.`,
|
- **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#`.
|
- **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#%`,
|
- **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#`,
|
- **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#.`.
|
|
||||||
- **sizeUnits**: A comma-separated list of units used to display messages with human-readable file sizes. Defaults to `b,Kb,Mb`.
|
- **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).
|
- **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:<br>`<script src="https://cdn.jsdelivr.net/highlight.js/latest/highlight.min.js"></script>`<br>`<link rel="stylesheet" href="https://cdn.jsdelivr.net/highlight.js/latest/styles/github.min.css">`
|
- **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:<br>`<script src="https://cdn.jsdelivr.net/highlight.js/latest/highlight.min.js"></script>`<br>`<link rel="stylesheet" href="https://cdn.jsdelivr.net/highlight.js/latest/styles/github.min.css">`
|
||||||
- **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`.
|
- **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`.
|
||||||
|
@ -705,6 +705,7 @@ function drawImage(editor) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Action for opening the browse-file window to upload an image to a server.
|
* Action for opening the browse-file window to upload an image to a server.
|
||||||
|
* @param editor {EasyMDE} The EasyMDE object
|
||||||
*/
|
*/
|
||||||
function drawUploadedImage(editor) {
|
function drawUploadedImage(editor) {
|
||||||
// TODO: Draw the image template with a fake url, ie: ''
|
// TODO: Draw the image template with a fake url, ie: ''
|
||||||
@ -714,7 +715,7 @@ function drawUploadedImage(editor) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Action executed after an image have been successfully imported on the server.
|
* 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
|
* @param url {string} The url of the uploaded image
|
||||||
*/
|
*/
|
||||||
function afterImageUploaded(editor, url) {
|
function afterImageUploaded(editor, url) {
|
||||||
@ -1411,18 +1412,31 @@ var blockStyles = {
|
|||||||
'italic': '*',
|
'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 = {
|
var imageTexts = {
|
||||||
sbInit: 'Attach files by drag and dropping or pasting from clipboard.',
|
sbInit: 'Attach files by drag and dropping or pasting from clipboard.',
|
||||||
sbOnDragEnter: 'Drop image to upload it.',
|
sbOnDragEnter: 'Drop image to upload it.',
|
||||||
sbOnDrop: 'Uploading image #images_names#...',
|
sbOnDrop: 'Uploading image #images_names#...',
|
||||||
sbProgress: 'Uploading #file_name#: #progress#%',
|
sbProgress: 'Uploading #file_name#: #progress#%',
|
||||||
sbOnUploaded: 'Uploaded #image_name#',
|
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',
|
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.
|
* Interface of EasyMDE.
|
||||||
*/
|
*/
|
||||||
@ -1534,11 +1548,12 @@ function EasyMDE(options) {
|
|||||||
options.minHeight = options.minHeight || '300px';
|
options.minHeight = options.minHeight || '300px';
|
||||||
|
|
||||||
|
|
||||||
// import-image default configuration
|
// Import-image default configuration
|
||||||
options.uploadImage = options.uploadImage || false;
|
options.uploadImage = options.uploadImage || false;
|
||||||
options.imageMaxSize = options.imageMaxSize || 1024*1024*2;
|
options.imageMaxSize = options.imageMaxSize || 1024*1024*2;
|
||||||
options.imageTexts = extend({}, imageTexts, options.imageTexts || {});
|
|
||||||
options.imageAccept = options.imageAccept || 'image/png, image/jpeg';
|
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
|
// Change unique_id to uniqueId for backwards compatibility
|
||||||
@ -1605,18 +1620,17 @@ EasyMDE.prototype.uploadImages = function(files) {
|
|||||||
|
|
||||||
this.uploadImage(files[i], function onSuccess(imageUrl) {
|
this.uploadImage(files[i], function onSuccess(imageUrl) {
|
||||||
afterImageUploaded(self, imageUrl);
|
afterImageUploaded(self, imageUrl);
|
||||||
}, function onFailure(imageName, errorStatus, errorStatusText) {
|
}, function onFailure(error) {
|
||||||
alert(self.options.imageTexts.errorImport.replace('#image_name#', imageName));
|
alert(error);
|
||||||
console.log('EasyMDE: error ' + errorStatus + ' when importing image ' + imageName + ': ' + errorStatusText);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
this.updateStatusBar('upload-image', self.options.imageTexts.sbOnDrop.replace('#images_names#', names.join(', ')));
|
this.updateStatusBar('upload-image', self.options.imageTexts.sbOnDrop.replace('#images_names#', names.join(', ')));
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* Update an item in the status bar.
|
||||||
* @param itemName
|
* @param itemName {string} The name of the item to update (ie. 'upload-image', 'autosave', etc.).
|
||||||
* @param content
|
* @param content {string} the new content of the item to write in the status bar.
|
||||||
*/
|
*/
|
||||||
EasyMDE.prototype.updateStatusBar = function(itemName, content) {
|
EasyMDE.prototype.updateStatusBar = function(itemName, content) {
|
||||||
var matchingClasses = this.gui.statusbar.getElementsByClassName(itemName);
|
var matchingClasses = this.gui.statusbar.getElementsByClassName(itemName);
|
||||||
@ -1890,23 +1904,23 @@ EasyMDE.prototype.clearAutosavedValue = function () {
|
|||||||
/**
|
/**
|
||||||
* Upload an image to the server.
|
* 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 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 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.
|
* - url (string): The URL of the uploaded image.
|
||||||
* @param onError {function} A callback function to execute when the image upload fails, with parameters:
|
* @param onError {function} A callback function to execute when the image upload fails, with one parameter:
|
||||||
* - fileName: the name of the image file provided by the user.
|
* - error (string): the detailed error to display to the user (based on messages from options.errorMessages).
|
||||||
* - 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).
|
|
||||||
*/
|
*/
|
||||||
EasyMDE.prototype.uploadImage = function(file, onSuccess, onError) {
|
EasyMDE.prototype.uploadImage = function(file, onSuccess, onError) {
|
||||||
if (file.size >= this.options.imageMaxSize) {
|
function fillErrorMessage(errorMessage) {
|
||||||
var units = this.options.imageTexts.sizeUnits.split(',');
|
var units = self.options.imageTexts.sizeUnits.split(',');
|
||||||
alert(this.options.imageTexts.errorImageTooBig
|
return errorMessage
|
||||||
.replace('#image_name#', file.name)
|
.replace('#image_name#', file.name)
|
||||||
.replace('#image_size#', humanFileSize(file.size, units))
|
.replace('#image_size#', humanFileSize(file.size, units))
|
||||||
.replace('#image_max_size#', humanFileSize(this.options.imageMaxSize, units))
|
.replace('#image_max_size#', humanFileSize(self.options.imageMaxSize, units));
|
||||||
);
|
}
|
||||||
return;
|
|
||||||
|
if (file.size > this.options.imageMaxSize) {
|
||||||
|
onError(fillErrorMessage(this.options.errorMessages.imageTooLarge));
|
||||||
}
|
}
|
||||||
|
|
||||||
var formData = new FormData();
|
var formData = new FormData();
|
||||||
@ -1914,23 +1928,41 @@ EasyMDE.prototype.uploadImage = function(file, onSuccess, onError) {
|
|||||||
var request = new XMLHttpRequest();
|
var request = new XMLHttpRequest();
|
||||||
request.open('POST', this.options.imageUploadEndpoint);
|
request.open('POST', this.options.imageUploadEndpoint);
|
||||||
request.send(formData);
|
request.send(formData);
|
||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
request.onprogress = function (event) {
|
request.onprogress = function (event) {
|
||||||
if (event.lengthComputable) {
|
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);
|
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));
|
self.updateStatusBar('upload-image', self.options.imageTexts.sbProgress.replace('#file_name#', file.name).replace('#progress#', progress));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
request.onload = function () {
|
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);
|
onSuccess(window.location.origin + '/' + this.responseText);
|
||||||
} else {
|
} else {
|
||||||
onError(file.name, this.status, this.statusText.toString());
|
if(response.error && response.error in self.options.errorMessages) {
|
||||||
// TODO: handle several errors defined by the server (bad type, file too large, etc.)
|
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);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user