+ // Handle scroll to sync display view
+ cm.on('scroll', instance => {
+ // Thanks to http://liuhao.im/english/2015/11/10/the-sync-scroll-of-markdown-editor-in-javascript.html
+ let scroll = instance.getScrollInfo();
+ let atEnd = scroll.top + scroll.clientHeight === scroll.height;
+ if (atEnd) {
+ scope.$emit('markdown-scroll', -1);
+ return;
+ }
+ let lineNum = instance.lineAtHeight(scroll.top, 'local');
+ let range = instance.getRange({line: 0, ch: null}, {line: lineNum, ch: null});
+ let parser = new DOMParser();
+ let doc = parser.parseFromString(md.render(range), 'text/html');
+ let totalLines = doc.documentElement.querySelectorAll('body > *');
+ scope.$emit('markdown-scroll', totalLines.length);
+ });
+
+ // Handle image paste
+ cm.on('paste', (cm, event) => {
+ if (!event.clipboardData || !event.clipboardData.items) return;
+ for (let i = 0; i < event.clipboardData.items.length; i++) {
+ uploadImage(event.clipboardData.items[i].getAsFile());
+ }
+ });
+
+ // Handle images on drag-drop
+ cm.on('drop', (cm, event) => {
+ event.stopPropagation();
+ event.preventDefault();
+ let cursorPos = cm.coordsChar({left: event.pageX, top: event.pageY});
+ cm.setCursor(cursorPos);
+ if (!event.dataTransfer || !event.dataTransfer.files) return;
+ for (let i = 0; i < event.dataTransfer.files.length; i++) {
+ uploadImage(event.dataTransfer.files[i]);
+ }
+ });
+
+ // Helper to replace editor content
+ function replaceContent(search, replace) {
+ let text = cm.getValue();
+ let cursor = cm.listSelections();
+ cm.setValue(text.replace(search, replace));
+ cm.setSelections(cursor);
+ }
+
+ // Helper to replace the start of the line
+ function replaceLineStart(newStart) {
+ let cursor = cm.getCursor();
+ let lineContent = cm.getLine(cursor.line);
+ let lineLen = lineContent.length;
+ let lineStart = lineContent.split(' ')[0];
+
+ // Remove symbol if already set
+ if (lineStart === newStart) {
+ lineContent = lineContent.replace(`${newStart} `, '');
+ cm.replaceRange(lineContent, {line: cursor.line, ch: 0}, {line: cursor.line, ch: lineLen});
+ cm.setCursor({line: cursor.line, ch: cursor.ch - (newStart.length + 1)});
+ return;
+ }
+
+ let alreadySymbol = /^[#>`]/.test(lineStart);
+ let posDif = 0;
+ if (alreadySymbol) {
+ posDif = newStart.length - lineStart.length;
+ lineContent = lineContent.replace(lineStart, newStart).trim();
+ } else if (newStart !== '') {
+ posDif = newStart.length + 1;
+ lineContent = newStart + ' ' + lineContent;
+ }
+ cm.replaceRange(lineContent, {line: cursor.line, ch: 0}, {line: cursor.line, ch: lineLen});
+ cm.setCursor({line: cursor.line, ch: cursor.ch + posDif});
+ }
+
+ function wrapLine(start, end) {
+ let cursor = cm.getCursor();
+ let lineContent = cm.getLine(cursor.line);
+ let lineLen = lineContent.length;
+ let newLineContent = lineContent;
+
+ if (lineContent.indexOf(start) === 0 && lineContent.slice(-end.length) === end) {
+ newLineContent = lineContent.slice(start.length, lineContent.length - end.length);
+ } else {
+ newLineContent = `${start}${lineContent}${end}`;
+ }
+
+ cm.replaceRange(newLineContent, {line: cursor.line, ch: 0}, {line: cursor.line, ch: lineLen});
+ cm.setCursor({line: cursor.line, ch: cursor.ch + (newLineContent.length - lineLen)});
+ }
+
+ function wrapSelection(start, end) {
+ let selection = cm.getSelection();
+ if (selection === '') return wrapLine(start, end);
+ let newSelection = selection;
+ let frontDiff = 0;
+ let endDiff = 0;
+
+ if (selection.indexOf(start) === 0 && selection.slice(-end.length) === end) {
+ newSelection = selection.slice(start.length, selection.length - end.length);
+ endDiff = -(end.length + start.length);
+ } else {
+ newSelection = `${start}${selection}${end}`;
+ endDiff = start.length + end.length;
+ }
+
+ let selections = cm.listSelections()[0];
+ cm.replaceSelection(newSelection);
+ let headFirst = selections.head.ch <= selections.anchor.ch;
+ selections.head.ch += headFirst ? frontDiff : endDiff;
+ selections.anchor.ch += headFirst ? endDiff : frontDiff;
+ cm.setSelections([selections]);
+ }
+
+ // Handle image upload and add image into markdown content
+ function uploadImage(file) {
+ if (file === null || file.type.indexOf('image') !== 0) return;
+ let ext = 'png';
+
+ if (file.name) {
+ let fileNameMatches = file.name.match(/\.(.+)$/);
+ if (fileNameMatches.length > 1) ext = fileNameMatches[1];
+ }
+
+ // Insert image into markdown
+ let id = "image-" + Math.random().toString(16).slice(2);
+ let placeholderImage = window.baseUrl(`/loading.gif#upload${id}`);
+ let selectedText = cm.getSelection();
+ let placeHolderText = ``;
+ cm.replaceSelection(placeHolderText);
+
+ let remoteFilename = "image-" + Date.now() + "." + ext;
+ let formData = new FormData();
+ formData.append('file', file, remoteFilename);
+
+ window.$http.post('/images/gallery/upload', formData).then(resp => {
+ replaceContent(placeholderImage, resp.data.thumbs.display);
+ }).catch(err => {
+ events.emit('error', trans('errors.image_upload_error'));
+ replaceContent(placeHolderText, selectedText);
+ console.log(err);
+ });
+ }
+
+ // Show the popup link selector and insert a link when finished
+ function showLinkSelector() {
+ let cursorPos = cm.getCursor('from');
+ window.showEntityLinkSelector(entity => {
+ let selectedText = cm.getSelection() || entity.name;
+ let newText = `[${selectedText}](${entity.link})`;
+ cm.focus();
+ cm.replaceSelection(newText);
+ cm.setCursor(cursorPos.line, cursorPos.ch + newText.length);
+ });
+ }