summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.meteor/packages1
-rw-r--r--.meteor/versions2
-rwxr-xr-xclient/components/main/editor.js132
3 files changed, 129 insertions, 6 deletions
diff --git a/.meteor/packages b/.meteor/packages
index ccbff63d..875b1f98 100644
--- a/.meteor/packages
+++ b/.meteor/packages
@@ -94,3 +94,4 @@ lamhieu:unblock
meteorhacks:aggregate@1.3.0
wekan-markdown
konecty:mongo-counter
+summernote:summernote
diff --git a/.meteor/versions b/.meteor/versions
index 13400796..2ca1d706 100644
--- a/.meteor/versions
+++ b/.meteor/versions
@@ -168,6 +168,7 @@ standard-minifier-css@1.5.3
standard-minifier-js@2.4.1
staringatlights:fast-render@3.2.0
staringatlights:inject-data@2.3.0
+summernote:summernote@0.8.1
tap:i18n@1.8.2
templates:tabs@2.3.0
templating@1.3.2
@@ -175,6 +176,7 @@ templating-compiler@1.3.3
templating-runtime@1.3.2
templating-tools@1.1.2
tracker@1.2.0
+twbs:bootstrap@3.3.6
ui@1.0.13
underscore@1.0.10
url@1.2.0
diff --git a/client/components/main/editor.js b/client/components/main/editor.js
index 2824723d..400043f2 100755
--- a/client/components/main/editor.js
+++ b/client/components/main/editor.js
@@ -1,9 +1,7 @@
Template.editor.onRendered(() => {
- const $textarea = this.$('textarea');
-
- autosize($textarea);
-
- $textarea.escapeableTextComplete([
+ const textareaSelector = 'textarea';
+ const disableRicherEditor = Meteor.settings.public.NO_RICHER_EDITOR;
+ const mentions = [
// User mentions
{
match: /\B@([\w.]*)$/,
@@ -27,7 +25,129 @@ Template.editor.onRendered(() => {
},
index: 1,
},
- ]);
+ ];
+ if (!disableRicherEditor) {
+ const isSmall = Utils.isMiniScreen();
+ const toolbar = isSmall
+ ? [
+ ['font', ['bold', 'underline']],
+ ['fontsize', ['fontsize']],
+ ['color', ['color']],
+ ['table', ['table']],
+ ['view', ['fullscreen']],
+ ]
+ : [
+ ['style', ['style']],
+ ['font', ['bold', 'underline', 'clear']],
+ ['fontsize', ['fontsize']],
+ ['fontname', ['fontname']],
+ ['color', ['color']],
+ ['para', ['ul', 'ol', 'paragraph']],
+ ['table', ['table']],
+ //['insert', ['link', 'picture', 'video']], // iframe tag will be sanitized TODO if iframe[class=note-video-clip] can be added into safe list, insert video can be enabled
+ ['insert', ['link', 'picture']],
+ ['view', ['fullscreen', 'help']],
+ ];
+ const cleanPastedHTML = function(input) {
+ const badTags = [
+ 'style',
+ 'script',
+ 'applet',
+ 'embed',
+ 'noframes',
+ 'noscript',
+ 'meta',
+ 'link',
+ 'button',
+ 'form',
+ ].join('|');
+ const badPatterns = new RegExp(
+ `(?:${[
+ `<(${badTags})s*[^>][\\s\\S]*?<\\/\\1>`,
+ `<(${badTags})[^>]*?\\/>`,
+ ].join('|')})`,
+ 'gi',
+ );
+ let output = input;
+ // remove bad Tags
+ output = output.replace(badPatterns, '');
+ // remove attributes ' style="..."'
+ const badAttributes = new RegExp(
+ `(?:${[
+ 'on\\S+=([\'"]?).*?\\1',
+ 'href=([\'"]?)javascript:.*?\\2',
+ 'style=([\'"]?).*?\\3',
+ 'target=\\S+',
+ ].join('|')})`,
+ 'gi',
+ );
+ output = output.replace(badAttributes, '');
+ output = output.replace(/(<a )/gi, '$1target=_ '); // always to new target
+ return output;
+ };
+ const editor = '.editor';
+ const selectors = [
+ `.js-new-comment-form ${editor}`,
+ `.js-edit-comment ${editor}`,
+ ].join(','); // only new comment and edit comment
+ $(selectors).summernote({
+ callbacks: {
+ onInit(object) {
+ const jEditor = object && object.editor;
+ const toolbar = object && object.toolbar;
+ if (jEditor !== undefined) {
+ jEditor.find('.note-editable').escapeableTextComplete(mentions);
+ }
+ if (toolbar !== undefined) {
+ const fBtn = toolbar.find('.btn-fullscreen');
+ fBtn.on('click', function() {
+ const $this = $(this),
+ isActive = $this.hasClass('active');
+ $('.minicards').toggle(!isActive); // mini card is still showing when editor is in fullscreen mode, we hide here manually
+ });
+ }
+ },
+ onPaste() {
+ // clear up unwanted tag info when user pasted in text
+ const thisNote = $(this);
+ const updatePastedText = function(someNote) {
+ const original = someNote.summernote('code');
+ const cleaned = cleanPastedHTML(original); //this is where to call whatever clean function you want. I have mine in a different file, called CleanPastedHTML.
+ someNote.summernote('code', ''); //clear original
+ someNote.summernote('pasteHTML', cleaned); //this sets the displayed content editor to the cleaned pasted code.
+ };
+ setTimeout(function() {
+ //this kinda sucks, but if you don't do a setTimeout,
+ //the function is called before the text is really pasted.
+ updatePastedText(thisNote);
+ }, 10);
+ },
+ },
+ dialogsInBody: true,
+ disableDragAndDrop: true,
+ toolbar,
+ popover: {
+ image: [
+ [
+ 'image',
+ ['resizeFull', 'resizeHalf', 'resizeQuarter', 'resizeNone'],
+ ],
+ ['float', ['floatLeft', 'floatRight', 'floatNone']],
+ ['remove', ['removeMedia']],
+ ],
+ table: [
+ ['add', ['addRowDown', 'addRowUp', 'addColLeft', 'addColRight']],
+ ['delete', ['deleteRow', 'deleteCol', 'deleteTable']],
+ ],
+ air: [['color', ['color']], ['font', ['bold', 'underline', 'clear']]],
+ },
+ height: 200,
+ });
+ } else {
+ const $textarea = this.$(textareaSelector);
+ autosize($textarea);
+ $textarea.escapeableTextComplete(mentions);
+ }
});
import sanitizeXss from 'xss';