From 56bcdff89081eb7f7fa504e20786ea645525d97b Mon Sep 17 00:00:00 2001 From: yflory Date: Thu, 18 May 2017 12:16:26 +0200 Subject: [PATCH] Add a preview of the markdown content in the code editor --- www/code/inner.html | 75 +++++++++++++++++------- www/code/main.js | 49 ++++++++++++++-- www/common/common-codemirror.js | 8 ++- www/drive/main.js | 5 +- www/slide/main.js | 11 ++++ www/slide/slide.js | 101 +++----------------------------- 6 files changed, 123 insertions(+), 126 deletions(-) diff --git a/www/code/inner.html b/www/code/inner.html index cec96bbac..8dd02548a 100644 --- a/www/code/inner.html +++ b/www/code/inner.html @@ -32,32 +32,63 @@
- +
+ +
+
diff --git a/www/code/main.js b/www/code/main.js index 85cd26db3..04e20141a 100644 --- a/www/code/main.js +++ b/www/code/main.js @@ -8,7 +8,9 @@ define([ '/bower_components/chainpad-json-validator/json-ot.js', '/common/cryptpad-common.js', '/common/cryptget.js', -], function ($, Crypto, Realtime, TextPatcher, Toolbar, JSONSortify, JsonOT, Cryptpad, Cryptget) { + '/common/diffMarked.js', +], function ($, Crypto, Realtime, TextPatcher, Toolbar, JSONSortify, JsonOT, Cryptpad, + Cryptget, DiffMd) { var Messages = Cryptpad.Messages; var module = window.APP = { @@ -25,6 +27,18 @@ define([ var toolbar; var editor; + var $iframe = $('#pad-iframe').contents(); + var $preview = $iframe.find('#preview'); + $preview.click(function (e) { + if (!e.target) { return; } + var $t = $(e.target); + if ($t.is('a') || $t.parents('a').length) { + e.preventDefault(); + var $a = $t.is('a') ? $t : $t.parents('a').first(); + var href = $a.attr('href'); + window.open(href); + } + }); var secret = Cryptpad.getSecrets(); var readOnly = secret.keys && !secret.keys.editKeyStr; @@ -102,6 +116,8 @@ define([ editor.save(); + DiffMd.apply(DiffMd.render(editor.getValue()), $preview); + var textValue = canonicalize(CodeMirror.$textarea.val()); var shjson = stringifyInner(textValue); @@ -112,7 +128,15 @@ define([ } }; - + var onModeChanged = function (mode) { + if (mode === "markdown") { + APP.$previewButton.show(); + $preview.show(); + return; + } + APP.$previewButton.hide(); + $preview.hide(); + }; config.onInit = function (info) { UserList = Cryptpad.createUserList(info, config.onLocal, Cryptget, Cryptpad); @@ -193,8 +217,19 @@ define([ var $forgetPad = Cryptpad.createButton('forget', true, {}, forgetCb); $rightside.append($forgetPad); + var $previewButton = APP.$previewButton = Cryptpad.createButton(null, true); + $previewButton.removeClass('fa-question').addClass('fa-eye'); + $previewButton.attr('title', 'TODO Preview'); //TODO + $previewButton.click(function () { + if (CodeMirror.highlightMode !== 'markdown') { + return void $preview.hide(); + } + $preview.toggle(); + }); + $rightside.append($previewButton); + if (!readOnly) { - CodeMirror.configureLanguage(CodeMirror.configureTheme); + CodeMirror.configureLanguage(CodeMirror.configureTheme, onModeChanged); } else { CodeMirror.configureTheme(); @@ -231,12 +266,12 @@ define([ newDoc = hjson.content; if (hjson.highlightMode) { - CodeMirror.setMode(hjson.highlightMode); + CodeMirror.setMode(hjson.highlightMode, onModeChanged); } } if (!CodeMirror.highlightMode) { - CodeMirror.setMode('javascript'); + CodeMirror.setMode('markdown', onModeChanged); console.log("%s => %s", CodeMirror.highlightMode, CodeMirror.$language.val()); } @@ -274,9 +309,11 @@ define([ var hjson = JSON.parse(shjson); var remoteDoc = hjson.content; + DiffMd.apply(DiffMd.render(remoteDoc), $preview); + var highlightMode = hjson.highlightMode; if (highlightMode && highlightMode !== module.highlightMode) { - CodeMirror.setMode(highlightMode); + CodeMirror.setMode(highlightMode, onModeChanged); } CodeMirror.setValueAndCursor(oldDoc, remoteDoc, TextPatcher); diff --git a/www/common/common-codemirror.js b/www/common/common-codemirror.js index bec2b98e4..4bc38cc46 100644 --- a/www/common/common-codemirror.js +++ b/www/common/common-codemirror.js @@ -46,10 +46,11 @@ define([ }); editor.setValue(Messages.codeInitialState); - var setMode = exp.setMode = function (mode) { + var setMode = exp.setMode = function (mode, cb) { exp.highlightMode = mode; if (mode === 'text') { editor.setOption('mode', 'text'); + if (cb) { cb('text'); } return; } CMeditor.autoLoadMode(editor, mode); @@ -58,6 +59,7 @@ define([ var name = exp.$language.find('a[data-value="' + mode + '"]').text() || 'Mode'; exp.$language.setValue(name); } + if(cb) { cb(mode); } }; var setTheme = exp.setTheme = (function () { @@ -131,7 +133,7 @@ define([ return text.trim(); }; - exp.configureLanguage = function (cb) { + exp.configureLanguage = function (cb, onModeChanged) { var options = []; Modes.list.forEach(function (l) { options.push({ @@ -151,7 +153,7 @@ define([ }; var $block = exp.$language = Cryptpad.createDropdown(dropdownConfig); $block.find('a').click(function () { - setMode($(this).attr('data-value'), $block); + setMode($(this).attr('data-value'), onModeChanged); onLocal(); }); diff --git a/www/drive/main.js b/www/drive/main.js index 114c9d3d5..a85c86e47 100644 --- a/www/drive/main.js +++ b/www/drive/main.js @@ -2357,9 +2357,12 @@ define([ e.stopPropagation(); var path = $(this).data('path'); var onCreated = function (err, info) { - if (err && err === E_OVER_LIMIT) { + if (err === E_OVER_LIMIT) { return void Cryptpad.alert(Messages.pinLimitDrive, null, true); } + if (err) { + return void console.error("Unable to create the file", err); + } module.newFolder = info.newPath; refresh(); }; diff --git a/www/slide/main.js b/www/slide/main.js index c8463e5a6..6f04f96d0 100644 --- a/www/slide/main.js +++ b/www/slide/main.js @@ -79,6 +79,17 @@ define([ var $print = $pad.contents().find('#print'); var slideOptions = {}; + $content.click(function (e) { + if (!e.target) { return; } + var $t = $(e.target); + if ($t.is('a') || $t.parents('a').length) { + e.preventDefault(); + var $a = $t.is('a') ? $t : $t.parents('a').first(); + var href = $a.attr('href'); + window.open(href); + } + }); + Slide.setModal(APP, $modal, $content, $pad, ifrw, slideOptions, initialState); var enterPresentationMode = function (shouldLog) { diff --git a/www/slide/slide.js b/www/slide/slide.js index c7a8adf5e..108a8848a 100644 --- a/www/slide/slide.js +++ b/www/slide/slide.js @@ -1,15 +1,11 @@ define([ 'jquery', - '/bower_components/marked/marked.min.js', - '/bower_components/diff-dom/diffDOM.js' -],function ($, Marked) { - var DiffDOM = window.diffDOM; - - var renderer = new Marked.Renderer(); - + '/common/diffMarked.js', +],function ($, DiffMd) { + // Tasks list var checkedTaskItemPtn = /^\s*\[x\]\s*/; var uncheckedTaskItemPtn = /^\s*\[ \]\s*/; - renderer.listitem = function (text) { + DiffMd.renderer.listitem = function (text) { var isCheckedTaskItem = checkedTaskItemPtn.test(text); var isUncheckedTaskItem = uncheckedTaskItemPtn.test(text); if (isCheckedTaskItem) { @@ -24,10 +20,6 @@ define([ return '' + text + '\n'; }; - Marked.setOptions({ - renderer: renderer - }); - var Slide = { index: 0, lastIndex: 0, @@ -63,78 +55,6 @@ define([ } }; - var forbiddenTags = Slide.forbiddenTags = [ - 'SCRIPT', - 'IFRAME', - 'OBJECT', - 'APPLET', - 'VIDEO', - 'AUDIO', - ]; - var unsafeTag = function (info) { - if (['addAttribute', 'modifyAttribute'].indexOf(info.diff.action) !== -1) { - if (/^on/.test(info.diff.name)) { - console.log("Rejecting forbidden element attribute with name", info.diff.name); - return true; - } - } - if (['addElement', 'replaceElement'].indexOf(info.diff.action) !== -1) { - var msg = "Rejecting forbidden tag of type (%s)"; - if (info.diff.element && forbiddenTags.indexOf(info.diff.element.nodeName) !== -1) { - console.log(msg, info.diff.element.nodeName); - return true; - } else if (info.diff.newValue && forbiddenTags.indexOf(info.diff.newValue.nodeName) !== -1) { - console.log("Replacing restricted element type (%s) with PRE", info.diff.newValue.nodeName); - info.diff.newValue.nodeName = 'PRE'; - } - } - }; - - var domFromHTML = Slide.domFromHTML = function (html) { - return new DOMParser().parseFromString(html, "text/html"); - }; - - var DD = new DiffDOM({ - preDiffApply: function (info) { - if (unsafeTag(info)) { return true; } - } - }); - - var makeDiff = function (A, B) { - var Err; - var Els = [A, B].map(function (frag) { - if (typeof(frag) === 'object') { - if (!frag || (frag && !frag.body)) { - Err = "No body"; - return; - } - var els = frag.body.querySelectorAll('#content'); - if (els.length) { - return els[0]; - } - } - Err = 'No candidate found'; - }); - if (Err) { return Err; } - var patch = DD.diff(Els[0], Els[1]); - return patch; - }; - - var slice = function (coll) { - return Array.prototype.slice.call(coll); - }; - - /* remove listeners from the DOM */ - var removeListeners = function (root) { - slice(root.attributes).map(function (attr) { - if (/^on/.test(attr.name)) { - root.attributes.removeNamedItem(attr.name); - } - }); - // all the way down - slice(root.children).forEach(removeListeners); - }; - var updateFontSize = Slide.updateFontSize = function() { // 20vh // 20 * 16 / 9vw @@ -157,17 +77,10 @@ define([ if (typeof(Slide.content) !== 'string') { return; } var c = Slide.content; - var m = ''+Marked(c).replace(separatorReg, '')+''; + var m = ''+DiffMd.render(c).replace(separatorReg, '')+''; - var Dom = domFromHTML('
' + m + '
'); - removeListeners(Dom.body); - var patch = makeDiff(domFromHTML($content[0].outerHTML), Dom); + DiffMd.apply(m, $content); - if (typeof(patch) === 'string') { - $content.html(m); - } else { - DD.apply($content[0], patch); - } var length = getNumberOfSlides(); $modal.find('style.slideStyle').remove(); if (options.style && Slide.shown) { @@ -185,7 +98,7 @@ define([ } }); $content.removeClass('transition'); - if (options.transition) { + if (options.transition || typeof(options.transition) === "undefined") { $content.addClass('transition'); } //$content.find('.' + slideClass).hide();