From b5eb7f9c0c19f88af0fcdf21b476abd7303e000c Mon Sep 17 00:00:00 2001
From: Pouria Hadjibagheri
Date: Sun, 19 Mar 2017 16:43:53 +0000
Subject: [PATCH] JavaScript updated.
---
markdownx/static/markdownx/js/markdownx.js | 239 ++++++++------
.../static/markdownx/js/markdownx.min.js | 2 +-
.../static/markdownx/js/src/markdownx.ts | 296 +++++++++++-------
3 files changed, 340 insertions(+), 197 deletions(-)
diff --git a/markdownx/static/markdownx/js/markdownx.js b/markdownx/static/markdownx/js/markdownx.js
index 83b580f..19bc645 100644
--- a/markdownx/static/markdownx/js/markdownx.js
+++ b/markdownx/static/markdownx/js/markdownx.js
@@ -13,7 +13,7 @@
"use strict";
exports.__esModule = true;
var utils_1 = require("./utils");
-var UPLOAD_URL_ATTRIBUTE = "data-markdownx-upload-urls-path", PROCESSING_URL_ATTRIBUTE = "data-markdownx-urls-path";
+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";
// ---------------------------------------------------------------------------------------------------------------------
/**
*
@@ -22,7 +22,7 @@ var UPLOAD_URL_ATTRIBUTE = "data-markdownx-upload-urls-path", PROCESSING_URL_ATT
* @param {string} value
* @returns {string}
*/
-function tabKeyEvent(start, end, value) {
+function applyIndentation(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));
@@ -34,7 +34,7 @@ function tabKeyEvent(start, end, value) {
* @param {string} value
* @returns {string}
*/
-function shiftTabKeyEvent(start, end, value) {
+function removeIndentation(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.
@@ -51,6 +51,34 @@ function shiftTabKeyEvent(start, end, value) {
}
return value.substring(0, start) + endString;
}
+/**
+ *
+ * @param start
+ * @param end
+ * @param value
+ * @returns {string}
+ */
+function applyDuplication(start, end, value) {
+ var pattern = new RegExp("(?:.|\n){0," + end + "}\n([^].+)(?:.|\n)*", 'm');
+ switch (start) {
+ case end:
+ var line_1 = '';
+ value.replace(pattern, function (match, p1) { return line_1 += p1; });
+ return value.replace(line_1, line_1 + "\n" + line_1);
+ default:
+ 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));
+ }
+}
+function getHeight(element) {
+ return Math.max(// Maximum of computed or set heights.
+ parseInt(window.getComputedStyle(element).height), // Height is not set in styles.
+ (parseInt(element.style.height) || 0) // Property's own height if set, otherwise 0.
+ );
+}
/**
* @example
*
@@ -64,92 +92,155 @@ function shiftTabKeyEvent(start, end, value) {
*/
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"; };
+ var properties = {
+ editor: editor,
+ preview: preview,
+ _editorIsResizable: null,
+ _latency: null
+ };
+ var _initialize = function () {
+ _this.timeout = null;
+ // Events
+ // ----------------------------------------------------------------------------------------------
+ var documentListeners = {
+ // ToDo: Deprecate.
+ object: document,
+ listeners: [
+ { type: "drop", capture: false, listener: onHtmlEvents },
+ { type: "dragover", capture: false, listener: onHtmlEvents },
+ { type: "dragenter", capture: false, listener: onHtmlEvents },
+ { type: "dragleave", capture: false, listener: onHtmlEvents }
+ ]
+ }, editorListeners = {
+ object: properties.editor,
+ listeners: [
+ { type: "drop", capture: false, listener: onDrop },
+ { type: "input", capture: true, listener: inputChanged },
+ { type: "keydown", capture: true, listener: onKeyDown },
+ { type: "dragover", capture: false, listener: onDragEnter },
+ { type: "dragenter", capture: false, listener: onDragEnter },
+ { type: "dragleave", capture: false, listener: onDragLeave },
+ { type: "compositionstart", capture: true, listener: onKeyDown }
+ ]
+ };
+ // Initialise
+ // --------------------------------------------------------
+ utils_1.mountEvents(editorListeners);
+ utils_1.mountEvents(documentListeners); // ToDo: Deprecate.
+ properties.editor.style.transition = "opacity 1s ease";
+ properties.editor.style.webkitTransition = "opacity 1s ease";
+ // Latency must be a value >= 500 microseconds.
+ properties._latency = Math.max(parseInt(properties.editor.getAttribute(LATENCY_ATTRIBUTE)) || 0, 500);
+ properties._editorIsResizable =
+ (properties.editor.getAttribute(RESIZABILITY_ATTRIBUTE).match(/True/) || []).length > 0;
+ getMarkdown();
+ inputChanged();
+ utils_1.triggerCustomEvent("markdownx.init");
+ };
/**
* settings for ``timeout``.
*
* @private
*/
- this._markdownify = function () {
+ var _markdownify = function () {
clearTimeout(_this.timeout);
- _this.timeout = setTimeout(_this.getMarkdown, 500);
+ _this.timeout = setTimeout(getMarkdown, properties._latency);
};
- this.updateHeight = function () {
- _this._editorIsResizable ? _this.editor.style.height = _this.getEditorHeight(_this.editor) : null;
- };
- this.inputChanged = function () {
- _this.updateHeight();
- _this._markdownify();
- };
- // ToDo: Deprecate.
- 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) {
+ var _inhibitDefault = function (event) {
event.preventDefault();
event.stopPropagation();
};
- this.onDragEnter = function (event) {
+ var updateHeight = function () {
+ // Ensure that the editor is resizable before anything else.
+ // Change size if scroll is larger that height, otherwise do nothing.
+ if (properties._editorIsResizable && getHeight(properties.editor) < properties.editor.scrollHeight)
+ properties.editor.style.height = properties.editor.scrollHeight + "px";
+ };
+ var inputChanged = function () {
+ updateHeight();
+ _markdownify();
+ };
+ // ToDo: Deprecate.
+ var onHtmlEvents = function (event) { return _inhibitDefault(event); };
+ var onDragEnter = function (event) {
event.dataTransfer.dropEffect = 'copy';
- _this._routineEventResponse(event);
+ _inhibitDefault(event);
};
- this.onDragLeave = function (event) { return _this._routineEventResponse(event); };
- this.onDrop = function (event) {
+ var onDragLeave = function (event) { return _inhibitDefault(event); };
+ var 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);
+ Object.keys(event.dataTransfer.files).map(function (fileKey) { return sendFile(event.dataTransfer.files[fileKey]); });
+ _inhibitDefault(event);
};
- this.onKeyDown = function (event) {
- // ASCII code references:
- var TAB_KEY = 9, SELECTION_START = _this.editor.selectionStart;
- if (event.keyCode !== TAB_KEY)
+ /**
+ *
+ * @param event
+ * @returns {KeyboardEvent}
+ */
+ var onKeyDown = function (event) {
+ // `Tab` for indentation, `d` for duplication.
+ if (event.key !== 'Tab' && event.key !== 'd')
return null;
- 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;
+ _inhibitDefault(event);
+ var handlerFunc = null;
+ switch (event.key) {
+ case "Tab":
+ // Shift pressed: un-indent, otherwise indent.
+ handlerFunc = event.shiftKey ? removeIndentation : applyIndentation;
+ break;
+ case "d":
+ if (event.ctrlKey || event.metaKey)
+ handlerFunc = applyDuplication;
+ else
+ return null;
+ break;
+ default:
+ return null;
+ }
+ // 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);
+ _markdownify();
+ properties.editor.focus();
+ // Set the cursor location to the start location of the selection.
+ properties.editor.selectionEnd = properties.editor.selectionStart = SELECTION_START;
return false;
};
/**
*
* @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
+ var sendFile = function (file) {
+ properties.editor.style.opacity = "0.3";
+ var xhr = new utils_1.Request(properties.editor.getAttribute(UPLOAD_URL_ATTRIBUTE), // URL
utils_1.preparePostData({ image: file }) // Data
);
xhr.success = function (resp) {
var response = JSON.parse(resp);
if (response.image_code) {
- _this.insertImage(response.image_code);
+ insertImage(response.image_code);
utils_1.triggerCustomEvent('markdownx.fileUploadEnd', [response]);
}
else if (response.image_path) {
// ToDo: Deprecate.
- _this.insertImage("");
+ insertImage("");
utils_1.triggerCustomEvent('markdownx.fileUploadEnd', [response]);
}
else {
console.error('Wrong response', response);
utils_1.triggerCustomEvent('markdownx.fileUploadError', [response]);
}
- _this.preview.innerHTML = _this.response;
- _this.editor.style.opacity = "1";
+ properties.preview.innerHTML = _this.response;
+ properties.editor.style.opacity = "1";
};
xhr.error = function (response) {
- _this.editor.style.opacity = "1";
+ properties.editor.style.opacity = "1";
console.error(response);
utils_1.triggerCustomEvent('fileUploadError', [response]);
};
@@ -157,15 +248,14 @@ var MarkdownX = function (editor, preview) {
};
/**
*
- * @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
+ var getMarkdown = function () {
+ var xhr = new utils_1.Request(properties.editor.getAttribute(PROCESSING_URL_ATTRIBUTE), // URL
+ utils_1.preparePostData({ content: properties.editor.value }) // Data
);
xhr.success = function (response) {
- _this.preview.innerHTML = response;
- _this.updateHeight();
+ properties.preview.innerHTML = response;
+ updateHeight();
utils_1.triggerCustomEvent('markdownx.update', [response]);
};
xhr.error = function (response) {
@@ -174,46 +264,15 @@ var MarkdownX = function (editor, preview) {
};
return xhr.send();
};
- 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();
+ 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;
+ utils_1.triggerEvent(properties.editor, 'keyup');
+ inputChanged();
};
- // 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();
+ _initialize();
};
(function (funcName, baseObj) {
// The public function name defaults to window.docReady
diff --git a/markdownx/static/markdownx/js/markdownx.min.js b/markdownx/static/markdownx/js/markdownx.min.js
index cffd877..61bd683 100644
--- a/markdownx/static/markdownx/js/markdownx.min.js
+++ b/markdownx/static/markdownx/js/markdownx.min.js
@@ -1 +1 @@
-!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
+!function e(t,r,n){function o(s,a){if(!r[s]){if(!t[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 l=r[s]={exports:{}};t[s][0].call(l.exports,function(e){var r=t[s][1][e];return o(r?r:e)},l,l.exports,e,t,r,n)}return r[s].exports}for(var i="function"==typeof require&&require,s=0;s0&&null!==r[e-1].match(/\t/)?e-1:e,n=r.substring(e).replace(/\t/,"")):n=o?r.substring(e,t).replace(/^\t/gm,"")+r.substring(t,r.length):r.substring(e).replace(/\t/,""),r.substring(0,e)+n}function i(e,t,r){var n=new RegExp("(?:.|\n){0,"+t+"}\n([^].+)(?:.|\n)*","m");switch(e){case t:var o="";return r.replace(n,function(e,t){return o+=t}),r.replace(o,o+"\n"+o);default:return r.substring(0,e)+r.substring(e,t)+(~r.charAt(e-1).indexOf("\n")||~r.charAt(e).indexOf("\n")?"\n":"")+r.substring(e,t)+r.substring(t)}}function s(e){return Math.max(parseInt(window.getComputedStyle(e).height),parseInt(e.style.height)||0)}r.__esModule=!0;var a=e("./utils"),u=function(e,t){var r=this,u={editor:e,preview:t,_editorIsResizable:null,_latency:null},c=function(){clearTimeout(r.timeout),r.timeout=setTimeout(w,u._latency)},l=function(e){e.preventDefault(),e.stopPropagation()},d=function(){u._editorIsResizable&&s(u.editor)0,w(),p(),a.triggerCustomEvent("markdownx.init")}()};!function(e,t){e=e||"docReady",t=t||window;var r=[],n=!1,o=!1,i=function(){n||(n=!0,r.map(function(e){return e.fn.call(window,e.ctx)}),r=[])};t[e]=function(e,t){if(n)return void setTimeout(function(){return e(t)},1);r.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 a.zip(e,t).map(function(e){return new u(e[0],e[1])})})},{"./utils":2}],2:[function(e,t,r){"use strict";function n(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&&r.splice(n,1),e.className=r.join(" ")}})}r.__esModule=!0,r.getCookie=n,r.zip=o,r.mountEvents=i,r.preparePostData=s;var d=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=d(),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,r=this.error,n=this.progress;this.xhr.open("POST",this.url,!0),this.xhr.setRequestHeader("X-Requested-With","XMLHttpRequest"),this.xhr.upload.onprogress=function(e){return n(e)},this.xhr.onerror=function(t){r(e.xhr.responseText)},this.xhr.onload=function(r){var n=null;e.xhr.readyState==XMLHttpRequest.DONE&&(n=e.xhr.responseType&&"text"!==e.xhr.responseType?"document"===e.xhr.responseType?e.xhr.responseXML:e.xhr.response:e.xhr.responseText),t(n)},this.xhr.send(this.data)},e}();r.Request=p,r.triggerEvent=a,r.triggerCustomEvent=u,r.addClass=c,r.removeClass=l},{}]},{},[1]);
\ No newline at end of file
diff --git a/markdownx/static/markdownx/js/src/markdownx.ts b/markdownx/static/markdownx/js/src/markdownx.ts
index d74b9ba..dbb5e2e 100644
--- a/markdownx/static/markdownx/js/src/markdownx.ts
+++ b/markdownx/static/markdownx/js/src/markdownx.ts
@@ -25,7 +25,9 @@ import {
} from "./utils";
const UPLOAD_URL_ATTRIBUTE: string = "data-markdownx-upload-urls-path",
- PROCESSING_URL_ATTRIBUTE: string = "data-markdownx-urls-path";
+ PROCESSING_URL_ATTRIBUTE: string = "data-markdownx-urls-path",
+ RESIZABILITY_ATTRIBUTE: string = "data-markdownx-editor-resizable",
+ LATENCY_ATTRIBUTE: string = "data-markdownx-latency";
// ---------------------------------------------------------------------------------------------------------------------
@@ -37,7 +39,7 @@ const UPLOAD_URL_ATTRIBUTE: string = "data-markdownx-upload-urls-path",
* @param {string} value
* @returns {string}
*/
-function tabKeyEvent(start: number, end: number, value: string): string {
+function applyIndentation(start: number, end: number, value: string): string {
return value.substring(0, start) + (
value.substring(start, end).match(/\n/g) === null ?
@@ -55,7 +57,7 @@ function tabKeyEvent(start: number, end: number, value: string): string {
* @param {string} value
* @returns {string}
*/
-function shiftTabKeyEvent(start: number, end: number, value: string): string {
+function removeIndentation(start: number, end: number, value: string): string {
let endString: string = null,
lineNumbers: number = (value.substring(start, end).match(/\n/g) || []).length;
@@ -84,6 +86,45 @@ function shiftTabKeyEvent(start: number, end: number, value: string): string {
}
+/**
+ *
+ * @param start
+ * @param end
+ * @param value
+ * @returns {string}
+ */
+function applyDuplication(start, end, value): string {
+
+ const pattern = new RegExp(`(?:.|\n){0,${end}}\n([^].+)(?:.|\n)*`, 'm');
+
+ switch (start) {
+ case end: // not selected.
+ let line: string = '';
+ value.replace(pattern, (match, p1) => line += p1);
+ return value.replace(line, `${line}\n${line}`);
+
+ default: // selected.
+ 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)
+ )
+ }
+
+}
+
+function getHeight (element: HTMLElement): number {
+
+ return Math.max( // Maximum of computed or set heights.
+ parseInt(window.getComputedStyle(element).height), // Height is not set in styles.
+ (parseInt(element.style.height) || 0) // Property's own height if set, otherwise 0.
+ )
+
+}
+
+
/**
* @example
*
@@ -95,97 +136,176 @@ function shiftTabKeyEvent(start: number, end: number, value: string): string {
* @param {HTMLTextAreaElement} editor - Markdown editor element.
* @param {HTMLElement} preview - Markdown preview element.
*/
-const MarkdownX = function (editor: HTMLTextAreaElement, preview: Element) {
+const MarkdownX = function (editor: HTMLTextAreaElement, preview: Element): void {
- this.editor = editor;
- this.preview = preview;
- this._editorIsResizable = this.editor.style.resize == 'none';
- this.timeout = null;
+ const properties = {
- this.getEditorHeight = (editor: HTMLTextAreaElement) => `${editor.scrollHeight}px`;
+ editor: editor,
+ preview: preview,
+ _editorIsResizable: null,
+ _latency: null
+
+ };
+
+ const _initialize = () => {
+
+ this.timeout = null;
+
+ // Events
+ // ----------------------------------------------------------------------------------------------
+ let documentListeners = {
+ // ToDo: Deprecate.
+ object: document,
+ listeners: [
+ { type: "drop" , capture: false, listener: onHtmlEvents },
+ { type: "dragover" , capture: false, listener: onHtmlEvents },
+ { type: "dragenter", capture: false, listener: onHtmlEvents },
+ { type: "dragleave", capture: false, listener: onHtmlEvents }
+ ]
+ },
+ editorListeners = {
+ object: properties.editor,
+ listeners: [
+ { type: "drop", capture: false, listener: onDrop },
+ { type: "input", capture: true , listener: inputChanged },
+ { type: "keydown", capture: true , listener: onKeyDown },
+ { type: "dragover", capture: false, listener: onDragEnter },
+ { type: "dragenter", capture: false, listener: onDragEnter },
+ { type: "dragleave", capture: false, listener: onDragLeave },
+ { type: "compositionstart", capture: true , listener: onKeyDown }
+ ]
+ };
+
+ // Initialise
+ // --------------------------------------------------------
+ mountEvents(editorListeners);
+ mountEvents(documentListeners); // ToDo: Deprecate.
+
+ properties.editor.style.transition = "opacity 1s ease";
+ properties.editor.style.webkitTransition = "opacity 1s ease";
+
+ // Latency must be a value >= 500 microseconds.
+ properties._latency = Math.max(parseInt(properties.editor.getAttribute(LATENCY_ATTRIBUTE)) || 0, 500);
+
+ properties._editorIsResizable =
+ (properties.editor.getAttribute(RESIZABILITY_ATTRIBUTE).match(/True/) || []).length > 0;
+
+ getMarkdown();
+ inputChanged();
+
+ triggerCustomEvent("markdownx.init");
+
+ };
/**
* settings for ``timeout``.
*
* @private
*/
- this._markdownify = (): void => {
+ const _markdownify = (): void => {
clearTimeout(this.timeout);
- this.timeout = setTimeout(this.getMarkdown, 500)
+ this.timeout = setTimeout(getMarkdown, properties._latency)
};
- this.updateHeight = (): void => {
-
- this._editorIsResizable ? this.editor.style.height = this.getEditorHeight(this.editor) : null
-
- };
-
- this.inputChanged = (): void => {
-
- this.updateHeight();
- this._markdownify()
-
- };
-
- // 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 => {
+ const _inhibitDefault = (event: any): void => {
event.preventDefault();
event.stopPropagation()
};
- this.onDragEnter = (event: any): void => {
+ const updateHeight = (): void => {
+ // Ensure that the editor is resizable before anything else.
+ // Change size if scroll is larger that height, otherwise do nothing.
+ if (properties._editorIsResizable && getHeight(properties.editor) < properties.editor.scrollHeight)
+ properties.editor.style.height = `${properties.editor.scrollHeight}px`;
+
+ };
+
+ const inputChanged = (): void => {
+
+ updateHeight();
+ _markdownify()
+
+ };
+
+ // ToDo: Deprecate.
+ const onHtmlEvents = (event: Event): void => _inhibitDefault(event);
+
+ const onDragEnter = (event: any): void => {
event.dataTransfer.dropEffect = 'copy';
- this._routineEventResponse(event)
+ _inhibitDefault(event)
};
- this.onDragLeave = (event: Event): void => this._routineEventResponse(event);
+ const onDragLeave = (event: Event): void => _inhibitDefault(event);
- this.onDrop = (event: any): void => {
+ const onDrop = (event: any): void => {
if (event.dataTransfer && event.dataTransfer.files.length)
- Object.keys(event.dataTransfer.files).map(fileKey => this.sendFile(event.dataTransfer.files[fileKey]));
+ Object.keys(event.dataTransfer.files).map(fileKey => sendFile(event.dataTransfer.files[fileKey]));
- this._routineEventResponse(event);
+ _inhibitDefault(event);
};
- this.onKeyDown = (event: any): Boolean | null => {
+ /**
+ *
+ * @param event
+ * @returns {KeyboardEvent}
+ */
+ const onKeyDown = (event: KeyboardEvent): Boolean | null => {
- // ASCII code references:
- const TAB_KEY: number = 9,
- SELECTION_START: number = this.editor.selectionStart;
+ // `Tab` for indentation, `d` for duplication.
+ if (event.key !== 'Tab' && event.key !== 'd') return null;
- if (event.keyCode !== TAB_KEY) return null;
+ _inhibitDefault(event);
- event.preventDefault();
- let handlerFunc = event.shiftKey && event.keyCode === TAB_KEY ? shiftTabKeyEvent : tabKeyEvent;
+ let handlerFunc = null;
- this.editor.value = handlerFunc(
- this.editor.selectionStart,
- this.editor.selectionEnd,
- this.editor.value
+ switch (event.key) {
+ case "Tab": // For indentation.
+ // Shift pressed: un-indent, otherwise indent.
+ handlerFunc = event.shiftKey ? removeIndentation : applyIndentation;
+ break;
+
+ case "d": // For duplication.
+ if (event.ctrlKey || event.metaKey) // Is CTRL or CMD (on Mac) pressed?
+ handlerFunc = applyDuplication;
+ else
+ return null;
+
+ break;
+
+ default:
+ return null
+ }
+
+ // 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
);
- this._markdownify();
+ _markdownify();
- this.editor.focus();
+ properties.editor.focus();
- this.editor.selectionEnd = this.editor.selectionStart = SELECTION_START;
+ // Set the cursor location to the start location of the selection.
+ properties.editor.selectionEnd = properties.editor.selectionStart = SELECTION_START;
return false
@@ -194,14 +314,13 @@ const MarkdownX = function (editor: HTMLTextAreaElement, preview: Element) {
/**
*
* @param file
- * @returns {Request}
*/
- this.sendFile = (file: File): Request => {
+ const sendFile = (file: File) => {
- this.editor.style.opacity = "0.3";
+ properties.editor.style.opacity = "0.3";
const xhr = new Request(
- this.editor.getAttribute(UPLOAD_URL_ATTRIBUTE), // URL
+ properties.editor.getAttribute(UPLOAD_URL_ATTRIBUTE), // URL
preparePostData({image: file}) // Data
);
@@ -211,13 +330,13 @@ const MarkdownX = function (editor: HTMLTextAreaElement, preview: Element) {
if (response.image_code) {
- this.insertImage(response.image_code);
+ insertImage(response.image_code);
triggerCustomEvent('markdownx.fileUploadEnd', [response])
} else if (response.image_path) {
// ToDo: Deprecate.
- this.insertImage(``);
+ insertImage(``);
triggerCustomEvent('markdownx.fileUploadEnd', [response])
} else {
@@ -227,14 +346,14 @@ const MarkdownX = function (editor: HTMLTextAreaElement, preview: Element) {
}
- this.preview.innerHTML = this.response;
- this.editor.style.opacity = "1";
+ properties.preview.innerHTML = this.response;
+ properties.editor.style.opacity = "1";
};
xhr.error = (response: string): void => {
- this.editor.style.opacity = "1";
+ properties.editor.style.opacity = "1";
console.error(response);
triggerCustomEvent('fileUploadError', [response])
@@ -246,18 +365,17 @@ const MarkdownX = function (editor: HTMLTextAreaElement, preview: Element) {
/**
*
- * @returns {Request}
*/
- this.getMarkdown = (): Request => {
+ const getMarkdown = () => {
const xhr = new Request(
- this.editor.getAttribute(PROCESSING_URL_ATTRIBUTE), // URL
- preparePostData({content: this.editor.value}) // Data
+ properties.editor.getAttribute(PROCESSING_URL_ATTRIBUTE), // URL
+ preparePostData({content: properties.editor.value}) // Data
);
xhr.success = (response: string): void => {
- this.preview.innerHTML = response;
- this.updateHeight();
+ properties.preview.innerHTML = response;
+ updateHeight();
triggerCustomEvent('markdownx.update', [response])
};
@@ -270,57 +388,23 @@ const MarkdownX = function (editor: HTMLTextAreaElement, preview: Element) {
};
- this.insertImage = (textToInsert): void => {
+ const insertImage = (textToInsert): void => {
- let cursorPosition = this.editor.selectionStart,
- text = this.editor.value,
+ let cursorPosition = properties.editor.selectionStart,
+ text = properties.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;
+ properties.editor.value = `${textBeforeCursor}${textToInsert}${textAfterCursor}`;
+ properties.editor.selectionStart = cursorPosition + textToInsert.length;
+ properties.editor.selectionEnd = cursorPosition + textToInsert.length;
- triggerEvent(this.editor, 'keyup');
- this.inputChanged();
+ triggerEvent(properties.editor, 'keyup');
+ 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.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
- // ----------------------------------------------------------------------------------------------
-
- 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()
+ _initialize();
};