From 8506ea179d9fcc1b9e44f73820b64915e7bf0533 Mon Sep 17 00:00:00 2001
From: Pouria Hadjibagheri
Date: Sat, 18 Mar 2017 20:53:22 +0000
Subject: [PATCH] Now supports TAB indentations (including in selected
sub-strings) and unindentation with SHIFT + TAB.
---
markdownx/static/markdownx/js/markdownx.js | 442 ++++++------------
.../static/markdownx/js/markdownx.min.js | 2 +-
markdownx/static/markdownx/js/src/_backup | 287 ++++++++++++
.../static/markdownx/js/src/markdownx.ts | 116 ++++-
testapp/models.py | 1 +
5 files changed, 537 insertions(+), 311 deletions(-)
create mode 100644 markdownx/static/markdownx/js/src/_backup
diff --git a/markdownx/static/markdownx/js/markdownx.js b/markdownx/static/markdownx/js/markdownx.js
index 81aa64c..83b580f 100644
--- a/markdownx/static/markdownx/js/markdownx.js
+++ b/markdownx/static/markdownx/js/markdownx.js
@@ -16,349 +16,205 @@ var utils_1 = require("./utils");
var UPLOAD_URL_ATTRIBUTE = "data-markdownx-upload-urls-path", PROCESSING_URL_ATTRIBUTE = "data-markdownx-urls-path";
// ---------------------------------------------------------------------------------------------------------------------
/**
+ *
+ * @param {number} start
+ * @param {number} end
+ * @param {string} value
+ * @returns {string}
+ */
+function tabKeyEvent(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));
+}
+/**
+ *
+ * @param {number} start
+ * @param {number} end
+ * @param {string} value
+ * @returns {string}
+ */
+function shiftTabKeyEvent(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/, '');
+ }
+ else if (!lineNumbers) {
+ // Replacing `\t` within a single line selection.
+ endString = value.substring(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);
+ }
+ return value.substring(0, start) + endString;
+}
+/**
+ * @example
+ *
+ * let editor = document.getElementById('MyMarkdownEditor'),
+ * preview = document.getElementById('MyMarkdownPreview');
+ *
+ * let mdx = new MarkdownX(editor, preview)
*
* @param {HTMLTextAreaElement} editor - Markdown editor element.
* @param {HTMLElement} preview - Markdown preview element.
*/
-// const MarkdownX = function (editor: HTMLTextAreaElement, preview: Element) {
-//
-// this.editor = editor;
-// this.preview = preview;
-// this.editorIsResizable = this.editor.style.resize == 'none';
-// this.timeout = null;
-//
-// this.getEditorHeight = () => `${this.editor.scrollHeight}px`;
-//
-// this.markdownify = (): void => {
-//
-// clearTimeout(this.timeout);
-// this.timeout = setTimeout(this.getMarkdown, 500)
-//
-// };
-//
-// this.updateHeight = (): void => {
-//
-// this.editorIsResizable ? this.editor.style.height = this.getEditorHeight() : null
-//
-// };
-//
-// this.inputChanged = (): void => {
-//
-// this.updateHeight();
-// this.markdownify()
-//
-// };
-//
-// // ToDo: Deprecate.
-// this.onHtmlEvents = (event: Event): void => this.routineEventResponse(event);
-//
-// this.routineEventResponse = (event: any): void => {
-//
-// event.preventDefault();
-// event.stopPropagation()
-//
-// };
-//
-// this.onDragEnterEvent = (event: any): void => {
-//
-// event.dataTransfer.dropEffect = 'copy';
-// this.routineEventResponse(event)
-//
-// };
-//
-// this.onDragLeaveEvent = (event: Event): void => this.routineEventResponse(event);
-//
-// this.onDropEvent = (event: any): void => {
-//
-// if (event.dataTransfer && event.dataTransfer.files.length)
-// Object.keys(event.dataTransfer.files).map(fileKey => this.sendFile(event.dataTransfer.files[fileKey]));
-//
-// this.routineEventResponse(event);
-//
-// };
-//
-// this.onKeyDownEvent = (event: any): Boolean | null => {
-//
-// const TAB_ASCII_CODE = 9;
-//
-// if (event.keyCode !== TAB_ASCII_CODE) return null;
-//
-// let start: number = this.editor.selectionStart,
-// end: number = this.editor.selectionEnd,
-// value: string = this.editor.value;
-//
-// this.editor.value = `${value.substring(0, start)}\t${value.substring(end)}`;
-// this.editor.selectionStart = this.editor.selectionEnd = start++;
-//
-// this.markdownify();
-//
-// this.editor.focus();
-//
-// return false
-//
-// };
-//
-// this.sendFile = (file: File): void => {
-//
-// this.editor.style.opacity = "0.3";
-//
-// const xhr = new Request(
-// this.editor.getAttribute(UPLOAD_URL_ATTRIBUTE), // URL
-// preparePostData({image: file}) // Data
-// );
-//
-// xhr.success = (resp: string): void => {
-//
-// const response = JSON.parse(resp);
-//
-// if (response.image_code) {
-//
-// this.insertImage(response.image_code);
-// triggerCustomEvent('markdownx.fileUploadEnd', [response])
-//
-// } else if (response.image_path) {
-//
-// // ToDo: Deprecate.
-// this.insertImage(``);
-// triggerCustomEvent('markdownx.fileUploadEnd', [response])
-//
-// } else {
-//
-// console.error('Wrong response', response);
-// triggerCustomEvent('markdownx.fileUploadError', [response])
-//
-// }
-//
-// this.preview.innerHTML = this.response;
-// this.editor.style.opacity = "1";
-//
-// };
-//
-// xhr.error = (response: string): void => {
-//
-// this.editor.style.opacity = "1";
-// console.error(response);
-// triggerCustomEvent('fileUploadError', [response])
-//
-// };
-//
-// xhr.send()
-//
-// };
-//
-// this.getMarkdown = (): void => {
-//
-// const xhr = new Request(
-// this.editor.getAttribute(PROCESSING_URL_ATTRIBUTE), // URL
-// preparePostData({content: this.editor.value}) // Data
-// );
-//
-// xhr.success = (response: string): void => {
-// this.preview.innerHTML = response;
-// this.updateHeight();
-// triggerCustomEvent('markdownx.update', [response])
-// };
-//
-// xhr.error = (response: string): void => {
-// console.error(response);
-// triggerCustomEvent('markdownx.updateError', [response])
-// };
-//
-// xhr.send()
-//
-// };
-//
-// this.insertImage = (textToInsert): void => {
-//
-// let cursorPosition = this.editor.selectionStart,
-// text = this.editor.value,
-// textBeforeCursor = text.substring(0, cursorPosition),
-// textAfterCursor = text.substring(cursorPosition, text.length);
-//
-// this.editor.value = `${textBeforeCursor}${textToInsert}${textAfterCursor}`;
-// this.editor.selectionStart = cursorPosition + textToInsert.length;
-// this.editor.selectionEnd = cursorPosition + textToInsert.length;
-//
-// triggerEvent(this.editor, 'keyup');
-// this.inputChanged();
-//
-// };
-//
-// // Events
-// // ----------------------------------------------------------------------------------------------
-// let documentListeners = {
-// // ToDo: Deprecate.
-// object: document,
-// listeners: [
-// { type: 'drop' , capture: false, listener: this.onHtmlEvents },
-// { type: 'dragover' , capture: false, listener: this.onHtmlEvents },
-// { type: 'dragenter', capture: false, listener: this.onHtmlEvents },
-// { type: 'dragleave', capture: false, listener: this.onHtmlEvents }
-// ]
-// },
-// editorListeners = {
-// object: this.editor,
-// listeners: [
-// { type: 'drop', capture: false, listener: this.onDropEvent },
-// { type: 'input', capture: true , listener: this.inputChanged },
-// { type: 'keydown', capture: true , listener: this.onKeyDownEvent },
-// { type: 'dragover', capture: false, listener: this.onDragEnterEvent },
-// { type: 'dragenter', capture: false, listener: this.onDragEnterEvent },
-// { type: 'dragleave', capture: false, listener: this.onDragLeaveEvent },
-// { type: 'compositionstart', capture: true , listener: this.onKeyDownEvent }
-// ]
-// };
-//
-// // Initialise
-// // ----------------------------------------------------------------------------------------------
-//
-// mountEvents(editorListeners);
-// mountEvents(documentListeners); // ToDo: Deprecate.
-// triggerCustomEvent('markdownx.init');
-// this.editor.style.transition = "opacity 1s ease";
-// this.editor.style.webkitTransition = "opacity 1s ease";
-// this.getMarkdown();
-// this.inputChanged()
-//
-// };
-var MarkdownX = (function () {
- function MarkdownX(editor, preview) {
- this.UPLOAD_URL_ATTRIBUTE = "data-markdownx-upload-urls-path";
- this.PROCESSING_URL_ATTRIBUTE = "data-markdownx-urls-path";
- this.editor = editor;
- this.preview = preview;
- this.editorIsResizable = this.editor.style.resize == 'none';
- this.timeout = null;
- // Events
- // ----------------------------------------------------------------------------------------------
- var documentListeners = {
- // ToDo: Deprecate.
- object: document,
- listeners: [
- { type: 'drop', capture: false, listener: this.onHtmlEvents },
- { type: 'dragover', capture: false, listener: this.onHtmlEvents },
- { type: 'dragenter', capture: false, listener: this.onHtmlEvents },
- { type: 'dragleave', capture: false, listener: this.onHtmlEvents }
- ]
- }, editorListeners = {
- object: this.editor,
- listeners: [
- { type: 'drop', capture: false, listener: this.onDrop },
- { type: 'input', capture: true, listener: this.inputChanged },
- { type: 'keydown', capture: true, listener: this.onKeyDown },
- { type: 'dragover', capture: false, listener: this.onDragEnter },
- { type: 'dragenter', capture: false, listener: this.onDragEnter },
- { type: 'dragleave', capture: false, listener: this.onDragLeave },
- { type: 'compositionstart', capture: true, listener: this.onKeyDown }
- ]
- };
- // Initialise
- // ----------------------------------------------------------------------------------------------
- utils_1.mountEvents(editorListeners);
- utils_1.mountEvents(documentListeners); // ToDo: Deprecate.
- utils_1.triggerCustomEvent('markdownx.init');
- this.editor.style.transition = "opacity 1s ease";
- this.editor.style.webkitTransition = "opacity 1s ease";
- this.getMarkdown();
- this.inputChanged();
- }
- MarkdownX.prototype.getEditorHeight = function () { return this.editor.scrollHeight + "px"; };
- MarkdownX.prototype._markdownify = function () {
- clearTimeout(this.timeout);
- this.timeout = setTimeout(this.getMarkdown, 500);
+var MarkdownX = function (editor, preview) {
+ var _this = this;
+ this.editor = editor;
+ this.preview = preview;
+ this._editorIsResizable = this.editor.style.resize == 'none';
+ this.timeout = null;
+ this.getEditorHeight = function (editor) { return editor.scrollHeight + "px"; };
+ /**
+ * settings for ``timeout``.
+ *
+ * @private
+ */
+ this._markdownify = function () {
+ clearTimeout(_this.timeout);
+ _this.timeout = setTimeout(_this.getMarkdown, 500);
};
- MarkdownX.prototype.updateHeight = function () {
- this.editorIsResizable ? this.editor.style.height = this.getEditorHeight() : null;
+ this.updateHeight = function () {
+ _this._editorIsResizable ? _this.editor.style.height = _this.getEditorHeight(_this.editor) : null;
};
- MarkdownX.prototype.inputChanged = function () {
- this.updateHeight();
- this._markdownify();
+ this.inputChanged = function () {
+ _this.updateHeight();
+ _this._markdownify();
};
// ToDo: Deprecate.
- MarkdownX.prototype.onHtmlEvents = function (event) { this._routineEventResponse(event); };
- MarkdownX.prototype._routineEventResponse = function (event) {
+ this.onHtmlEvents = function (event) { return _this._routineEventResponse(event); };
+ /**
+ * Routine tasks for event handlers (e.g. default preventions).
+ *
+ * @param {Event} event
+ * @private
+ */
+ this._routineEventResponse = function (event) {
event.preventDefault();
event.stopPropagation();
};
- MarkdownX.prototype.onDragEnter = function (event) {
+ this.onDragEnter = function (event) {
event.dataTransfer.dropEffect = 'copy';
- this._routineEventResponse(event);
+ _this._routineEventResponse(event);
};
- MarkdownX.prototype.onDragLeave = function (event) {
- this._routineEventResponse(event);
- };
- MarkdownX.prototype.onDrop = function (event) {
- var _this = this;
+ this.onDragLeave = function (event) { return _this._routineEventResponse(event); };
+ this.onDrop = function (event) {
if (event.dataTransfer && event.dataTransfer.files.length)
Object.keys(event.dataTransfer.files).map(function (fileKey) { return _this.sendFile(event.dataTransfer.files[fileKey]); });
- this._routineEventResponse(event);
+ _this._routineEventResponse(event);
};
- MarkdownX.prototype.onKeyDown = function (event) {
- var TAB_ASCII_CODE = 9;
- if (event.keyCode !== TAB_ASCII_CODE)
+ this.onKeyDown = function (event) {
+ // ASCII code references:
+ var TAB_KEY = 9, SELECTION_START = _this.editor.selectionStart;
+ if (event.keyCode !== TAB_KEY)
return null;
- var start = this.editor.selectionStart, end = this.editor.selectionEnd, value = this.editor.value;
- this.editor.value = value.substring(0, start) + "\t" + value.substring(end);
- this.editor.selectionStart = this.editor.selectionEnd = start++;
- this._markdownify();
- this.editor.focus();
+ event.preventDefault();
+ var handlerFunc = event.shiftKey && event.keyCode === TAB_KEY ? shiftTabKeyEvent : tabKeyEvent;
+ _this.editor.value = handlerFunc(_this.editor.selectionStart, _this.editor.selectionEnd, _this.editor.value);
+ _this._markdownify();
+ _this.editor.focus();
+ _this.editor.selectionEnd = _this.editor.selectionStart = SELECTION_START;
return false;
};
- MarkdownX.prototype.sendFile = function (file) {
- var preview = this.preview, editor = this.editor, url = this.UPLOAD_URL_ATTRIBUTE, xhr = new utils_1.Request(editor.getAttribute(url), // URL
+ /**
+ *
+ * @param file
+ * @returns {Request}
+ */
+ this.sendFile = function (file) {
+ _this.editor.style.opacity = "0.3";
+ var xhr = new utils_1.Request(_this.editor.getAttribute(UPLOAD_URL_ATTRIBUTE), // URL
utils_1.preparePostData({ image: file }) // Data
);
- editor.style.opacity = "0.3";
xhr.success = function (resp) {
var response = JSON.parse(resp);
if (response.image_code) {
- this.insertImage(response.image_code);
+ _this.insertImage(response.image_code);
utils_1.triggerCustomEvent('markdownx.fileUploadEnd', [response]);
}
else if (response.image_path) {
// ToDo: Deprecate.
- this.insertImage("");
+ _this.insertImage("");
utils_1.triggerCustomEvent('markdownx.fileUploadEnd', [response]);
}
else {
console.error('Wrong response', response);
utils_1.triggerCustomEvent('markdownx.fileUploadError', [response]);
}
- preview.innerHTML = this.response;
- editor.style.opacity = "1";
+ _this.preview.innerHTML = _this.response;
+ _this.editor.style.opacity = "1";
};
xhr.error = function (response) {
- editor.style.opacity = "1";
+ _this.editor.style.opacity = "1";
console.error(response);
utils_1.triggerCustomEvent('fileUploadError', [response]);
};
- xhr.send();
+ return xhr.send();
};
- MarkdownX.prototype.getMarkdown = function () {
- var preview = this.preview, editor = this.editor, url = this.PROCESSING_URL_ATTRIBUTE, xhr = new utils_1.Request(editor.getAttribute(url), // URL
- utils_1.preparePostData({ content: this.editor.value }) // Data
+ /**
+ *
+ * @returns {Request}
+ */
+ this.getMarkdown = function () {
+ var xhr = new utils_1.Request(_this.editor.getAttribute(PROCESSING_URL_ATTRIBUTE), // URL
+ utils_1.preparePostData({ content: _this.editor.value }) // Data
);
xhr.success = function (response) {
- preview.innerHTML = response;
- this.updateHeight();
+ _this.preview.innerHTML = response;
+ _this.updateHeight();
utils_1.triggerCustomEvent('markdownx.update', [response]);
};
xhr.error = function (response) {
console.error(response);
utils_1.triggerCustomEvent('markdownx.updateError', [response]);
};
- xhr.send();
+ return xhr.send();
};
- MarkdownX.prototype.insertImage = function (textToInsert) {
- var cursorPosition = this.editor.selectionStart, text = this.editor.value, textBeforeCursor = text.substring(0, cursorPosition), textAfterCursor = text.substring(cursorPosition, text.length);
- this.editor.value = "" + textBeforeCursor + textToInsert + textAfterCursor;
- this.editor.selectionStart = cursorPosition + textToInsert.length;
- this.editor.selectionEnd = cursorPosition + textToInsert.length;
- utils_1.triggerEvent(this.editor, 'keyup');
- this.inputChanged();
+ this.insertImage = function (textToInsert) {
+ var cursorPosition = _this.editor.selectionStart, text = _this.editor.value, textBeforeCursor = text.substring(0, cursorPosition), textAfterCursor = text.substring(cursorPosition, text.length);
+ _this.editor.value = "" + textBeforeCursor + textToInsert + textAfterCursor;
+ _this.editor.selectionStart = cursorPosition + textToInsert.length;
+ _this.editor.selectionEnd = cursorPosition + textToInsert.length;
+ utils_1.triggerEvent(_this.editor, 'keyup');
+ _this.inputChanged();
};
- return MarkdownX;
-}());
+ // Events
+ // ----------------------------------------------------------------------------------------------
+ var documentListeners = {
+ // ToDo: Deprecate.
+ object: document,
+ listeners: [
+ { type: 'drop', capture: false, listener: this.onHtmlEvents },
+ { type: 'dragover', capture: false, listener: this.onHtmlEvents },
+ { type: 'dragenter', capture: false, listener: this.onHtmlEvents },
+ { type: 'dragleave', capture: false, listener: this.onHtmlEvents }
+ ]
+ }, editorListeners = {
+ object: this.editor,
+ listeners: [
+ { type: 'drop', capture: false, listener: this.onDrop },
+ { type: 'input', capture: true, listener: this.inputChanged },
+ { type: 'keydown', capture: true, listener: this.onKeyDown },
+ { type: 'dragover', capture: false, listener: this.onDragEnter },
+ { type: 'dragenter', capture: false, listener: this.onDragEnter },
+ { type: 'dragleave', capture: false, listener: this.onDragLeave },
+ { type: 'compositionstart', capture: true, listener: this.onKeyDown }
+ ]
+ };
+ // Initialise
+ // ----------------------------------------------------------------------------------------------
+ utils_1.mountEvents(editorListeners);
+ utils_1.mountEvents(documentListeners); // ToDo: Deprecate.
+ utils_1.triggerCustomEvent('markdownx.init');
+ this.editor.style.transition = "opacity 1s ease";
+ this.editor.style.webkitTransition = "opacity 1s ease";
+ this.getMarkdown();
+ this.inputChanged();
+};
(function (funcName, baseObj) {
// The public function name defaults to window.docReady
// but you can pass in your own object and own function
diff --git a/markdownx/static/markdownx/js/markdownx.min.js b/markdownx/static/markdownx/js/markdownx.min.js
index 550ce3d..cffd877 100644
--- a/markdownx/static/markdownx/js/markdownx.min.js
+++ b/markdownx/static/markdownx/js/markdownx.min.js
@@ -1 +1 @@
-!function t(e,n,r){function o(s,a){if(!n[s]){if(!e[s]){var u="function"==typeof require&&require;if(!a&&u)return u(s,!0);if(i)return i(s,!0);var c=new Error("Cannot find module '"+s+"'");throw c.code="MODULE_NOT_FOUND",c}var p=n[s]={exports:{}};e[s][0].call(p.exports,function(t){var n=e[s][1][t];return o(n?n:t)},p,p.exports,t,e,n,r)}return n[s].exports}for(var i="function"==typeof require&&require,s=0;s .markdownx-editor"),e=document.querySelectorAll(".markdownx > .markdownx-preview");return r.zip(t,e).map(function(t){return new o(t[0],t[1])})})},{"./utils":2}],2:[function(t,e,n){"use strict";function r(t){if(document.cookie&&document.cookie.length){var e=document.cookie.split(";").filter(function(e){return e.indexOf(t+"=")!==-1})[0];try{return decodeURIComponent(e.trim().substring(t.length+1))}catch(e){if(e instanceof TypeError)return console.info('No cookie with key "'+t+'". Wrong name?'),null;throw e}}return null}function o(){for(var t=[],e=0;e-1&&n.splice(r,1),t.className=n.join(" ")}})}n.__esModule=!0,n.getCookie=r,n.zip=o,n.mountEvents=i,n.preparePostData=s;var d=function(){if("XMLHttpRequest"in window)return new XMLHttpRequest;try{return new ActiveXObject("Msxml2.XMLHTTP.6.0")}catch(t){}try{return new ActiveXObject("Msxml2.XMLHTTP.3.0")}catch(t){}try{return new ActiveXObject("Microsoft.XMLHTTP")}catch(t){}throw alert("Your browser belongs to history!"),new TypeError("This browser does not support AJAX requests.")},l=function(){function t(t,e){this.xhr=d(),this.url=t,this.data=e}return t.prototype.progress=function(t){t.lengthComputable&&console.log(t.loaded/t.total*100+"% uploaded")},t.prototype.error=function(t){console.error(t)},t.prototype.success=function(t){console.info(t)},t.prototype.send=function(){var t=this,e=this.success,n=this.error,r=this.progress;this.xhr.open("POST",this.url,!0),this.xhr.setRequestHeader("X-Requested-With","XMLHttpRequest"),this.xhr.upload.onprogress=function(t){return r(t)},this.xhr.onerror=function(e){n(t.xhr.responseText)},this.xhr.onload=function(n){var r=null;t.xhr.readyState==XMLHttpRequest.DONE&&(r=t.xhr.responseType&&"text"!==t.xhr.responseType?"document"===t.xhr.responseType?t.xhr.responseXML:t.xhr.response:t.xhr.responseText),e(r)},this.xhr.send(this.data)},t}();n.Request=l,n.triggerEvent=a,n.triggerCustomEvent=u,n.addClass=c,n.removeClass=p},{}]},{},[1]);
\ No newline at end of file
+!function e(t,n,r){function o(s,u){if(!n[s]){if(!t[s]){var a="function"==typeof require&&require;if(!u&&a)return a(s,!0);if(i)return i(s,!0);var c=new Error("Cannot find module '"+s+"'");throw c.code="MODULE_NOT_FOUND",c}var d=n[s]={exports:{}};t[s][0].call(d.exports,function(e){var n=t[s][1][e];return o(n?n:e)},d,d.exports,e,t,n,r)}return n[s].exports}for(var i="function"==typeof require&&require,s=0;s0&&null!==n[e-1].match(/\t/)?e-1:e,r=n.substring(e).replace(/\t/,"")):r=o?n.substring(e,t).replace(/^\t/gm,"")+n.substring(t,n.length):n.substring(e).replace(/\t/,""),n.substring(0,e)+r}n.__esModule=!0;var i=e("./utils"),s=function(e,t){var n=this;this.editor=e,this.preview=t,this._editorIsResizable="none"==this.editor.style.resize,this.timeout=null,this.getEditorHeight=function(e){return e.scrollHeight+"px"},this._markdownify=function(){clearTimeout(n.timeout),n.timeout=setTimeout(n.getMarkdown,500)},this.updateHeight=function(){n._editorIsResizable&&(n.editor.style.height=n.getEditorHeight(n.editor))},this.inputChanged=function(){n.updateHeight(),n._markdownify()},this.onHtmlEvents=function(e){return n._routineEventResponse(e)},this._routineEventResponse=function(e){e.preventDefault(),e.stopPropagation()},this.onDragEnter=function(e){e.dataTransfer.dropEffect="copy",n._routineEventResponse(e)},this.onDragLeave=function(e){return n._routineEventResponse(e)},this.onDrop=function(e){e.dataTransfer&&e.dataTransfer.files.length&&Object.keys(e.dataTransfer.files).map(function(t){return n.sendFile(e.dataTransfer.files[t])}),n._routineEventResponse(e)},this.onKeyDown=function(e){var t=n.editor.selectionStart;if(9!==e.keyCode)return null;e.preventDefault();var i=e.shiftKey&&9===e.keyCode?o:r;return n.editor.value=i(n.editor.selectionStart,n.editor.selectionEnd,n.editor.value),n._markdownify(),n.editor.focus(),n.editor.selectionEnd=n.editor.selectionStart=t,!1},this.sendFile=function(e){n.editor.style.opacity="0.3";var t=new i.Request(n.editor.getAttribute("data-markdownx-upload-urls-path"),i.preparePostData({image:e}));return t.success=function(e){var t=JSON.parse(e);t.image_code?(n.insertImage(t.image_code),i.triggerCustomEvent("markdownx.fileUploadEnd",[t])):t.image_path?(n.insertImage(''),i.triggerCustomEvent("markdownx.fileUploadEnd",[t])):(console.error("Wrong response",t),i.triggerCustomEvent("markdownx.fileUploadError",[t])),n.preview.innerHTML=n.response,n.editor.style.opacity="1"},t.error=function(e){n.editor.style.opacity="1",console.error(e),i.triggerCustomEvent("fileUploadError",[e])},t.send()},this.getMarkdown=function(){var e=new i.Request(n.editor.getAttribute("data-markdownx-urls-path"),i.preparePostData({content:n.editor.value}));return e.success=function(e){n.preview.innerHTML=e,n.updateHeight(),i.triggerCustomEvent("markdownx.update",[e])},e.error=function(e){console.error(e),i.triggerCustomEvent("markdownx.updateError",[e])},e.send()},this.insertImage=function(e){var t=n.editor.selectionStart,r=n.editor.value,o=r.substring(0,t),s=r.substring(t,r.length);n.editor.value=""+o+e+s,n.editor.selectionStart=t+e.length,n.editor.selectionEnd=t+e.length,i.triggerEvent(n.editor,"keyup"),n.inputChanged()};var s={object:document,listeners:[{type:"drop",capture:!1,listener:this.onHtmlEvents},{type:"dragover",capture:!1,listener:this.onHtmlEvents},{type:"dragenter",capture:!1,listener:this.onHtmlEvents},{type:"dragleave",capture:!1,listener:this.onHtmlEvents}]},u={object:this.editor,listeners:[{type:"drop",capture:!1,listener:this.onDrop},{type:"input",capture:!0,listener:this.inputChanged},{type:"keydown",capture:!0,listener:this.onKeyDown},{type:"dragover",capture:!1,listener:this.onDragEnter},{type:"dragenter",capture:!1,listener:this.onDragEnter},{type:"dragleave",capture:!1,listener:this.onDragLeave},{type:"compositionstart",capture:!0,listener:this.onKeyDown}]};i.mountEvents(u),i.mountEvents(s),i.triggerCustomEvent("markdownx.init"),this.editor.style.transition="opacity 1s ease",this.editor.style.webkitTransition="opacity 1s ease",this.getMarkdown(),this.inputChanged()};!function(e,t){e=e||"docReady",t=t||window;var n=[],r=!1,o=!1,i=function(){r||(r=!0,n.map(function(e){return e.fn.call(window,e.ctx)}),n=[])};t[e]=function(e,t){if(r)return void setTimeout(function(){return e(t)},1);n.push({fn:e,ctx:t}),"complete"===document.readyState?setTimeout(i,1):o||(document.addEventListener("DOMContentLoaded",i,!1),window.addEventListener("load",i,!1),o=!0)}}("docReady",window),docReady(function(){var e=document.querySelectorAll(".markdownx > .markdownx-editor"),t=document.querySelectorAll(".markdownx > .markdownx-preview");return i.zip(e,t).map(function(e){return new s(e[0],e[1])})})},{"./utils":2}],2:[function(e,t,n){"use strict";function r(e){if(document.cookie&&document.cookie.length){var t=document.cookie.split(";").filter(function(t){return t.indexOf(e+"=")!==-1})[0];try{return decodeURIComponent(t.trim().substring(e.length+1))}catch(t){if(t instanceof TypeError)return console.info('No cookie with key "'+e+'". Wrong name?'),null;throw t}}return null}function o(){for(var e=[],t=0;t-1&&n.splice(r,1),e.className=n.join(" ")}})}n.__esModule=!0,n.getCookie=r,n.zip=o,n.mountEvents=i,n.preparePostData=s;var l=function(){if("XMLHttpRequest"in window)return new XMLHttpRequest;try{return new ActiveXObject("Msxml2.XMLHTTP.6.0")}catch(e){}try{return new ActiveXObject("Msxml2.XMLHTTP.3.0")}catch(e){}try{return new ActiveXObject("Microsoft.XMLHTTP")}catch(e){}throw alert("Your browser belongs to history!"),new TypeError("This browser does not support AJAX requests.")},p=function(){function e(e,t){this.xhr=l(),this.url=e,this.data=t}return e.prototype.progress=function(e){e.lengthComputable&&console.log(e.loaded/e.total*100+"% uploaded")},e.prototype.error=function(e){console.error(e)},e.prototype.success=function(e){console.info(e)},e.prototype.send=function(){var e=this,t=this.success,n=this.error,r=this.progress;this.xhr.open("POST",this.url,!0),this.xhr.setRequestHeader("X-Requested-With","XMLHttpRequest"),this.xhr.upload.onprogress=function(e){return r(e)},this.xhr.onerror=function(t){n(e.xhr.responseText)},this.xhr.onload=function(n){var r=null;e.xhr.readyState==XMLHttpRequest.DONE&&(r=e.xhr.responseType&&"text"!==e.xhr.responseType?"document"===e.xhr.responseType?e.xhr.responseXML:e.xhr.response:e.xhr.responseText),t(r)},this.xhr.send(this.data)},e}();n.Request=p,n.triggerEvent=u,n.triggerCustomEvent=a,n.addClass=c,n.removeClass=d},{}]},{},[1]);
\ No newline at end of file
diff --git a/markdownx/static/markdownx/js/src/_backup b/markdownx/static/markdownx/js/src/_backup
new file mode 100644
index 0000000..2dd102a
--- /dev/null
+++ b/markdownx/static/markdownx/js/src/_backup
@@ -0,0 +1,287 @@
+///
+/**
+ * Markdownx
+ *
+ * Frontend (JavaScript) management of Django-Markdownx module.
+ *
+ * Written in JavaScript (ECMA6), compiled in (ECMA6 - 2015).
+ *
+ * Requirements:
+ * - Modern browser with support for HTML5 and ECMA 2011+ (IE 9+).
+ */
+
+
+/**
+ * Looks for a cookie, and if found, returns the values.
+ *
+ * NOTE: Only the first item in the array is returned
+ * to eliminate the need for array deconstruction in
+ * the target.
+ *
+ * @param {string} name - The name of the cookie.
+ * @returns {string | null}
+ */
+function getCookie (name: string): string | null {
+ if (document.cookie && document.cookie.length) {
+
+ const cookies = document.cookie
+ .split(';')
+ .filter(cookie => cookie.indexOf(`${name}=`) !== -1)[0];
+
+ try{
+
+ return decodeURIComponent(cookies.trim().substring(name.length + 1));
+
+ } catch (e){
+
+ if (e instanceof TypeError) {
+ console.info(`No cookie with key "${name}". Wrong name?`);
+ return null
+ }
+
+ throw e
+ }
+ }
+
+ return null;
+}
+
+/**
+ *
+ * @param data
+ * @param csrf
+ * @returns {FormData}
+ */
+function preparePostData(data: Object, csrf=true){
+ let form: FormData = new FormData();
+
+ csrf ? form.append("csrfmiddlewaretoken", getCookie('csrftoken')) : null;
+
+ Object.keys(data).map(key => form.append(key, data[key]));
+
+ return form
+}
+
+
+($ => {
+
+ if (!$) $ = django.jQuery;
+
+ /**
+ *
+ * @returns {JQuery|markdownx}
+ */
+ $.fn.markdownx = function () {
+
+ return this.each(() => {
+
+ /**
+ *
+ */
+ let getMarkdown = () => {
+
+ $.ajax({
+ type: 'POST',
+ url: markdownxEditor.data("markdownxUrlsPath"),
+ data: preparePostData({content: markdownxEditor.val()}),
+ processData: false,
+ contentType: false,
+
+ success: response => {
+ markdownxPreview.html(response);
+ updateHeight();
+ markdownx.trigger('markdownx.update', [response]);
+ },
+
+ error: response => {
+ console.error(response);
+ markdownx.trigger('markdownx.updateError', [response]);
+ }
+ });
+
+ };
+
+ /**
+ *
+ */
+ let updateHeight = () => {
+ isMarkdownxEditorResizable ?
+ markdownxEditor.innerHeight(markdownxEditor.prop('scrollHeight')) : null
+ };
+
+ /**
+ *
+ * @param textToInsert
+ */
+ let insertImage = textToInsert => {
+ let cursor_pos = markdownxEditor.prop('selectionStart'),
+ text = markdownxEditor.val(),
+ textBeforeCursor = text.substring(0, cursor_pos),
+ textAfterCursor = text.substring(cursor_pos, text.length);
+
+ markdownxEditor
+ .val(textBeforeCursor + textToInsert + textAfterCursor)
+ .prop('selectionStart', cursor_pos + textToInsert.length)
+ .prop('selectionEnd', cursor_pos + textToInsert.length)
+ .keyup();
+
+ updateHeight();
+ markdownify();
+ };
+
+ /**
+ *
+ * @param file
+ */
+ let sendFile = file => {
+
+ $.ajax({
+ type: 'POST',
+ url: markdownxEditor.data("markdownxUploadUrlsPath"),
+ data: preparePostData({image: file}),
+ processData: false,
+ contentType: false,
+
+ beforeSend: () => {
+ markdownxEditor.fadeTo("fast", 0.3);
+ markdownx.trigger('markdownx.fileUploadBegin');
+ },
+
+ success: response => {
+ markdownxEditor.fadeTo("fast", 1);
+
+ if (response.image_code) {
+ insertImage(response.image_code);
+ markdownx.trigger('markdownx.fileUploadEnd', [response]);
+ } else if (response.image_path) {
+ // For backwards-compatibility
+ insertImage(``);
+ markdownx.trigger('markdownx.fileUploadEnd', [response]);
+ } else {
+ console.error('Wrong response', response);
+ markdownx.trigger('markdownx.fileUploadError', [response]);
+ }
+ },
+
+ error: response => {
+ console.error(response);
+ markdownxEditor.fadeTo("fast", 1);
+ markdownx.trigger('markdownx.fileUploadError', [response]);
+ }
+ });
+
+ };
+
+ let timeout;
+
+ /**
+ *
+ */
+ let markdownify = () => {
+ clearTimeout(timeout);
+ timeout = setTimeout(getMarkdown, 500);
+ };
+
+ /**
+ *
+ * @param event
+ * @returns {boolean}
+ */
+ let onKeyDownEvent = function(event) {
+ const TAB_ASCII_CODE = 9;
+
+ if (event.keyCode !== TAB_ASCII_CODE) return null;
+
+ let start: number = this.editor.selectionStart,
+ end: number = this.editor.selectionEnd,
+ value: string = this.editor.value;
+
+ this.editor.value = `${value.substring(0, start)}\t${value.substring(end)}`;
+
+ this.editor.selectionStart = this.editor.selectionEnd = start++;
+
+ markdownify();
+
+ this.editor.focus();
+
+ return false;
+
+ };
+
+ /**
+ *
+ */
+ let onInputChangeEvent = () => {
+ updateHeight();
+ markdownify();
+ };
+
+ /**
+ *
+ * @param event
+ */
+ let onHtmlEvents = event => {
+ event.preventDefault();
+ event.stopPropagation();
+ };
+
+ /**
+ *
+ * @param event
+ */
+ let onDragEnterEvent = event => {
+ event.originalEvent.dataTransfer.dropEffect = 'copy';
+ event.preventDefault();
+ event.stopPropagation();
+ };
+
+ /**
+ *
+ * @param event
+ */
+ let onDragLeaveEvent = event => {
+ event.preventDefault();
+ event.stopPropagation();
+ };
+
+ let onDropEvent = event => {
+ const dataTransfer = event.originalEvent.dataTransfer;
+
+ if (dataTransfer)
+ dataTransfer.files.length ? dataTransfer.files.map((file) => sendFile(file)) : null;
+
+ event.preventDefault();
+ event.stopPropagation();
+ };
+
+ // Init
+
+
+ let markdownx = $(this),
+ markdownxEditor: any = $(this).find('.markdownx-editor'),
+ markdownxPreview: any = $(this).find('.markdownx-preview'),
+ isMarkdownxEditorResizable = markdownxEditor.is("[data-markdownx-editor-resizable]");
+
+ $('html').on('dragenter.markdownx dragover.markdownx drop.markdownx dragleave.markdownx', onHtmlEvents);
+
+ markdownxEditor
+ .on('keydown.markdownx', onKeyDownEvent)
+ .on('input.markdownx propertychange.markdownx', onInputChangeEvent)
+ .on('dragenter.markdownx dragover.markdownx', onDragEnterEvent)
+ .on('dragleave.markdownx', onDragLeaveEvent)
+ .on('drop.markdownx', onDropEvent);
+
+ markdownx.trigger('markdownx.init');
+
+ updateHeight();
+ markdownify();
+ });
+ };
+
+ $(() => {
+
+ $('.markdownx').markdownx();
+
+ });
+
+})(jQuery);
diff --git a/markdownx/static/markdownx/js/src/markdownx.ts b/markdownx/static/markdownx/js/src/markdownx.ts
index af93898..d74b9ba 100644
--- a/markdownx/static/markdownx/js/src/markdownx.ts
+++ b/markdownx/static/markdownx/js/src/markdownx.ts
@@ -29,6 +29,61 @@ const UPLOAD_URL_ATTRIBUTE: string = "data-markdownx-upload-urls-path",
// ---------------------------------------------------------------------------------------------------------------------
+
+/**
+ *
+ * @param {number} start
+ * @param {number} end
+ * @param {string} value
+ * @returns {string}
+ */
+function tabKeyEvent(start: number, end: number, value: string): string {
+
+ 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 {number} start
+ * @param {number} end
+ * @param {string} value
+ * @returns {string}
+ */
+function shiftTabKeyEvent(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) {
+
+ // Replacing `\t` within a single line selection.
+ endString = value.substring(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);
+
+ }
+
+ return value.substring(0, start) + endString;
+
+}
+
+
/**
* @example
*
@@ -42,13 +97,18 @@ const UPLOAD_URL_ATTRIBUTE: string = "data-markdownx-upload-urls-path",
*/
const MarkdownX = function (editor: HTMLTextAreaElement, preview: Element) {
- this.editor = editor;
- this.preview = preview;
- this.editorIsResizable = this.editor.style.resize == 'none';
- this.timeout = null;
+ this.editor = editor;
+ this.preview = preview;
+ this._editorIsResizable = this.editor.style.resize == 'none';
+ this.timeout = null;
- this.getEditorHeight = () => `${this.editor.scrollHeight}px`;
+ this.getEditorHeight = (editor: HTMLTextAreaElement) => `${editor.scrollHeight}px`;
+ /**
+ * settings for ``timeout``.
+ *
+ * @private
+ */
this._markdownify = (): void => {
clearTimeout(this.timeout);
@@ -58,7 +118,7 @@ const MarkdownX = function (editor: HTMLTextAreaElement, preview: Element) {
this.updateHeight = (): void => {
- this.editorIsResizable ? this.editor.style.height = this.getEditorHeight() : null
+ this._editorIsResizable ? this.editor.style.height = this.getEditorHeight(this.editor) : null
};
@@ -72,6 +132,12 @@ const MarkdownX = function (editor: HTMLTextAreaElement, preview: Element) {
// ToDo: Deprecate.
this.onHtmlEvents = (event: Event): void => this._routineEventResponse(event);
+ /**
+ * Routine tasks for event handlers (e.g. default preventions).
+ *
+ * @param {Event} event
+ * @private
+ */
this._routineEventResponse = (event: any): void => {
event.preventDefault();
@@ -99,26 +165,38 @@ const MarkdownX = function (editor: HTMLTextAreaElement, preview: Element) {
this.onKeyDown = (event: any): Boolean | null => {
- const TAB_ASCII_CODE = 9;
+ // ASCII code references:
+ const TAB_KEY: number = 9,
+ SELECTION_START: number = this.editor.selectionStart;
- if (event.keyCode !== TAB_ASCII_CODE) return null;
+ if (event.keyCode !== TAB_KEY) return null;
- let start: number = this.editor.selectionStart,
- end: number = this.editor.selectionEnd,
- value: string = this.editor.value;
+ event.preventDefault();
- this.editor.value = `${value.substring(0, start)}\t${value.substring(end)}`;
- this.editor.selectionStart = this.editor.selectionEnd = start++;
+ let handlerFunc = event.shiftKey && event.keyCode === TAB_KEY ? shiftTabKeyEvent : tabKeyEvent;
+
+ this.editor.value = handlerFunc(
+ this.editor.selectionStart,
+ this.editor.selectionEnd,
+ this.editor.value
+ );
this._markdownify();
this.editor.focus();
+ this.editor.selectionEnd = this.editor.selectionStart = SELECTION_START;
+
return false
};
- this.sendFile = (file: File): void => {
+ /**
+ *
+ * @param file
+ * @returns {Request}
+ */
+ this.sendFile = (file: File): Request => {
this.editor.style.opacity = "0.3";
@@ -162,11 +240,15 @@ const MarkdownX = function (editor: HTMLTextAreaElement, preview: Element) {
};
- xhr.send()
+ return xhr.send()
};
- this.getMarkdown = (): void => {
+ /**
+ *
+ * @returns {Request}
+ */
+ this.getMarkdown = (): Request => {
const xhr = new Request(
this.editor.getAttribute(PROCESSING_URL_ATTRIBUTE), // URL
@@ -184,7 +266,7 @@ const MarkdownX = function (editor: HTMLTextAreaElement, preview: Element) {
triggerCustomEvent('markdownx.updateError', [response])
};
- xhr.send()
+ return xhr.send()
};
diff --git a/testapp/models.py b/testapp/models.py
index 30c74b7..111c0b8 100644
--- a/testapp/models.py
+++ b/testapp/models.py
@@ -2,6 +2,7 @@ from django.db import models
from markdownx.models import MarkdownxField
+
class MyModel(models.Model):
markdownx_field1 = MarkdownxField()
markdownx_field2 = MarkdownxField()