2
0
mirror of https://github.com/Ionaru/easy-markdown-editor synced 2025-09-24 16:40:55 -06:00

Added multilanguage translation

Signed-off-by: Dmitry Mazurov <dimabzz@gmail.com>
This commit is contained in:
Dmitry Mazurov 2020-03-24 21:31:25 +03:00
parent 6470e10e29
commit bf8fc071ea
4 changed files with 215 additions and 118 deletions

103
README.md
View File

@ -30,6 +30,7 @@ The editor is entirely customizable, from theming to toolbar buttons and javascr
- [Configuration](#configuration) - [Configuration](#configuration)
- [Options list](#options-list) - [Options list](#options-list)
- [Options example](#options-example) - [Options example](#options-example)
- [Localization example](#localization-example)
- [Toolbar icons](#toolbar-icons) - [Toolbar icons](#toolbar-icons)
- [Toolbar customization](#toolbar-customization) - [Toolbar customization](#toolbar-customization)
- [Keyboard shortcuts](#keyboard-shortcuts) - [Keyboard shortcuts](#keyboard-shortcuts)
@ -115,6 +116,20 @@ easyMDE.value('New input for **EasyMDE**');
### Options list ### Options list
- **locale**: Set locale. Default `en`.
- **localization**: Set custom translate.
- **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#`.
- **sizeUnits**: A comma-separated list of units used to display messages with human-readable file sizes. Defaults to `b,Kb,Mb`.
- **noFileGiven**: The server did not receive any file from the user. Defaults to `You must select a file.`.
- **typeNotAllowed**: 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.`.
- **fileTooLarge**: 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#.`.
- **importError**: An unexpected error occurred when uploading the image. Defaults to `Something went wrong when uploading the image #image_name#.`.
- **errorMessages**: Errors displayed to the user, using the `errorCallback` option, where `#image_name#`, `#image_size#` and `#image_max_size#` will replaced by their respective values, that can be used for customization or internationalization:
- **autoDownloadFontAwesome**: If set to `true`, force downloads Font Awesome (used for icons). If set to `false`, prevents downloading. Defaults to `undefined`, which will intelligently check whether Font Awesome has already been included, then download accordingly. - **autoDownloadFontAwesome**: If set to `true`, force downloads Font Awesome (used for icons). If set to `false`, prevents downloading. Defaults to `undefined`, which will intelligently check whether Font Awesome has already been included, then download accordingly.
- **autofocus**: If set to `true`, focuses the editor automatically. Defaults to `false`. - **autofocus**: If set to `true`, focuses the editor automatically. Defaults to `false`.
- **autosave**: *Saves the text that's being written and will load it back in the future. It will forget the text when the form it's contained in is submitted.* - **autosave**: *Saves the text that's being written and will load it back in the future. It will forget the text when the form it's contained in is submitted.*
@ -123,7 +138,6 @@ easyMDE.value('New input for **EasyMDE**');
- **submit_delay**: Delay before assuming that submit of the form failed and saving the text, in milliseconds. Defaults to `autosave.delay` or `10000` (10s). - **submit_delay**: Delay before assuming that submit of the form failed and saving the text, in milliseconds. Defaults to `autosave.delay` or `10000` (10s).
- **uniqueId**: You must set a unique string identifier so that EasyMDE can autosave. Something that separates this from other instances of EasyMDE elsewhere on your website. - **uniqueId**: You must set a unique string identifier so that EasyMDE can autosave. Something that separates this from other instances of EasyMDE elsewhere on your website.
- **timeFormat**: Set DateTimeFormat. More information see [DateTimeFormat instances](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/DateTimeFormat). Default `locale: en-US, format: hour:minute`. - **timeFormat**: Set DateTimeFormat. More information see [DateTimeFormat instances](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/DateTimeFormat). Default `locale: en-US, format: hour:minute`.
- **text**: Set text for autosave.
- **blockStyles**: Customize how certain buttons that style blocks of text behave. - **blockStyles**: Customize how certain buttons that style blocks of text behave.
- **bold**: Can be set to `**` or `__`. Defaults to `**`. - **bold**: Can be set to `**` or `__`. Defaults to `**`.
- **code**: Can be set to ```` ``` ```` or `~~~`. Defaults to ```` ``` ````. - **code**: Can be set to ```` ``` ```` or `~~~`. Defaults to ```` ``` ````.
@ -149,9 +163,6 @@ easyMDE.value('New input for **EasyMDE**');
- **previewClass**: A string or array of strings that will be applied to the preview screen when activated. Defaults to `"editor-preview"`. - **previewClass**: A string or array of strings that will be applied to the preview screen when activated. Defaults to `"editor-preview"`.
- **previewRender**: Custom function for parsing the plaintext Markdown and returning HTML. Used when user previews. - **previewRender**: Custom function for parsing the plaintext Markdown and returning HTML. Used when user previews.
- **promptURLs**: If set to `true`, a JS alert window appears asking for the link or image URL. Defaults to `false`. - **promptURLs**: If set to `true`, a JS alert window appears asking for the link or image URL. Defaults to `false`.
- **promptTexts**: Customize the text used to prompt for URLs.
- **image**: The text to use when prompting for an image's URL. Defaults to `URL of the image:`.
- **link**: The text to use when prompting for a link's URL. Defaults to `URL for the link:`.
- **uploadImage**: If set to `true`, enables the image upload functionality, which can be triggered by drag&drop, copy-paste and through the browse-file window (opened when the user click on the *upload-image* icon). Defaults to `false`. - **uploadImage**: If set to `true`, enables the image upload functionality, which can be triggered by drag&drop, copy-paste and through the browse-file window (opened when the user click on the *upload-image* icon). Defaults to `false`.
- **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`.
@ -162,18 +173,6 @@ easyMDE.value('New input for **EasyMDE**');
- otherwise: `{"error": "<errorCode>"}`, where *errorCode* can be `noFileGiven` (HTTP 400), `typeNotAllowed` (HTTP 415), `fileTooLarge` (HTTP 413) or `importError` (see *errorMessages* below). If *errorCode* is not one of the *errorMessages*, it is alerted unchanged to the user. This allows for server side error messages. - otherwise: `{"error": "<errorCode>"}`, where *errorCode* can be `noFileGiven` (HTTP 400), `typeNotAllowed` (HTTP 415), `fileTooLarge` (HTTP 413) or `importError` (see *errorMessages* below). If *errorCode* is not one of the *errorMessages*, it is alerted unchanged to the user. This allows for server side error messages.
No default value. No default value.
- **imageCSRFToken**: CSRF token to include with AJAX call to upload image. For instance used with Django backend. - **imageCSRFToken**: CSRF token to include with AJAX call to upload image. For instance used with Django backend.
- **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#`.
- **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, using the `errorCallback` option, 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.`.
- **typeNotAllowed**: 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.`.
- **fileTooLarge**: 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#.`.
- **importError**: An unexpected error occurred when uploading the image. Defaults to `Something went wrong when uploading the image #image_name#.`.
- **errorCallback**: A callback function used to define how to display an error message. Defaults to `function(errorMessage) {alert(errorMessage);};`. - **errorCallback**: A callback function used to define how to display an error message. Defaults to `function(errorMessage) {alert(errorMessage);};`.
- **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">`
@ -202,6 +201,14 @@ Most options demonstrate the non-default behavior:
```JavaScript ```JavaScript
var editor = new EasyMDE({ var editor = new EasyMDE({
locale: 'en',
localization: {
"status": {
"lines": "lines",
"words": "words",
"autosave": "Autosaved: ",
},
},
autofocus: true, autofocus: true,
autosave: { autosave: {
enabled: true, enabled: true,
@ -218,7 +225,6 @@ var editor = new EasyMDE({
minute: '2-digit', minute: '2-digit',
}, },
}, },
text: "Autosaved: "
}, },
blockStyles: { blockStyles: {
bold: "__", bold: "__",
@ -258,10 +264,6 @@ var editor = new EasyMDE({
return "Loading..."; return "Loading...";
}, },
promptURLs: true, promptURLs: true,
promptTexts: {
image: "Custom prompt for URL:",
link: "Custom prompt for URL:",
},
renderingConfig: { renderingConfig: {
singleLineBreaks: false, singleLineBreaks: false,
codeSyntaxHighlighting: true, codeSyntaxHighlighting: true,
@ -294,7 +296,66 @@ var editor = new EasyMDE({
toolbarTips: false, toolbarTips: false,
}); });
``` ```
### Localization example
```JavaScript
var editor = new EasyMDE({
locale: 'en',
localization: {
"promptTexts": {
"link": "URL for the link:",
"image": "URL of the image:"
},
"status": {
"lines": "lines",
"words": "words",
"autosave": "Autosaved: "
},
"errorMessages": {
"noFileGiven": "You must select a file.",
"typeNotAllowed": "This image type is not allowed.",
"fileTooLarge": "Image #image_name# is too big (#image_size#).\nMaximum file size is #image_max_size#.",
"importError": "Something went wrong when uploading the image #image_name#."
},
"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#",
"sizeUnits": "b,Kb,Mb"
},
"toolbar": {
"bold": {"title": "Bold"},
"italic": {"title": "Italic"},
"strikethrough": {"title": "Strikethrough"},
"heading": {"title": "Heading"},
"heading-smaller": {"title": "Smaller Heading"},
"heading-bigger": {"title": "Bigger Heading"},
"heading-1": {"title": "Big Heading"},
"heading-2": {"title": "Medium Heading"},
"heading-3": {"title": "Small Heading"},
"code": {"title": "Code"},
"quote": {"title": "Quote"},
"unordered-list": {"title": "Generic List"},
"ordered-list": {"title": "Numbered List"},
"clean-block": {"title": "Clean block"},
"link": {"title": "Create Link"},
"image": {"title": "Insert Image"},
"upload-image": {"title": "Import an image"},
"table": {"title": "Insert Table"},
"horizontal-rule": {"title": "Insert Horizontal Line"},
"preview": {"title": "Toggle Preview"},
"side-by-side": {"title": "Toggle Side by Side"},
"fullscreen": {"title": "Toggle Fullscreen"},
"guide": {"title": "Markdown Guide"},
"undo": {"title": "Undo"},
"redo": {"title": "Redo"}
}
},
...
});
```
### Toolbar icons ### Toolbar icons

View File

@ -205,12 +205,12 @@
content: 'lines: ' content: 'lines: '
} }
.editor-statusbar .words:before { .editor-statusbar .lines:before {
content: 'words: ' content: attr(data-status-bar-before) ': ';
} }
.editor-statusbar .characters:before { .editor-statusbar .words:before {
content: 'characters: ' content: attr(data-status-bar-before) ': ';
} }
.editor-preview-full { .editor-preview-full {

View File

@ -11,7 +11,8 @@ require('codemirror/addon/search/searchcursor.js');
require('codemirror/mode/gfm/gfm.js'); require('codemirror/mode/gfm/gfm.js');
require('codemirror/mode/xml/xml.js'); require('codemirror/mode/xml/xml.js');
var CodeMirrorSpellChecker = require('codemirror-spell-checker'); var CodeMirrorSpellChecker = require('codemirror-spell-checker');
var marked = require('marked/lib/marked'); var marked = require('marked');
var localization = require('./languages.json');
// Some variables // Some variables
@ -730,7 +731,7 @@ function drawLink(editor) {
var options = editor.options; var options = editor.options;
var url = 'https://'; var url = 'https://';
if (options.promptURLs) { if (options.promptURLs) {
url = prompt(options.promptTexts.link, 'https://'); url = prompt(options.locale.promptTexts.link, 'https://');
if (!url) { if (!url) {
return false; return false;
} }
@ -747,7 +748,7 @@ function drawImage(editor) {
var options = editor.options; var options = editor.options;
var url = 'https://'; var url = 'https://';
if (options.promptURLs) { if (options.promptURLs) {
url = prompt(options.promptTexts.image, 'https://'); url = prompt(options.locale.promptTexts.image, 'https://');
if (!url) { if (!url) {
return false; return false;
} }
@ -776,9 +777,9 @@ function afterImageUploaded(editor, url) {
var imageName = url.substr(url.lastIndexOf('/') + 1); var imageName = url.substr(url.lastIndexOf('/') + 1);
_replaceSelection(cm, stat.image, options.insertTexts.uploadedImage, url); _replaceSelection(cm, stat.image, options.insertTexts.uploadedImage, url);
// show uploaded image filename for 1000ms // show uploaded image filename for 1000ms
editor.updateStatusBar('upload-image', editor.options.imageTexts.sbOnUploaded.replace('#image_name#', imageName)); editor.updateStatusBar('upload-image', editor.options.locale.imageTexts.sbOnUploaded.replace('#image_name#', imageName));
setTimeout(function () { setTimeout(function () {
editor.updateStatusBar('upload-image', editor.options.imageTexts.sbInit); editor.updateStatusBar('upload-image', editor.options.locale.imageTexts.sbInit);
}, 1000); }, 1000);
} }
@ -1292,58 +1293,49 @@ var toolbarBuiltInButtons = {
name: 'bold', name: 'bold',
action: toggleBold, action: toggleBold,
className: 'fa fa-bold', className: 'fa fa-bold',
title: 'Bold',
default: true, default: true,
}, },
'italic': { 'italic': {
name: 'italic', name: 'italic',
action: toggleItalic, action: toggleItalic,
className: 'fa fa-italic', className: 'fa fa-italic',
title: 'Italic',
default: true, default: true,
}, },
'strikethrough': { 'strikethrough': {
name: 'strikethrough', name: 'strikethrough',
action: toggleStrikethrough, action: toggleStrikethrough,
className: 'fa fa-strikethrough', className: 'fa fa-strikethrough',
title: 'Strikethrough',
}, },
'heading': { 'heading': {
name: 'heading', name: 'heading',
action: toggleHeadingSmaller, action: toggleHeadingSmaller,
className: 'fa fa-header fa-heading', className: 'fa fa-header fa-heading',
title: 'Heading',
default: true, default: true,
}, },
'heading-smaller': { 'heading-smaller': {
name: 'heading-smaller', name: 'heading-smaller',
action: toggleHeadingSmaller, action: toggleHeadingSmaller,
className: 'fa fa-header fa-heading header-smaller', className: 'fa fa-header fa-heading header-smaller',
title: 'Smaller Heading',
}, },
'heading-bigger': { 'heading-bigger': {
name: 'heading-bigger', name: 'heading-bigger',
action: toggleHeadingBigger, action: toggleHeadingBigger,
className: 'fa fa-header fa-heading header-bigger', className: 'fa fa-header fa-heading header-bigger',
title: 'Bigger Heading',
}, },
'heading-1': { 'heading-1': {
name: 'heading-1', name: 'heading-1',
action: toggleHeading1, action: toggleHeading1,
className: 'fa fa-header fa-heading header-1', className: 'fa fa-header fa-heading header-1',
title: 'Big Heading',
}, },
'heading-2': { 'heading-2': {
name: 'heading-2', name: 'heading-2',
action: toggleHeading2, action: toggleHeading2,
className: 'fa fa-header fa-heading header-2', className: 'fa fa-header fa-heading header-2',
title: 'Medium Heading',
}, },
'heading-3': { 'heading-3': {
name: 'heading-3', name: 'heading-3',
action: toggleHeading3, action: toggleHeading3,
className: 'fa fa-header fa-heading header-3', className: 'fa fa-header fa-heading header-3',
title: 'Small Heading',
}, },
'separator-1': { 'separator-1': {
name: 'separator-1', name: 'separator-1',
@ -1352,34 +1344,29 @@ var toolbarBuiltInButtons = {
name: 'code', name: 'code',
action: toggleCodeBlock, action: toggleCodeBlock,
className: 'fa fa-code', className: 'fa fa-code',
title: 'Code',
}, },
'quote': { 'quote': {
name: 'quote', name: 'quote',
action: toggleBlockquote, action: toggleBlockquote,
className: 'fa fa-quote-left', className: 'fa fa-quote-left',
title: 'Quote',
default: true, default: true,
}, },
'unordered-list': { 'unordered-list': {
name: 'unordered-list', name: 'unordered-list',
action: toggleUnorderedList, action: toggleUnorderedList,
className: 'fa fa-list-ul', className: 'fa fa-list-ul',
title: 'Generic List',
default: true, default: true,
}, },
'ordered-list': { 'ordered-list': {
name: 'ordered-list', name: 'ordered-list',
action: toggleOrderedList, action: toggleOrderedList,
className: 'fa fa-list-ol', className: 'fa fa-list-ol',
title: 'Numbered List',
default: true, default: true,
}, },
'clean-block': { 'clean-block': {
name: 'clean-block', name: 'clean-block',
action: cleanBlock, action: cleanBlock,
className: 'fa fa-eraser', className: 'fa fa-eraser',
title: 'Clean block',
}, },
'separator-2': { 'separator-2': {
name: 'separator-2', name: 'separator-2',
@ -1388,33 +1375,28 @@ var toolbarBuiltInButtons = {
name: 'link', name: 'link',
action: drawLink, action: drawLink,
className: 'fa fa-link', className: 'fa fa-link',
title: 'Create Link',
default: true, default: true,
}, },
'image': { 'image': {
name: 'image', name: 'image',
action: drawImage, action: drawImage,
className: 'fa fa-image', className: 'fa fa-image',
title: 'Insert Image',
default: true, default: true,
}, },
'upload-image': { 'upload-image': {
name: 'upload-image', name: 'upload-image',
action: drawUploadedImage, action: drawUploadedImage,
className: 'fa fa-image', className: 'fa fa-image',
title: 'Import an image',
}, },
'table': { 'table': {
name: 'table', name: 'table',
action: drawTable, action: drawTable,
className: 'fa fa-table', className: 'fa fa-table',
title: 'Insert Table',
}, },
'horizontal-rule': { 'horizontal-rule': {
name: 'horizontal-rule', name: 'horizontal-rule',
action: drawHorizontalRule, action: drawHorizontalRule,
className: 'fa fa-minus', className: 'fa fa-minus',
title: 'Insert Horizontal Line',
}, },
'separator-3': { 'separator-3': {
name: 'separator-3', name: 'separator-3',
@ -1424,7 +1406,6 @@ var toolbarBuiltInButtons = {
action: togglePreview, action: togglePreview,
className: 'fa fa-eye', className: 'fa fa-eye',
noDisable: true, noDisable: true,
title: 'Toggle Preview',
default: true, default: true,
}, },
'side-by-side': { 'side-by-side': {
@ -1433,7 +1414,6 @@ var toolbarBuiltInButtons = {
className: 'fa fa-columns', className: 'fa fa-columns',
noDisable: true, noDisable: true,
noMobile: true, noMobile: true,
title: 'Toggle Side by Side',
default: true, default: true,
}, },
'fullscreen': { 'fullscreen': {
@ -1442,7 +1422,6 @@ var toolbarBuiltInButtons = {
className: 'fa fa-arrows-alt', className: 'fa fa-arrows-alt',
noDisable: true, noDisable: true,
noMobile: true, noMobile: true,
title: 'Toggle Fullscreen',
default: true, default: true,
}, },
'separator-4': { 'separator-4': {
@ -1453,7 +1432,6 @@ var toolbarBuiltInButtons = {
action: 'https://www.markdownguide.org/basic-syntax/', action: 'https://www.markdownguide.org/basic-syntax/',
className: 'fa fa-question-circle', className: 'fa fa-question-circle',
noDisable: true, noDisable: true,
title: 'Markdown Guide',
default: true, default: true,
}, },
'separator-5': { 'separator-5': {
@ -1464,14 +1442,12 @@ var toolbarBuiltInButtons = {
action: undo, action: undo,
className: 'fa fa-undo', className: 'fa fa-undo',
noDisable: true, noDisable: true,
title: 'Undo',
}, },
'redo': { 'redo': {
name: 'redo', name: 'redo',
action: redo, action: redo,
className: 'fa fa-repeat fa-redo', className: 'fa fa-repeat fa-redo',
noDisable: true, noDisable: true,
title: 'Redo',
}, },
}; };
@ -1484,11 +1460,6 @@ var insertTexts = {
horizontalRule: ['', '\n\n-----\n\n'], horizontalRule: ['', '\n\n-----\n\n'],
}; };
var promptTexts = {
link: 'URL for the link:',
image: 'URL of the image:',
};
var timeFormat = { var timeFormat = {
locale: 'en-US', locale: 'en-US',
format: { format: {
@ -1503,31 +1474,6 @@ 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 = {
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#',
sizeUnits: 'b,Kb,Mb',
};
/**
* Errors displayed to the user, using the `errorCallback` option. Can be used for
* customization or internationalization.
*/
var errorMessages = {
noFileGiven: 'You must select a file.',
typeNotAllowed: 'This image type is not allowed.',
fileTooLarge: 'Image #image_name# is too big (#image_size#).\n' +
'Maximum file size is #image_max_size#.',
importError: 'Something went wrong when uploading the image #image_name#.',
};
/** /**
* Interface of EasyMDE. * Interface of EasyMDE.
*/ */
@ -1625,24 +1571,22 @@ function EasyMDE(options) {
}, options.parsingConfig || {}); }, options.parsingConfig || {});
// Merging the insertTexts, with the given options // Merging localizations
options.locale = extend(localization['en'], localization[options.locale], options.localization || {});
options.insertTexts = extend({}, insertTexts, options.insertTexts || {}); options.insertTexts = extend({}, insertTexts, options.insertTexts || {});
// Merging the promptTexts, with the given options
options.promptTexts = extend({}, promptTexts, options.promptTexts || {});
// Merging the blockStyles, with the given options
options.blockStyles = extend({}, blockStyles, options.blockStyles || {});
if (options.autosave != undefined) { if (options.autosave != undefined) {
// Merging the Autosave timeFormat, with the given options // Merging the Autosave timeFormat, with the given options
options.autosave.timeFormat = extend({}, timeFormat, options.autosave.timeFormat || {}); options.autosave.timeFormat = extend({}, timeFormat, options.autosave.timeFormat || {});
} }
// Merging the blockStyles, with the given options
options.blockStyles = extend({}, blockStyles, options.blockStyles || {});
// Merging the shortcuts, with the given options // Merging the shortcuts, with the given options
options.shortcuts = extend({}, shortcuts, options.shortcuts || {}); options.shortcuts = extend({}, shortcuts, options.shortcuts || {});
@ -1656,8 +1600,6 @@ function EasyMDE(options) {
options.uploadImage = options.uploadImage || false; options.uploadImage = options.uploadImage || false;
options.imageMaxSize = options.imageMaxSize || 2097152; // 1024 * 1024 * 2 options.imageMaxSize = options.imageMaxSize || 2097152; // 1024 * 1024 * 2
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
@ -1684,23 +1626,23 @@ function EasyMDE(options) {
var self = this; var self = this;
this.codemirror.on('dragenter', function (cm, event) { this.codemirror.on('dragenter', function (cm, event) {
self.updateStatusBar('upload-image', self.options.imageTexts.sbOnDragEnter); self.updateStatusBar('upload-image', self.options.locale.imageTexts.sbOnDragEnter);
event.stopPropagation(); event.stopPropagation();
event.preventDefault(); event.preventDefault();
}); });
this.codemirror.on('dragend', function (cm, event) { this.codemirror.on('dragend', function (cm, event) {
self.updateStatusBar('upload-image', self.options.imageTexts.sbInit); self.updateStatusBar('upload-image', self.options.locale.imageTexts.sbInit);
event.stopPropagation(); event.stopPropagation();
event.preventDefault(); event.preventDefault();
}); });
this.codemirror.on('dragleave', function (cm, event) { this.codemirror.on('dragleave', function (cm, event) {
self.updateStatusBar('upload-image', self.options.imageTexts.sbInit); self.updateStatusBar('upload-image', self.options.locale.imageTexts.sbInit);
event.stopPropagation(); event.stopPropagation();
event.preventDefault(); event.preventDefault();
}); });
this.codemirror.on('dragover', function (cm, event) { this.codemirror.on('dragover', function (cm, event) {
self.updateStatusBar('upload-image', self.options.imageTexts.sbOnDragEnter); self.updateStatusBar('upload-image', self.options.locale.imageTexts.sbOnDragEnter);
event.stopPropagation(); event.stopPropagation();
event.preventDefault(); event.preventDefault();
}); });
@ -1745,7 +1687,7 @@ EasyMDE.prototype.uploadImages = function (files, onSuccess, onError) {
names.push(files[i].name); names.push(files[i].name);
this.uploadImage(files[i], onSuccess, onError); this.uploadImage(files[i], onSuccess, onError);
} }
this.updateStatusBar('upload-image', this.options.imageTexts.sbOnDrop.replace('#images_names#', names.join(', '))); this.updateStatusBar('upload-image', this.options.locale.imageTexts.sbOnDrop.replace('#images_names#', names.join(', ')));
}; };
/** /**
@ -1767,7 +1709,7 @@ EasyMDE.prototype.uploadImagesUsingCustomFunction = function (imageUploadFunctio
names.push(files[i].name); names.push(files[i].name);
this.uploadImageUsingCustomFunction(imageUploadFunction, files[i]); this.uploadImageUsingCustomFunction(imageUploadFunction, files[i]);
} }
this.updateStatusBar('upload-image', this.options.imageTexts.sbOnDrop.replace('#images_names#', names.join(', '))); this.updateStatusBar('upload-image', this.options.locale.imageTexts.sbOnDrop.replace('#images_names#', names.join(', ')));
}; };
/** /**
@ -2025,7 +1967,7 @@ EasyMDE.prototype.autosave = function () {
if (el != null && el != undefined && el != '') { if (el != null && el != undefined && el != '') {
var d = new Date(); var d = new Date();
var dd = new Intl.DateTimeFormat([this.options.autosave.timeFormat.locale, 'en-US'], this.options.autosave.timeFormat.format).format(d); var dd = new Intl.DateTimeFormat([this.options.autosave.timeFormat.locale, 'en-US'], this.options.autosave.timeFormat.format).format(d);
var save = this.options.autosave.text == undefined ? 'Autosaved: ' : this.options.autosave.text; var save = this.options.locale.status.autosave;
el.innerHTML = save + dd; el.innerHTML = save + dd;
} }
@ -2092,7 +2034,7 @@ EasyMDE.prototype.uploadImage = function (file, onSuccess, onError) {
self.updateStatusBar('upload-image', errorMessage); self.updateStatusBar('upload-image', errorMessage);
setTimeout(function () { setTimeout(function () {
self.updateStatusBar('upload-image', self.options.imageTexts.sbInit); self.updateStatusBar('upload-image', self.options.locale.imageTexts.sbInit);
}, 10000); }, 10000);
// run custom error handler // run custom error handler
@ -2104,7 +2046,7 @@ EasyMDE.prototype.uploadImage = function (file, onSuccess, onError) {
} }
function fillErrorMessage(errorMessage) { function fillErrorMessage(errorMessage) {
var units = self.options.imageTexts.sizeUnits.split(','); var units = self.options.locale.imageTexts.sizeUnits.split(',');
return errorMessage 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))
@ -2112,7 +2054,7 @@ EasyMDE.prototype.uploadImage = function (file, onSuccess, onError) {
} }
if (file.size > this.options.imageMaxSize) { if (file.size > this.options.imageMaxSize) {
onErrorSup(fillErrorMessage(this.options.errorMessages.fileTooLarge)); onErrorSup(fillErrorMessage(this.options.locale.errorMessages.fileTooLarge));
return; return;
} }
@ -2127,7 +2069,7 @@ EasyMDE.prototype.uploadImage = function (file, onSuccess, onError) {
request.upload.onprogress = function (event) { request.upload.onprogress = function (event) {
if (event.lengthComputable) { if (event.lengthComputable) {
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.locale.imageTexts.sbProgress.replace('#file_name#', file.name).replace('#progress#', progress));
} }
}; };
request.open('POST', this.options.imageUploadEndpoint); request.open('POST', this.options.imageUploadEndpoint);
@ -2137,20 +2079,20 @@ EasyMDE.prototype.uploadImage = function (file, onSuccess, onError) {
var response = JSON.parse(this.responseText); var response = JSON.parse(this.responseText);
} catch (error) { } catch (error) {
console.error('EasyMDE: The server did not return a valid json.'); console.error('EasyMDE: The server did not return a valid json.');
onErrorSup(fillErrorMessage(self.options.errorMessages.importError)); onErrorSup(fillErrorMessage(self.options.locale.errorMessages.importError));
return; return;
} }
if (this.status === 200 && response && !response.error && response.data && response.data.filePath) { if (this.status === 200 && response && !response.error && response.data && response.data.filePath) {
onSuccess(window.location.origin + '/' + response.data.filePath); onSuccess(window.location.origin + '/' + response.data.filePath);
} else { } else {
if (response.error && response.error in self.options.errorMessages) { // preformatted error message if (response.error && response.error in self.options.locale.errorMessages) { // preformatted error message
onErrorSup(fillErrorMessage(self.options.errorMessages[response.error])); onErrorSup(fillErrorMessage(self.options.locale.errorMessages[response.error]));
} else if (response.error) { // server side generated error message } else if (response.error) { // server side generated error message
onErrorSup(fillErrorMessage(response.error)); onErrorSup(fillErrorMessage(response.error));
} else { //unknown error } else { //unknown error
console.error('EasyMDE: Received an unexpected response after uploading the image.' console.error('EasyMDE: Received an unexpected response after uploading the image.'
+ this.status + ' (' + this.statusText + ')'); + this.status + ' (' + this.statusText + ')');
onErrorSup(fillErrorMessage(self.options.errorMessages.importError)); onErrorSup(fillErrorMessage(self.options.locale.errorMessages.importError));
} }
} }
}; };
@ -2158,7 +2100,7 @@ EasyMDE.prototype.uploadImage = function (file, onSuccess, onError) {
request.onerror = function (event) { request.onerror = function (event) {
console.error('EasyMDE: An unexpected error occurred when trying to upload the image.' console.error('EasyMDE: An unexpected error occurred when trying to upload the image.'
+ event.target.status + ' (' + event.target.statusText + ')'); + event.target.status + ' (' + event.target.statusText + ')');
onErrorSup(self.options.errorMessages.importError); onErrorSup(self.options.locale.errorMessages.importError);
}; };
request.send(formData); request.send(formData);
@ -2183,7 +2125,7 @@ EasyMDE.prototype.uploadImageUsingCustomFunction = function(imageUploadFunction,
self.updateStatusBar('upload-image', filledErrorMessage); self.updateStatusBar('upload-image', filledErrorMessage);
setTimeout(function () { setTimeout(function () {
self.updateStatusBar('upload-image', self.options.imageTexts.sbInit); self.updateStatusBar('upload-image', self.options.locale.imageTexts.sbInit);
}, 10000); }, 10000);
// run error handler from options, this alerts the message. // run error handler from options, this alerts the message.
@ -2191,7 +2133,7 @@ EasyMDE.prototype.uploadImageUsingCustomFunction = function(imageUploadFunction,
} }
function fillErrorMessage(errorMessage) { function fillErrorMessage(errorMessage) {
var units = self.options.imageTexts.sizeUnits.split(','); var units = self.options.locale.imageTexts.sizeUnits.split(',');
return errorMessage 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))
@ -2265,7 +2207,7 @@ EasyMDE.prototype.createToolbar = function (items) {
var i; var i;
for (i = 0; i < items.length; i++) { for (i = 0; i < items.length; i++) {
if (toolbarBuiltInButtons[items[i]] != undefined) { if (toolbarBuiltInButtons[items[i]] != undefined) {
items[i] = toolbarBuiltInButtons[items[i]]; items[i] = extend({}, toolbarBuiltInButtons[items[i]], this.options.locale.toolbar[items[i]] || {});
} }
} }
@ -2372,25 +2314,30 @@ EasyMDE.prototype.createStatusbar = function (status) {
// Set up the built-in items // Set up the built-in items
var items = []; var items = [];
var i, onUpdate, defaultValue; var i, onUpdate, onCursorActivity, defaultValue, dataSet;
for (i = 0; i < status.length; i++) { for (i = 0; i < status.length; i++) {
// Reset some values // Reset some values
onUpdate = undefined; onUpdate = undefined;
onCursorActivity = undefined;
defaultValue = undefined; defaultValue = undefined;
dataSet = undefined;
// Handle if custom or not // Handle if custom or not
if (typeof status[i] === 'object') { if (typeof status[i] === 'object') {
items.push({ items.push({
className: status[i].className, className: status[i].className,
dataSet: status[i].dataSet,
defaultValue: status[i].defaultValue, defaultValue: status[i].defaultValue,
onUpdate: status[i].onUpdate, onUpdate: status[i].onUpdate,
onCursorActivity: status[i].onCursorActivity,
}); });
} else { } else {
var name = status[i]; var name = status[i];
if (name === 'words') { if (name === 'words') {
dataSet = options.locale.status[name];
defaultValue = function (el) { defaultValue = function (el) {
el.innerHTML = wordCount(cm.getValue()); el.innerHTML = wordCount(cm.getValue());
}; };
@ -2398,6 +2345,8 @@ EasyMDE.prototype.createStatusbar = function (status) {
el.innerHTML = wordCount(cm.getValue()); el.innerHTML = wordCount(cm.getValue());
}; };
} else if (name === 'lines') { } else if (name === 'lines') {
dataSet = options.locale.status[name];
defaultValue = function (el) { defaultValue = function (el) {
el.innerHTML = cm.lineCount(); el.innerHTML = cm.lineCount();
}; };
@ -2410,6 +2359,12 @@ EasyMDE.prototype.createStatusbar = function (status) {
}; };
onUpdate = function (el) { onUpdate = function (el) {
var pos = cm.getCursor(); var pos = cm.getCursor();
el.innerHTML = pos.line + ':' + pos.ch;
};
onCursorActivity = function (el) {
var pos = cm.getCursor();
el.innerHTML = pos.line + ':' + pos.ch; el.innerHTML = pos.line + ':' + pos.ch;
}; };
} else if (name === 'autosave') { } else if (name === 'autosave') {
@ -2420,14 +2375,16 @@ EasyMDE.prototype.createStatusbar = function (status) {
}; };
} else if (name === 'upload-image') { } else if (name === 'upload-image') {
defaultValue = function (el) { defaultValue = function (el) {
el.innerHTML = options.imageTexts.sbInit; el.innerHTML = options.locale.imageTexts.sbInit;
}; };
} }
items.push({ items.push({
className: name, className: name,
dataSet: dataSet,
defaultValue: defaultValue, defaultValue: defaultValue,
onUpdate: onUpdate, onUpdate: onUpdate,
onCursorActivity: onCursorActivity,
}); });
} }
} }
@ -2448,6 +2405,10 @@ EasyMDE.prototype.createStatusbar = function (status) {
var el = document.createElement('span'); var el = document.createElement('span');
el.className = item.className; el.className = item.className;
if (item.dataSet != undefined) {
el.dataset.statusBarBefore = item.dataSet;
}
// Ensure the defaultValue is a function // Ensure the defaultValue is a function
if (typeof item.defaultValue === 'function') { if (typeof item.defaultValue === 'function') {
@ -2465,6 +2426,15 @@ EasyMDE.prototype.createStatusbar = function (status) {
}(el, item))); }(el, item)));
} }
// Ensure the onCursorActivity is a function
if (typeof item.onCursorActivity === 'function') {
// Create a closure around the span of the current action, then execute the onCursorActivity handler
this.codemirror.on('cursorActivity', (function (el, item) {
return function () {
item.onCursorActivity(el);
};
}(el, item)));
}
// Append the item to the status bar // Append the item to the status bar
bar.appendChild(el); bar.appendChild(el);

66
src/js/languages.json Normal file
View File

@ -0,0 +1,66 @@
{
"en": {
"promptTexts": {
"link": "URL for the link:",
"image": "URL of the image:"
},
"status": {
"lines": "lines",
"words": "words",
"autosave": "Autosaved: "
},
"errorMessages": {
"noFileGiven": "You must select a file.",
"typeNotAllowed": "This image type is not allowed.",
"fileTooLarge": "Image #image_name# is too big (#image_size#).\nMaximum file size is #image_max_size#.",
"importError": "Something went wrong when uploading the image #image_name#."
},
"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#",
"sizeUnits": "b,Kb,Mb"
},
"toolbar": {
"bold": {"title": "Bold"},
"italic": {"title": "Italic"},
"strikethrough": {"title": "Strikethrough"},
"heading": {"title": "Heading"},
"heading-smaller": {"title": "Smaller Heading"},
"heading-bigger": {"title": "Bigger Heading"},
"heading-1": {"title": "Big Heading"},
"heading-2": {"title": "Medium Heading"},
"heading-3": {"title": "Small Heading"},
"code": {"title": "Code"},
"quote": {"title": "Quote"},
"unordered-list": {"title": "Generic List"},
"ordered-list": {"title": "Numbered List"},
"clean-block": {"title": "Clean block"},
"link": {"title": "Create Link"},
"image": {"title": "Insert Image"},
"upload-image": {"title": "Import an image"},
"table": {"title": "Insert Table"},
"horizontal-rule": {"title": "Insert Horizontal Line"},
"preview": {"title": "Toggle Preview"},
"side-by-side": {"title": "Toggle Side by Side"},
"fullscreen": {"title": "Toggle Fullscreen"},
"guide": {"title": "Markdown Guide"},
"undo": {"title": "Undo"},
"redo": {"title": "Redo"}
}
},
"ru": {
"promptTexts": {
"link": "Введите гиперссылку:",
"image": "Вставьте ссылку на изображение:"
},
"status": {
"lines": "строк",
"words": "слов",
"autosave": "Автосохранение: "
}
}
}