X-Git-Url: http://source.bookstackapp.com/bookstack/blobdiff_plain/2385a6c29b7b9c03123def135d3c6e95febaf9dd..refs/pull/422/head:/resources/assets/js/directives.js diff --git a/resources/assets/js/directives.js b/resources/assets/js/directives.js index 10458e753..9a38add9a 100644 --- a/resources/assets/js/directives.js +++ b/resources/assets/js/directives.js @@ -1,8 +1,10 @@ "use strict"; -import DropZone from "dropzone"; -import markdown from "marked"; +const DropZone = require("dropzone"); +const MarkdownIt = require("markdown-it"); +const mdTasksLists = require('markdown-it-task-lists'); +const code = require('./code'); -export default function (ngApp, events) { +module.exports = function (ngApp, events) { /** * Common tab controls using simple jQuery functions. @@ -214,18 +216,8 @@ export default function (ngApp, events) { } }]); - let renderer = new markdown.Renderer(); - // Custom markdown checkbox list item - // Attribution: https://github.com/chjj/marked/issues/107#issuecomment-44542001 - renderer.listitem = function(text) { - if (/^\s*\[[x ]\]\s*/.test(text)) { - text = text - .replace(/^\s*\[ \]\s*/, '') - .replace(/^\s*\[x\]\s*/, ''); - return `
  • ${text}
  • `; - } - return `
  • ${text}
  • `; - }; + const md = new MarkdownIt({html: true}); + md.use(mdTasksLists, {label: true}); /** * Markdown input @@ -242,22 +234,44 @@ export default function (ngApp, events) { // Set initial model content element = element.find('textarea').first(); - let content = element.val(); - scope.mdModel = content; - scope.mdChange(markdown(content, {renderer: renderer})); - element.on('change input', (event) => { - content = element.val(); + // Codemirror Setup + let cm = code.markdownEditor(element[0]); + cm.on('change', (instance, changeObj) => { + update(instance); + }); + + 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); + }); + + function update(instance) { + let content = instance.getValue(); + element.val(content); $timeout(() => { scope.mdModel = content; - scope.mdChange(markdown(content, {renderer: renderer})); + scope.mdChange(md.render(content)); }); - }); + } + update(cm); scope.$on('markdown-update', (event, value) => { + cm.setValue(value); element.val(value); scope.mdModel = value; - scope.mdChange(markdown(value)); + scope.mdChange(md.render(value)); }); } @@ -268,7 +282,7 @@ export default function (ngApp, events) { * Markdown Editor * Handles all functionality of the markdown editor. */ - ngApp.directive('markdownEditor', ['$timeout', function ($timeout) { + ngApp.directive('markdownEditor', ['$timeout', '$rootScope', function ($timeout, $rootScope) { return { restrict: 'A', link: function (scope, element, attrs) { @@ -291,34 +305,15 @@ export default function (ngApp, events) { currentCaretPos = $input[0].selectionStart; }); - // Scroll sync - let inputScrollHeight, - inputHeight, - displayScrollHeight, - displayHeight; - - function setScrollHeights() { - inputScrollHeight = $input[0].scrollHeight; - inputHeight = $input.height(); - displayScrollHeight = $display[0].scrollHeight; - displayHeight = $display.height(); - } - - setTimeout(() => { - setScrollHeights(); - }, 200); - window.addEventListener('resize', setScrollHeights); - let scrollDebounceTime = 800; - let lastScroll = 0; - $input.on('scroll', event => { - let now = Date.now(); - if (now - lastScroll > scrollDebounceTime) { - setScrollHeights() + // Handle scroll sync event from editor scroll + $rootScope.$on('markdown-scroll', (event, lineCount) => { + let elems = $display[0].children[0].children; + if (elems.length > lineCount) { + let topElem = (lineCount === -1) ? elems[elems.length-1] : elems[lineCount]; + $display.animate({ + scrollTop: topElem.offsetTop + }, {queue: false, duration: 200, easing: 'linear'}); } - let scrollPercent = ($input.scrollTop() / (inputScrollHeight - inputHeight)); - let displayScrollY = (displayScrollHeight - displayHeight) * scrollPercent; - $display.scrollTop(displayScrollY); - lastScroll = now; }); // Editor key-presses @@ -404,7 +399,7 @@ export default function (ngApp, events) { // Handle image upload and add image into markdown content function uploadImage(file) { - if (file.type.indexOf('image') !== 0) return; + if (file === null || !file.type.indexOf('image') !== 0) return; let formData = new FormData(); let ext = 'png'; let xhr = new XMLHttpRequest();