mirror of
https://github.com/Hopiu/django-markdownx.git
synced 2026-03-16 21:40:24 +00:00
Updated docs and interfaces.
This commit is contained in:
parent
004b408eac
commit
af002f3d59
4 changed files with 454 additions and 215 deletions
|
|
@ -21,6 +21,51 @@
|
|||
|
||||
declare function docReady(args: any): any;
|
||||
|
||||
interface ImageUploadResponse {
|
||||
image_code?: string,
|
||||
image_path?: string,
|
||||
[propName: string]: any;
|
||||
}
|
||||
|
||||
interface HandlerFunction {
|
||||
(properties: {
|
||||
start: number,
|
||||
end: number,
|
||||
value: string
|
||||
}): string
|
||||
}
|
||||
|
||||
interface KeyboardEvents {
|
||||
keys: {
|
||||
TAB: string,
|
||||
DUPLICATE: string,
|
||||
UNINDENT: string,
|
||||
INDENT: string
|
||||
},
|
||||
handlers: {
|
||||
_multiLineIndentation: HandlerFunction,
|
||||
applyTab: HandlerFunction,
|
||||
applyIndentation: HandlerFunction,
|
||||
removeIndentation: HandlerFunction,
|
||||
removeTab: HandlerFunction,
|
||||
applyDuplication: HandlerFunction
|
||||
},
|
||||
hub: Function
|
||||
}
|
||||
|
||||
interface EventHandlers {
|
||||
inhibitDefault: Function,
|
||||
onDragEnter: Function
|
||||
}
|
||||
|
||||
interface MarkdownxProperties {
|
||||
parent: HTMLElement,
|
||||
editor: HTMLTextAreaElement,
|
||||
preview: HTMLElement,
|
||||
_latency: number | null,
|
||||
_editorIsResizable: Boolean | null
|
||||
}
|
||||
|
||||
import {
|
||||
Request,
|
||||
mountEvents,
|
||||
|
|
@ -41,7 +86,10 @@ const UPLOAD_URL_ATTRIBUTE: string = "data-markdownx-upload-urls-path",
|
|||
// ---------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
const EventHandlers = {
|
||||
/**
|
||||
*
|
||||
*/
|
||||
const EventHandlers: EventHandlers = {
|
||||
|
||||
/**
|
||||
* Routine tasks for event handlers (e.g. default preventions).
|
||||
|
|
@ -74,175 +122,273 @@ const EventHandlers = {
|
|||
};
|
||||
|
||||
|
||||
const keyboardEvents = {
|
||||
/**
|
||||
*
|
||||
*/
|
||||
const keyboardEvents: KeyboardEvents = {
|
||||
|
||||
/**
|
||||
*
|
||||
* Custom hotkeys.
|
||||
*/
|
||||
keys: {
|
||||
|
||||
TAB: "Tab",
|
||||
TAB: "Tab",
|
||||
DUPLICATE: "d",
|
||||
UNINDENT: "[",
|
||||
INDENT: "]"
|
||||
|
||||
UNINDENT: "[",
|
||||
INDENT: "]"
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
* Hotkey response functions.
|
||||
*/
|
||||
handlers: {
|
||||
|
||||
/**
|
||||
* Smart application of tab indentations under various conditions.
|
||||
*
|
||||
* @param {number} start
|
||||
* @param {number} end
|
||||
* @param {string} value
|
||||
* @returns {string}
|
||||
* @private
|
||||
*/
|
||||
applyTab: function (start: number, end: number, value: string): string {
|
||||
applyTab: function (properties) {
|
||||
|
||||
return value.substring(0, start) + (
|
||||
value.substring(start, end).match(/\n/g) === null ?
|
||||
`\t${value.substring(start)}` :
|
||||
value.substring(start, end).replace(/^/gm, '\t') + value.substring(end)
|
||||
)
|
||||
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @param start
|
||||
* @param end
|
||||
* @param value
|
||||
* @returns {string}
|
||||
* @private
|
||||
*/
|
||||
_multiLineIndentation: function (start: number, end: number, value: string): string {
|
||||
|
||||
const endLine: string = new RegExp(`(?:\n|.){0,${end}}(^.*$)`, "m").exec(value)[1];
|
||||
|
||||
return value.substring(
|
||||
value.indexOf(
|
||||
new RegExp(`(?:\n|.){0,${start}}(^.*$)`, "m").exec(value)[1] // Start line.
|
||||
),
|
||||
(value.indexOf(endLine) ? value.indexOf(endLine) + endLine.length : end)
|
||||
);
|
||||
// Do not replace with variables; this
|
||||
// feature is optimised for swift response.
|
||||
return properties.value
|
||||
.substring(0, properties.start) + // Preceding text.
|
||||
(
|
||||
properties.value
|
||||
.substring(properties.start, properties.end) // Selected text
|
||||
.match(/\n/gm) === null ? // Not multi line?
|
||||
`\t${properties.value.substring(properties.start)}` : // Add `\t`.
|
||||
properties.value // Otherwise:
|
||||
.substring(properties.start, properties.end)
|
||||
.replace(/^/gm, '\t') + // Add `\t` to be beginning of each line.
|
||||
properties.value.substring(properties.end) // Succeeding text.
|
||||
)
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Smart removal of tab indentations.
|
||||
*
|
||||
* @param start
|
||||
* @param end
|
||||
* @param value
|
||||
* @param {JSON} properties
|
||||
* @returns {string}
|
||||
* @private
|
||||
*/
|
||||
applyIndentation: function (start: number, end: number, value: string): string {
|
||||
removeTab: function (properties) {
|
||||
|
||||
if (start === end) {
|
||||
const line: string = new RegExp(`(?:\n|.){0,${start}}(^.+$)`, "m").exec(value)[1];
|
||||
return value.replace(line, `\t${line}`)
|
||||
}
|
||||
let substitution: string = null,
|
||||
lineTotal: number = (
|
||||
properties.value
|
||||
.substring(
|
||||
properties.start,
|
||||
properties.end
|
||||
).match(/\n/g) || [] // Number of lines (\n) or empty array (zero).
|
||||
).length; // Length of the array is equal to the number of lines.
|
||||
|
||||
const content: string = this._multiLineIndentation(start, end, value);
|
||||
if (properties.start === properties.end) {
|
||||
|
||||
return value.replace(content, content.replace(/(^.+$)\n*/gmi, "\t$&"))
|
||||
// Replacing `\t` at a specific location
|
||||
// (+/- 1 chars) where there is no selection.
|
||||
properties.start =
|
||||
properties.start > 0 &&
|
||||
properties.value[properties.start - 1] // -1 is to account any tabs just before the cursor.
|
||||
.match(/\t/) !== null ? // if there's no `\t`, check the preceding character.
|
||||
properties.start - 1 : properties.start;
|
||||
|
||||
},
|
||||
substitution = properties.value
|
||||
.substring(properties.start)
|
||||
.replace("\t", ''); // Remove only a single `\t`.
|
||||
|
||||
/**
|
||||
*
|
||||
* @param start
|
||||
* @param end
|
||||
* @param value
|
||||
* @returns {string}
|
||||
* @private
|
||||
*/
|
||||
removeIndentation: function (start: number, end: number, value: string): string {
|
||||
|
||||
if (start === end) {
|
||||
const line: string = new RegExp(`(?:\n|.){0,${start}}(^\t.+$)`, "m").exec(value)[1];
|
||||
return value.replace(line, line.substring(1))
|
||||
}
|
||||
|
||||
const content: string = this._multiLineIndentation(start, end, value);
|
||||
|
||||
return value.replace(content, content.replace(/^\t(.+)\n*$/gmi, "$1"))
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {number} start
|
||||
* @param {number} end
|
||||
* @param {string} value
|
||||
* @returns {string}
|
||||
* @private
|
||||
*/
|
||||
removeTab: function (start: number, end: number, value: string): string {
|
||||
|
||||
let endString: string = null,
|
||||
lineNumbers: number = (value.substring(start, end).match(/\n/g) || []).length;
|
||||
|
||||
if (start === end) {
|
||||
|
||||
// Replacing `\t` at a specific location (+/- 1 chars) where there is no selection.
|
||||
start = start > 0 && value[start - 1].match(/\t/) !== null ? start - 1 : start;
|
||||
endString = value.substring(start).replace(/\t/, '');
|
||||
|
||||
} else if (!lineNumbers) {
|
||||
} else if (!lineTotal) {
|
||||
|
||||
// Replacing `\t` within a single line selection.
|
||||
endString = value.substring(start).replace(/\t/, '')
|
||||
|
||||
substitution =
|
||||
properties.value
|
||||
.substring(properties.start)
|
||||
.replace("\t", '')
|
||||
|
||||
} else {
|
||||
|
||||
// Replacing `\t` in the beginning of each line in a multi-line selection.
|
||||
endString = value.substring(start, end).replace(/^\t/gm, '') + value.substring(end, value.length);
|
||||
// Replacing `\t` in the beginning of each line
|
||||
// in a multi-line selection.
|
||||
substitution =
|
||||
properties.value.substring(
|
||||
properties.start,
|
||||
properties.end
|
||||
).replace(/^\t/gm, '') + // Selection.
|
||||
properties.value.substring(properties.end); // After the selection
|
||||
|
||||
}
|
||||
|
||||
return value.substring(0, start) + endString;
|
||||
return properties.value
|
||||
.substring(0, properties.start) + // Text preceding to selection / cursor.
|
||||
substitution
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles multi line indentations.
|
||||
*
|
||||
* @param {number} start
|
||||
* @param {number} end
|
||||
* @param {string} value
|
||||
* @param properties
|
||||
* @returns {string}
|
||||
* @private
|
||||
*/
|
||||
applyDuplication: function (start: number, end: number, value: string): string {
|
||||
_multiLineIndentation: function (properties) {
|
||||
|
||||
// Selected.
|
||||
if (start !== end) return (
|
||||
value.substring(0, start) +
|
||||
value.substring(start, end) +
|
||||
(~value.charAt(start - 1).indexOf('\n') || ~value.charAt(start).indexOf('\n') ? '\n' : '') +
|
||||
value.substring(start, end) +
|
||||
value.substring(end)
|
||||
// Last line in the selection; regardless of
|
||||
// where of not the entire line is selected.
|
||||
const endLine: string =
|
||||
new RegExp(`(?:\n|.){0,${properties.end}}(^.*$)`, "m")
|
||||
.exec(properties.value)[1];
|
||||
|
||||
// Do not replace with variables; this
|
||||
// feature is optimised for swift response.
|
||||
return properties.value.substring(
|
||||
// First line of the selection, regardless of
|
||||
// where or not the entire line is selected.
|
||||
properties.value.indexOf(
|
||||
new RegExp(`(?:\n|.){0,${properties.start}}(^.*$)`, "m")
|
||||
.exec(properties.value)[1] // Start line.
|
||||
), (
|
||||
// If there is a last line in a multi line selected
|
||||
// value where the last line is not empty or `\n`:
|
||||
properties.value.indexOf(endLine) ?
|
||||
// Location where the last line finishes with
|
||||
// respect to the entire value.
|
||||
properties.value.indexOf(endLine) + endLine.length :
|
||||
// Otherwise, where the selection ends.
|
||||
properties.end
|
||||
)
|
||||
);
|
||||
|
||||
// Not selected.
|
||||
let pattern: RegExp = new RegExp(`(?:.|\n){0,${end}}\n([^].+)(?:.|\n)*`, 'm'),
|
||||
line: string = '';
|
||||
},
|
||||
|
||||
value.replace(pattern, (match, p1) => line += p1);
|
||||
/**
|
||||
* Smart application of indentation at the beginning of the line.
|
||||
*
|
||||
* @returns {string}
|
||||
*/
|
||||
applyIndentation: function (properties) {
|
||||
|
||||
return value.replace(line, `${line}\n${line}`)
|
||||
// Single line?
|
||||
if (properties.start === properties.end) {
|
||||
// Current line, from the beginning to the end, regardless of any selections.
|
||||
const line: string =
|
||||
new RegExp(`(?:\n|.){0,${properties.start}}(^.+$)`, "m")
|
||||
.exec(properties.value)[1];
|
||||
|
||||
return properties.value.replace(line, `\t${line}`)
|
||||
}
|
||||
|
||||
// Multi line
|
||||
const content: string = this._multiLineIndentation({
|
||||
start: properties.start,
|
||||
end: properties.end,
|
||||
value: properties.value
|
||||
});
|
||||
|
||||
return properties.value
|
||||
.replace(
|
||||
content, // Existing contents.
|
||||
content.replace(/(^.+$)\n*/gmi, "\t$&") // Indented contents.
|
||||
)
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Smart removal of indentation from the beginning of the line.
|
||||
*
|
||||
* @returns {string}
|
||||
*/
|
||||
removeIndentation: function (properties) {
|
||||
|
||||
// Single Line
|
||||
if (properties.start === properties.end) {
|
||||
// Entire line where the line immediately begins
|
||||
// with a one or more `\t`, regardless of any
|
||||
// selections.
|
||||
const line: string =
|
||||
new RegExp(`(?:\n|.){0,${properties.start}}(^\t.+$)`, "m")
|
||||
.exec(properties.value)[1];
|
||||
|
||||
return properties.value
|
||||
.replace(
|
||||
line, // Existing content.
|
||||
line.substring(1) // First character (necessarily a `\t`) removed.
|
||||
)
|
||||
}
|
||||
|
||||
// Multi line
|
||||
const content: string = this._multiLineIndentation({
|
||||
start: properties.start,
|
||||
end: properties.end,
|
||||
value: properties.value
|
||||
});
|
||||
|
||||
return properties.value
|
||||
.replace(
|
||||
content, // Existing content.
|
||||
content.replace(/^\t(.+)\n*$/gmi, "$1") // A single `\t` removed from the beginning.
|
||||
)
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Duplication of the current or selected lines.
|
||||
*
|
||||
* @returns {string}
|
||||
*/
|
||||
applyDuplication: function (properties) {
|
||||
|
||||
// With selection.
|
||||
// Do not replace with variables. This
|
||||
// feature is optimised for swift response.
|
||||
if (properties.start !== properties.end)
|
||||
return (
|
||||
properties.value.substring( // Text preceding the selected area.
|
||||
0,
|
||||
properties.start
|
||||
) +
|
||||
properties.value.substring( // Selected area
|
||||
properties.start,
|
||||
properties.end
|
||||
) +
|
||||
(
|
||||
~properties.value // First character before the cursor is linebreak?
|
||||
.charAt(properties.start - 1)
|
||||
.indexOf('\n') || // --> or
|
||||
~properties.value // Character on the cursor is linebreak?
|
||||
.charAt(properties.start)
|
||||
.indexOf('\n') ? '\n' : '' // If either, add linebreak, otherwise add nothing.
|
||||
) +
|
||||
properties.value.substring( // Selected area (again for duplication).
|
||||
properties.start,
|
||||
properties.end
|
||||
) +
|
||||
properties.value.substring(properties.end) // Text succeeding the selected area.
|
||||
);
|
||||
|
||||
// Without selection.
|
||||
let pattern: RegExp = // Separate lines up to the end of the current line.
|
||||
new RegExp(`(?:.|\n){0,160}(^.*$)`, 'm'),
|
||||
line: string = '';
|
||||
|
||||
// Add anything found to the `line`. Note that
|
||||
// `replace` is used a simple hack; it functions
|
||||
// in a similar way to `regex.search` in Python.
|
||||
properties.value
|
||||
.replace(pattern, (match, p1) => line += p1);
|
||||
|
||||
return properties.value
|
||||
.replace(
|
||||
line, // Existing line.
|
||||
`${line}\n${line}` // Doubled ... magic!
|
||||
)
|
||||
|
||||
},
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Mapping of hotkeys from keyboard events to their corresponding functions.
|
||||
*
|
||||
* @param {KeyboardEvent} event
|
||||
* @returns {Function | Boolean}
|
||||
|
|
@ -267,6 +413,8 @@ const keyboardEvents = {
|
|||
return (event.ctrlKey || event.metaKey) ? this.handlers.removeIndentation : false;
|
||||
|
||||
default:
|
||||
// default would prevent the
|
||||
// inhibition of default settings.
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
@ -311,10 +459,13 @@ function updateHeight(editor: HTMLTextAreaElement): HTMLTextAreaElement {
|
|||
/**
|
||||
* @example
|
||||
*
|
||||
* let editor = document.getElementById('MyMarkdownEditor'),
|
||||
* preview = document.getElementById('MyMarkdownPreview');
|
||||
* let element = document.getElementsByClassName('markdownx');
|
||||
*
|
||||
* let mdx = new MarkdownX(editor, preview)
|
||||
* new MarkdownX(
|
||||
* element,
|
||||
* element.querySelector('.markdownx-editor'),
|
||||
* element.querySelector('.markdownx-preview')
|
||||
* )
|
||||
*
|
||||
* @param {HTMLElement} parent - Markdown editor element.
|
||||
* @param {HTMLTextAreaElement} editor - Markdown editor element.
|
||||
|
|
@ -322,7 +473,7 @@ function updateHeight(editor: HTMLTextAreaElement): HTMLTextAreaElement {
|
|||
*/
|
||||
const MarkdownX = function (parent: HTMLElement, editor: HTMLTextAreaElement, preview: HTMLElement): void {
|
||||
|
||||
const properties = {
|
||||
const properties: MarkdownxProperties = {
|
||||
|
||||
editor: editor,
|
||||
preview: preview,
|
||||
|
|
@ -380,7 +531,7 @@ const MarkdownX = function (parent: HTMLElement, editor: HTMLTextAreaElement, pr
|
|||
|
||||
// If `true`, the editor will expand to scrollHeight when needed.
|
||||
properties._editorIsResizable =
|
||||
(properties.editor.getAttribute(RESIZABILITY_ATTRIBUTE).match(/True/i) || []).length > 0;
|
||||
(properties.editor.getAttribute(RESIZABILITY_ATTRIBUTE).match(/true/i) || []).length > 0;
|
||||
|
||||
getMarkdown();
|
||||
|
||||
|
|
@ -445,11 +596,11 @@ const MarkdownX = function (parent: HTMLElement, editor: HTMLTextAreaElement, pr
|
|||
// Holding the start location before anything changes.
|
||||
const SELECTION_START: number = properties.editor.selectionStart;
|
||||
|
||||
properties.editor.value = handlerFunc(
|
||||
properties.editor.selectionStart,
|
||||
properties.editor.selectionEnd,
|
||||
properties.editor.value
|
||||
);
|
||||
properties.editor.value = handlerFunc({
|
||||
start: properties.editor.selectionStart,
|
||||
end: properties.editor.selectionEnd,
|
||||
value: properties.editor.value
|
||||
});
|
||||
|
||||
_markdownify();
|
||||
|
||||
|
|
@ -475,9 +626,9 @@ const MarkdownX = function (parent: HTMLElement, editor: HTMLTextAreaElement, pr
|
|||
preparePostData({image: file}) // Data
|
||||
);
|
||||
|
||||
xhr.success = (resp: string): void => {
|
||||
xhr.success = (resp: string): void | null => {
|
||||
|
||||
const response = JSON.parse(resp);
|
||||
const response: ImageUploadResponse = JSON.parse(resp);
|
||||
|
||||
if (response.image_code) {
|
||||
|
||||
|
|
@ -493,16 +644,16 @@ const MarkdownX = function (parent: HTMLElement, editor: HTMLTextAreaElement, pr
|
|||
} else {
|
||||
|
||||
console.error(XHR_RESPONSE_ERROR, response);
|
||||
triggerCustomEvent('markdownx.fileUploadError', properties.parent, [response])
|
||||
triggerCustomEvent('markdownx.fileUploadError', properties.parent, [response]);
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
properties.preview.innerHTML = this.response;
|
||||
properties.editor.style.opacity = NORMAL_OPACITY;
|
||||
|
||||
};
|
||||
|
||||
xhr.error = (response: string): void => {
|
||||
xhr.error = (response: any): void => {
|
||||
|
||||
properties.editor.style.opacity = NORMAL_OPACITY;
|
||||
console.error(response);
|
||||
|
|
@ -533,7 +684,7 @@ const MarkdownX = function (parent: HTMLElement, editor: HTMLTextAreaElement, pr
|
|||
|
||||
};
|
||||
|
||||
xhr.error = (response: string): void => {
|
||||
xhr.error = (response: any): void => {
|
||||
|
||||
console.error(response);
|
||||
|
||||
|
|
@ -549,16 +700,16 @@ const MarkdownX = function (parent: HTMLElement, editor: HTMLTextAreaElement, pr
|
|||
*
|
||||
* @param textToInsert
|
||||
*/
|
||||
const insertImage = (textToInsert): void => {
|
||||
const insertImage = (textToInsert: string): void => {
|
||||
|
||||
let cursorPosition = properties.editor.selectionStart,
|
||||
text = properties.editor.value,
|
||||
textBeforeCursor = text.substring(0, cursorPosition),
|
||||
textAfterCursor = text.substring(cursorPosition, text.length);
|
||||
properties.editor.value =
|
||||
`${properties.editor.value.substring(0, properties.editor.selectionStart)}\n\n` + // Preceding text.
|
||||
textToInsert +
|
||||
`\n\n${properties.editor.value.substring(properties.editor.selectionEnd)}`; // Succeeding text.
|
||||
|
||||
properties.editor.value = `${textBeforeCursor}${textToInsert}${textAfterCursor}`;
|
||||
properties.editor.selectionStart = cursorPosition + textToInsert.length;
|
||||
properties.editor.selectionEnd = cursorPosition + textToInsert.length;
|
||||
properties.editor.selectionStart =
|
||||
properties.editor.selectionEnd =
|
||||
properties.editor.selectionStart + textToInsert.length;
|
||||
|
||||
triggerEvent(properties.editor, 'keyup');
|
||||
inputChanged();
|
||||
|
|
|
|||
|
|
@ -58,12 +58,21 @@ export function zip (...rows) {
|
|||
}
|
||||
|
||||
|
||||
interface EventListener {
|
||||
object: Element | Document,
|
||||
listeners: {
|
||||
type: string,
|
||||
listener: any,
|
||||
capture: boolean,
|
||||
} []
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param collections
|
||||
* @returns
|
||||
*/
|
||||
export function mountEvents (...collections): any {
|
||||
export function mountEvents (...collections: EventListener[]): any[] {
|
||||
|
||||
return collections.map(events =>
|
||||
events.listeners.map(series =>
|
||||
|
|
|
|||
|
|
@ -22,6 +22,9 @@ var utils_1 = require("./utils");
|
|||
var UPLOAD_URL_ATTRIBUTE = "data-markdownx-upload-urls-path", PROCESSING_URL_ATTRIBUTE = "data-markdownx-urls-path", RESIZABILITY_ATTRIBUTE = "data-markdownx-editor-resizable", LATENCY_ATTRIBUTE = "data-markdownx-latency", LATENCY_MINIMUM = 500, // microseconds.
|
||||
XHR_RESPONSE_ERROR = "Invalid response", UPLOAD_START_OPACITY = "0.3", NORMAL_OPACITY = "1";
|
||||
// ---------------------------------------------------------------------------------------------------------------------
|
||||
/**
|
||||
*
|
||||
*/
|
||||
var EventHandlers = {
|
||||
/**
|
||||
* Routine tasks for event handlers (e.g. default preventions).
|
||||
|
|
@ -44,6 +47,9 @@ var EventHandlers = {
|
|||
return this.inhibitDefault(event);
|
||||
}
|
||||
};
|
||||
/**
|
||||
*
|
||||
*/
|
||||
var keyboardEvents = {
|
||||
/**
|
||||
*
|
||||
|
|
@ -60,107 +66,173 @@ var keyboardEvents = {
|
|||
handlers: {
|
||||
/**
|
||||
*
|
||||
* @param {number} start
|
||||
* @param {number} end
|
||||
* @param {string} value
|
||||
* @returns {string}
|
||||
* @private
|
||||
*/
|
||||
applyTab: function (start, end, value) {
|
||||
return value.substring(0, start) + (value.substring(start, end).match(/\n/g) === null ?
|
||||
"\t" + value.substring(start) :
|
||||
value.substring(start, end).replace(/^/gm, '\t') + value.substring(end));
|
||||
applyTab: function (properties) {
|
||||
// Do not replace with variables; this
|
||||
// feature is optimised for swift response.
|
||||
return properties.value
|
||||
.substring(0, properties.start) +
|
||||
(properties.value
|
||||
.substring(properties.start, properties.end) // Selected text
|
||||
.match(/\n/gm) === null ?
|
||||
"\t" + properties.value.substring(properties.start) :
|
||||
properties.value // Otherwise:
|
||||
.substring(properties.start, properties.end)
|
||||
.replace(/^/gm, '\t') +
|
||||
properties.value.substring(properties.end) // Succeeding text.
|
||||
);
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @param start
|
||||
* @param end
|
||||
* @param value
|
||||
* @returns {string}
|
||||
* @private
|
||||
*/
|
||||
_multiLineIndentation: function (start, end, value) {
|
||||
var endLine = new RegExp("(?:\n|.){0," + end + "}(^.*$)", "m").exec(value)[1];
|
||||
return value.substring(value.indexOf(new RegExp("(?:\n|.){0," + start + "}(^.*$)", "m").exec(value)[1] // Start line.
|
||||
), (value.indexOf(endLine) ? value.indexOf(endLine) + endLine.length : end));
|
||||
_multiLineIndentation: function (properties) {
|
||||
// Last line in the selection; regardless of
|
||||
// where of not the entire line is selected.
|
||||
var endLine = new RegExp("(?:\n|.){0," + properties.end + "}(^.*$)", "m")
|
||||
.exec(properties.value)[1];
|
||||
// Do not replace with variables; this
|
||||
// feature is optimised for swift response.
|
||||
return properties.value.substring(
|
||||
// First line of the selection, regardless of
|
||||
// where or not the entire line is selected.
|
||||
properties.value.indexOf(new RegExp("(?:\n|.){0," + properties.start + "}(^.*$)", "m")
|
||||
.exec(properties.value)[1] // Start line.
|
||||
), (
|
||||
// If there is a last line in a multi line selected
|
||||
// value where the last line is not empty or `\n`:
|
||||
properties.value.indexOf(endLine) ?
|
||||
// Location where the last line finishes with
|
||||
// respect to the entire value.
|
||||
properties.value.indexOf(endLine) + endLine.length :
|
||||
// Otherwise, where the selection ends.
|
||||
properties.end));
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @param start
|
||||
* @param end
|
||||
* @param value
|
||||
* @returns {string}
|
||||
* @private
|
||||
*/
|
||||
applyIndentation: function (start, end, value) {
|
||||
if (start === end) {
|
||||
var line = new RegExp("(?:\n|.){0," + start + "}(^.+$)", "m").exec(value)[1];
|
||||
return value.replace(line, "\t" + line);
|
||||
applyIndentation: function (properties) {
|
||||
// Single line?
|
||||
if (properties.start === properties.end) {
|
||||
// Current line, from the beginning to the end, regardless of any selections.
|
||||
var line = new RegExp("(?:\n|.){0," + properties.start + "}(^.+$)", "m")
|
||||
.exec(properties.value)[1];
|
||||
return properties.value.replace(line, "\t" + line);
|
||||
}
|
||||
var content = this._multiLineIndentation(start, end, value);
|
||||
return value.replace(content, content.replace(/(^.+$)\n*/gmi, "\t$&"));
|
||||
// Multi line
|
||||
var content = this._multiLineIndentation({
|
||||
start: properties.start,
|
||||
end: properties.end,
|
||||
value: properties.value
|
||||
});
|
||||
return properties.value
|
||||
.replace(content, // Existing contents.
|
||||
content.replace(/(^.+$)\n*/gmi, "\t$&") // Indented contents.
|
||||
);
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @param start
|
||||
* @param end
|
||||
* @param value
|
||||
* @returns {string}
|
||||
* @private
|
||||
*/
|
||||
removeIndentation: function (start, end, value) {
|
||||
if (start === end) {
|
||||
var line = new RegExp("(?:\n|.){0," + start + "}(^\t.+$)", "m").exec(value)[1];
|
||||
return value.replace(line, line.substring(1));
|
||||
removeIndentation: function (properties) {
|
||||
// Single Line
|
||||
if (properties.start === properties.end) {
|
||||
// Entire line where the line immediately begins
|
||||
// with a one or more `\t`, regardless of any
|
||||
// selections.
|
||||
var line = new RegExp("(?:\n|.){0," + properties.start + "}(^\t.+$)", "m")
|
||||
.exec(properties.value)[1];
|
||||
return properties.value
|
||||
.replace(line, // Existing content.
|
||||
line.substring(1) // First character (necessarily a `\t`) removed.
|
||||
);
|
||||
}
|
||||
var content = this._multiLineIndentation(start, end, value);
|
||||
return value.replace(content, content.replace(/^\t(.+)\n*$/gmi, "$1"));
|
||||
// Multi line
|
||||
var content = this._multiLineIndentation({
|
||||
start: properties.start,
|
||||
end: properties.end,
|
||||
value: properties.value
|
||||
});
|
||||
return properties.value
|
||||
.replace(content, // Existing content.
|
||||
content.replace(/^\t(.+)\n*$/gmi, "$1") // A single `\t` removed from the beginning.
|
||||
);
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @param {number} start
|
||||
* @param {number} end
|
||||
* @param {string} value
|
||||
* @returns {string}
|
||||
* @private
|
||||
*/
|
||||
removeTab: function (start, end, value) {
|
||||
var endString = null, lineNumbers = (value.substring(start, end).match(/\n/g) || []).length;
|
||||
if (start === end) {
|
||||
// Replacing `\t` at a specific location (+/- 1 chars) where there is no selection.
|
||||
start = start > 0 && value[start - 1].match(/\t/) !== null ? start - 1 : start;
|
||||
endString = value.substring(start).replace(/\t/, '');
|
||||
removeTab: function (properties) {
|
||||
var substitution = null, lineTotal = (properties.value
|
||||
.substring(properties.start, properties.end).match(/\n/g) || [] // Number of lines (\n) or empty array (zero).
|
||||
).length; // Length of the array is equal to the number of lines.
|
||||
if (properties.start === properties.end) {
|
||||
// Replacing `\t` at a specific location
|
||||
// (+/- 1 chars) where there is no selection.
|
||||
properties.start =
|
||||
properties.start > 0 &&
|
||||
properties.value[properties.start - 1] // -1 is to account any tabs just before the cursor.
|
||||
.match(/\t/) !== null ?
|
||||
properties.start - 1 : properties.start;
|
||||
substitution = properties.value
|
||||
.substring(properties.start)
|
||||
.replace("\t", ''); // Remove only a single `\t`.
|
||||
}
|
||||
else if (!lineNumbers) {
|
||||
else if (!lineTotal) {
|
||||
// Replacing `\t` within a single line selection.
|
||||
endString = value.substring(start).replace(/\t/, '');
|
||||
substitution =
|
||||
properties.value
|
||||
.substring(properties.start)
|
||||
.replace("\t", '');
|
||||
}
|
||||
else {
|
||||
// Replacing `\t` in the beginning of each line in a multi-line selection.
|
||||
endString = value.substring(start, end).replace(/^\t/gm, '') + value.substring(end, value.length);
|
||||
// Replacing `\t` in the beginning of each line
|
||||
// in a multi-line selection.
|
||||
substitution =
|
||||
properties.value.substring(properties.start, properties.end).replace(/^\t/gm, '') +
|
||||
properties.value.substring(properties.end); // After the selection
|
||||
}
|
||||
return value.substring(0, start) + endString;
|
||||
return properties.value
|
||||
.substring(0, properties.start) +
|
||||
substitution;
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @param {number} start
|
||||
* @param {number} end
|
||||
* @param {string} value
|
||||
* @returns {string}
|
||||
* @private
|
||||
*/
|
||||
applyDuplication: function (start, end, value) {
|
||||
// Selected.
|
||||
if (start !== end)
|
||||
return (value.substring(0, start) +
|
||||
value.substring(start, end) +
|
||||
(~value.charAt(start - 1).indexOf('\n') || ~value.charAt(start).indexOf('\n') ? '\n' : '') +
|
||||
value.substring(start, end) +
|
||||
value.substring(end));
|
||||
// Not selected.
|
||||
var pattern = new RegExp("(?:.|\n){0," + end + "}\n([^].+)(?:.|\n)*", 'm'), line = '';
|
||||
value.replace(pattern, function (match, p1) { return line += p1; });
|
||||
return value.replace(line, line + "\n" + line);
|
||||
applyDuplication: function (properties) {
|
||||
// With selection.
|
||||
// Do not replace with variables. This
|
||||
// feature is optimised for swift response.
|
||||
if (properties.start !== properties.end)
|
||||
return (properties.value.substring(// Text preceding the selected area.
|
||||
0, properties.start) +
|
||||
properties.value.substring(// Selected area
|
||||
properties.start, properties.end) +
|
||||
(~properties.value // First character before the cursor is linebreak?
|
||||
.charAt(properties.start - 1)
|
||||
.indexOf('\n') ||
|
||||
~properties.value // Character on the cursor is linebreak?
|
||||
.charAt(properties.start)
|
||||
.indexOf('\n') ? '\n' : '' // If either, add linebreak, otherwise add nothing.
|
||||
) +
|
||||
properties.value.substring(// Selected area (again for duplication).
|
||||
properties.start, properties.end) +
|
||||
properties.value.substring(properties.end) // Text succeeding the selected area.
|
||||
);
|
||||
// Without selection.
|
||||
var pattern = new RegExp("(?:.|\n){0,160}(^.*$)", 'm'), line = '';
|
||||
// Add anything found to the `line`. Note that
|
||||
// `replace` is used a simple hack; it functions
|
||||
// in a similar way to `regex.search` in Python.
|
||||
properties.value
|
||||
.replace(pattern, function (match, p1) { return line += p1; });
|
||||
return properties.value
|
||||
.replace(line, // Existing line.
|
||||
line + "\n" + line // Doubled ... magic!
|
||||
);
|
||||
},
|
||||
},
|
||||
/**
|
||||
|
|
@ -270,7 +342,7 @@ var MarkdownX = function (parent, editor, preview) {
|
|||
Math.max(parseInt(properties.editor.getAttribute(LATENCY_ATTRIBUTE)) || 0, LATENCY_MINIMUM);
|
||||
// If `true`, the editor will expand to scrollHeight when needed.
|
||||
properties._editorIsResizable =
|
||||
(properties.editor.getAttribute(RESIZABILITY_ATTRIBUTE).match(/True/i) || []).length > 0;
|
||||
(properties.editor.getAttribute(RESIZABILITY_ATTRIBUTE).match(/true/i) || []).length > 0;
|
||||
getMarkdown();
|
||||
utils_1.triggerCustomEvent("markdownx.init");
|
||||
};
|
||||
|
|
@ -314,7 +386,11 @@ var MarkdownX = function (parent, editor, preview) {
|
|||
EventHandlers.inhibitDefault(event);
|
||||
// Holding the start location before anything changes.
|
||||
var SELECTION_START = properties.editor.selectionStart;
|
||||
properties.editor.value = handlerFunc(properties.editor.selectionStart, properties.editor.selectionEnd, properties.editor.value);
|
||||
properties.editor.value = handlerFunc({
|
||||
start: properties.editor.selectionStart,
|
||||
end: properties.editor.selectionEnd,
|
||||
value: properties.editor.value
|
||||
});
|
||||
_markdownify();
|
||||
properties.editor.focus();
|
||||
// Set the cursor location to the start location of the selection.
|
||||
|
|
@ -344,8 +420,8 @@ var MarkdownX = function (parent, editor, preview) {
|
|||
else {
|
||||
console.error(XHR_RESPONSE_ERROR, response);
|
||||
utils_1.triggerCustomEvent('markdownx.fileUploadError', properties.parent, [response]);
|
||||
return null;
|
||||
}
|
||||
properties.preview.innerHTML = _this.response;
|
||||
properties.editor.style.opacity = NORMAL_OPACITY;
|
||||
};
|
||||
xhr.error = function (response) {
|
||||
|
|
@ -378,10 +454,13 @@ var MarkdownX = function (parent, editor, preview) {
|
|||
* @param textToInsert
|
||||
*/
|
||||
var insertImage = function (textToInsert) {
|
||||
var cursorPosition = properties.editor.selectionStart, text = properties.editor.value, textBeforeCursor = text.substring(0, cursorPosition), textAfterCursor = text.substring(cursorPosition, text.length);
|
||||
properties.editor.value = "" + textBeforeCursor + textToInsert + textAfterCursor;
|
||||
properties.editor.selectionStart = cursorPosition + textToInsert.length;
|
||||
properties.editor.selectionEnd = cursorPosition + textToInsert.length;
|
||||
properties.editor.value =
|
||||
properties.editor.value.substring(0, properties.editor.selectionStart) + "\n\n" +
|
||||
textToInsert +
|
||||
("\n\n" + properties.editor.value.substring(properties.editor.selectionEnd)); // Succeeding text.
|
||||
properties.editor.selectionStart =
|
||||
properties.editor.selectionEnd =
|
||||
properties.editor.selectionStart + textToInsert.length;
|
||||
utils_1.triggerEvent(properties.editor, 'keyup');
|
||||
inputChanged();
|
||||
};
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue