From 765b247e648fae784098fd0741d23a623e68de23 Mon Sep 17 00:00:00 2001
From: noi5e
Date: Wed, 3 Feb 2021 15:41:45 -0800
Subject: [PATCH] Refactor editor.js with Object-Oriented Principles (#9104)
* object-oriented refactor! #9004
* create instances of new object-oriented editor #9004
* pass codeclimate: add semicolon; comment unused methods #9004
* move editor instantiation to notes/comments partial #9004
* adding const to pass codeclimate #9004
* reformat object methods #9004
* instantiate 1 editor object each on wikis, notes, and questions #9004
* rewrite to reduce cognitive complexity #9004
* trying to pass codeclimate #9004
* trying to pass codeclimate #9004
* trying to pass codeclimate #9004
* trying to pass codeclimate #9004
* trying to pass codeclimate #9004
* hopefully the last codeclimate rewrite #9004
* rewrite E.wrap's parameters to pass codeclimate #9004
* remove newlines to appease codeclimate #9004
* typo: highlighT instead of highligh #9004
* fixing typo #9004
* change const to let #9004
* assign textarea & preview to jQuery objects in constructor #9004
* change newText assignment
Co-authored-by: Sagarpreet <53554917+Sagarpreet@users.noreply.github.com>
* change uri check for null #9004
Co-authored-by: Sagarpreet <53554917+Sagarpreet@users.noreply.github.com>
Co-authored-by: Sagarpreet <53554917+Sagarpreet@users.noreply.github.com>
---
app/assets/javascripts/editor.js | 177 ++++++++++++------------
app/assets/javascripts/editorToolbar.js | 10 +-
app/assets/javascripts/post.js | 2 +-
app/views/editor/_editor.html.erb | 2 +-
app/views/notes/_comments.html.erb | 7 +-
app/views/notes/show.html.erb | 1 -
app/views/questions/show.html.erb | 12 +-
app/views/wiki/show.html.erb | 18 ++-
8 files changed, 116 insertions(+), 113 deletions(-)
diff --git a/app/assets/javascripts/editor.js b/app/assets/javascripts/editor.js
index 997f0b176c..7f8de75b3b 100644
--- a/app/assets/javascripts/editor.js
+++ b/app/assets/javascripts/editor.js
@@ -1,13 +1,16 @@
-// jQuery (document).ready function:
-
-$E = {
- initialize: function() {
- // call setState with no parameters, aka. default parameters.
- // default parameters point toward either:
- // 1. the comment form at the bottom of multi-comment wikis/questions/research notes
- // 2. the only editor form on /wiki/new and /wiki/edit
- $E.setState();
-
+class Editor {
+ // default parameters reference the IDs of:
+ // 1. main comment form in multi-comment wikis, questions, & research notes.
+ // 2. the only editor form on /wiki/new and /wiki/edit
+ constructor(textarea = "text-input", preview = "comment-preview-main", title = "title") {
+ this.textarea = $('#' + textarea);
+ this.preview = $('#' + preview);
+ this.title = $('#' + title + 'title'); // not sure why this exists? seems like $E.title is always #title
+ this.previewing = false;
+ this.previewed = false;
+ // this will get deleted in the next few PRs, so collapsing into one line to pass codeclimate
+ this.templates = { 'blog': "## The beginning\n\n## What we did\n\n## Why it matters\n\n## How can you help", 'default': "## What I want to do\n\n## My attempt and results\n\n## Questions and next steps\n\n## Why I'm interested", 'support': "## Details about the problem\n\n## A photo or screenshot of the setup", 'event': "## Event details\n\nWhen, where, what\n\n## Background\n\nWho, why", 'question': "## What I want to do or know\n\n## Background story" };
+
marked.setOptions({
gfm: true,
tables: true,
@@ -23,96 +26,97 @@ $E = {
return code;
}
});
- },
- setState: function(textarea = 'text-input', preview = 'comment-preview-main', title = 'title') {
+ }
+ setState(textarea = 'text-input', preview = 'comment-preview-main', title = 'title') {
$E.title = $('#' + title + 'title'); // not sure why this exists? seems like $E.title is always #title
$E.textarea = $('#' + textarea);
$E.textarea.bind('input propertychange', $E.save);
$E.preview = $('#' + preview);
- },
- is_editing: function() {
- return ($E.textarea[0].selectionStart == 0 && $E.textarea[0].selectionEnd == 0)
- },
- refresh: function() {
+ }
+ // code seems unused, commenting out for now.
+ // is_editing() {
+ // return ($E.textarea[0].selectionStart == 0 && $E.textarea[0].selectionEnd == 0)
+ // };
+ refresh() {
// textarea
$E.textarea = ($D.selected).find('textarea').eq(0);
$E.textarea.bind('input propertychange',$E.save);
// preview
$E.preview = ($D.selected).find('.comment-preview').eq(0);
- },
- isRichTextEditor: function(url) {
- // this RegEx matches three different cases where the legacy editor is still used:
+ }
+ isSingleFormPage(url) {
+ // this RegEx matches three different pages where only one editor form is present (instead of multiple comment forms):
// 1. /wiki/new
// 2. /wiki/{wiki name}/edit
// 3. /features/new
- const legacyEditorPath = RegExp(/\/(wiki|features)(\/[^\/]+\/edit|\/new)/);
- return !legacyEditorPath.test(url); // if we're not on one of these pages, we are using the rich-text editor.
- },
+ const singleFormPath = RegExp(/\/(wiki|features)(\/[^\/]+\/edit|\/new)/);
+ return singleFormPath.test(url);
+ }
// wraps currently selected text in textarea with strings a and b
- wrap: function(a, b, args) {
- // we only refresh $E's values if we are on a page using the rich-text editor (most pages).
- // the legacy editor pages only have one editor form, unlike pages with multiple comments.
- if (this.isRichTextEditor(window.location.pathname)) { this.refresh(); }
- var len = $E.textarea.val().length;
- var start = $E.textarea[0].selectionStart;
- var end = $E.textarea[0].selectionEnd;
- const fallbackParameterExists = args && args['fallback'];
- const newlineParameterExists = args && args['newline'];
- var sel = fallbackParameterExists ? args['fallback'] : $E.textarea.val().substring(start, end); // // fallback if nothing has been selected, and we're simply dealing with an insertion point
- var replace = a + sel + b;
- if (newlineParameterExists) {
- replace = replace + "\n\n";
- }
- if (newlineParameterExists && $E.textarea[0].selectionStart > 0) {
- replace = "\n" + replace;
- }
- $E.textarea.val($E.textarea.val().substring(0,start) + replace + $E.textarea.val().substring(end,len));
- },
- bold: function() {
+ wrap(a, b, newlineDesired = false, fallback) {
+ // we only refresh $E's values if we are on a page with multiple comments
+ if (!this.isSingleFormPage(window.location.pathname)) { this.refresh(); }
+
+ const selectionStart = $E.textarea[0].selectionStart;
+ const selectionEnd = $E.textarea[0].selectionEnd;
+ const selection = fallback || $E.textarea.val().substring(selectionStart, selectionEnd); // fallback if nothing has been selected, and we're simply dealing with an insertion point
+
+ let newText = newlineDesired ? a + selection + b + "\n\n" : a + selection + b; // ie. ** + selection + ** (wrapping selection in bold)
+ const selectionStartsMidText = $E.textarea[0].selectionStart > 0;
+ if (newlineDesired && selectionStartsMidText) { newText = "\n" + newText; }
+
+ const textLength = $E.textarea.val().length;
+ const textBeforeSelection = $E.textarea.val().substring(0, selectionStart);
+ const textAfterSelection = $E.textarea.val().substring(selectionEnd, textLength);
+ $E.textarea.val(textBeforeSelection + newText + textAfterSelection);
+ }
+ bold() {
$E.wrap('**','**')
- },
- italic: function() {
+ }
+ italic() {
$E.wrap('_','_')
- },
- link: function(uri) {
+ }
+ link(uri) {
uri = prompt('Enter a URL');
- if (uri === null) { uri = ""; }
+ if (!uri) { uri = ""; }
$E.wrap('[', '](' + uri + ')');
- },
- image: function(src) {
+ }
+ image(src) {
$E.wrap('\n![',']('+src+')\n')
- },
- h1: function() {
- $E.wrap('#','')
- },
- h2: function() {
+ }
+ // these header formatting functions are not used anywhere, so commenting them out for now to pass codeclimate:
+
+ // h1() {
+ // $E.wrap('#','')
+ // }
+ h2() {
$E.wrap('##','')
- },
- h3: function() {
- $E.wrap('###','')
- },
- h4: function() {
- $E.wrap('####','')
- },
- h5: function() {
- $E.wrap('#####','')
- },
- h6: function() {
- $E.wrap('######','')
- },
- h7: function() {
- $E.wrap('#######','')
- },
+ }
+ // h3() {
+ // $E.wrap('###','')
+ // }
+ // h4() {
+ // $E.wrap('####','')
+ // }
+ // h5() {
+ // $E.wrap('#####','')
+ // }
+ // h6() {
+ // $E.wrap('######','')
+ // }
+ // h7() {
+ // $E.wrap('#######','')
+ // }
// this function is dedicated to Don Blair https://github.com/donblair
- save: function() {
+ save() {
localStorage.setItem('plots:lastpost',$E.textarea.val())
localStorage.setItem('plots:lasttitle',$E.title.val())
- },
- recover: function() {
+ }
+ recover() {
$E.textarea.val(localStorage.getItem('plots:lastpost'))
$E.title.val(localStorage.getItem('plots:lasttitle'))
- },
- apply_template: function(template) {
+ }
+ apply_template(template) {
if($E.textarea.val() == ""){
$E.textarea.val($E.templates[template])
}else if(($E.textarea.val() == $E.templates['event']) || ($E.textarea.val() == $E.templates['default']) || ($E.textarea.val() == $E.templates['support'])){
@@ -120,20 +124,11 @@ $E = {
}else{
$E.textarea.val($E.textarea.val()+'\n\n'+$E.templates[template])
}
- },
- templates: {
- 'blog': "## The beginning\n\n## What we did\n\n## Why it matters\n\n## How can you help",
- 'default': "## What I want to do\n\n## My attempt and results\n\n## Questions and next steps\n\n## Why I'm interested",
- 'support': "## Details about the problem\n\n## A photo or screenshot of the setup",
- 'event': "## Event details\n\nWhen, where, what\n\n## Background\n\nWho, why",
- 'question': "## What I want to do or know\n\n## Background story"
- },
- previewing: false,
- previewed: false,
- generate_preview: function(id,text) {
+ }
+ generate_preview(id,text) {
$('#'+id)[0].innerHTML = marked(text)
- },
- toggle_preview: function() {
+ }
+ toggle_preview() {
let previewBtn;
let dropzone;
// if the element is part of a multi-comment page,
@@ -146,8 +141,8 @@ $E = {
dropzone.toggle();
this.toggleButtonPreviewMode(previewBtn);
- },
- toggleButtonPreviewMode: function (previewBtn) {
+ }
+ toggleButtonPreviewMode(previewBtn) {
let isPreviewing = previewBtn.attr('data-previewing');
// If data-previewing attribute is not present -> we are not in "preview" mode
diff --git a/app/assets/javascripts/editorToolbar.js b/app/assets/javascripts/editorToolbar.js
index 37159c3244..0a7eac1366 100644
--- a/app/assets/javascripts/editorToolbar.js
+++ b/app/assets/javascripts/editorToolbar.js
@@ -108,21 +108,17 @@ $(function() {
switch (file_type) {
case 'image':
orig_image_url = file_url + '?s=o' // size = original
- $E.wrap('[![', '](' + file_url + ')](' + orig_image_url + ')', {'newline': true, 'fallback': data.result['filename']}) // on its own line; see /app/assets/js/editor.js
+ $E.wrap('[![', '](' + file_url + ')](' + orig_image_url + ')', true, data.result['filename']);
break;
case 'csv':
- $E.wrap('[graph:' + file_url + ']', '', {'newline': true})
+ $E.wrap('[graph:' + file_url + ']', '', true);
break;
default:
- $E.wrap(' ','', {'newline': true, 'fallback': data.result['filename'].replace(/[()]/g , "-")}) // on its own line; see /app/assets/js/editor.js
+ $E.wrap(' ', '', true, data.result['filename'].replace(/[()]/g , "-")); // on its own line; see /app/assets/js/editor.js
}
// here append the image id to the wiki edit form:
if ($('#node_images').val() && $('#node_images').val().split(',').length > 1) $('#node_images').val([$('#node_images').val(),data.result.id].join(','))
else $('#node_images').val(data.result.id)
- // eventual handling of multiple files; must add "multiple" to file input and handle on server side:
- //$.each(data.result.files, function (index, file) {
- // $('').text(file.name).appendTo(document.body);
- //});
},
fileuploadfail: function(e, data) {
console.log(e);
diff --git a/app/assets/javascripts/post.js b/app/assets/javascripts/post.js
index 027a5d4925..529a094143 100644
--- a/app/assets/javascripts/post.js
+++ b/app/assets/javascripts/post.js
@@ -2,7 +2,7 @@ jQuery(document).ready(function() {
$('.datepicker').datepicker()
- $E.initialize()
+ $E = new Editor();
$('#side-fileinput').bind('focus',function(e) { $('#side-dropzone').css('border-color','#4ac') })
$('#side-fileinput').bind('focusout',function(e) { $('#side-dropzone').css('border-color','#ccc') })
diff --git a/app/views/editor/_editor.html.erb b/app/views/editor/_editor.html.erb
index 56ebe58c61..9ab628d3f8 100644
--- a/app/views/editor/_editor.html.erb
+++ b/app/views/editor/_editor.html.erb
@@ -21,6 +21,6 @@
diff --git a/app/views/notes/_comments.html.erb b/app/views/notes/_comments.html.erb
index 03c33466db..ea6267d4b2 100644
--- a/app/views/notes/_comments.html.erb
+++ b/app/views/notes/_comments.html.erb
@@ -30,6 +30,11 @@
<%= translation('layout._header.login.login_title') %> to comment.
<% end %>
-
+
\ No newline at end of file
diff --git a/app/views/notes/show.html.erb b/app/views/notes/show.html.erb
index 1256c96278..8d7d3e3c7f 100644
--- a/app/views/notes/show.html.erb
+++ b/app/views/notes/show.html.erb
@@ -147,7 +147,6 @@
$(this).remove();
});
$(function() {
- $E.initialize();
$("img").lazyload();
});
diff --git a/app/views/questions/show.html.erb b/app/views/questions/show.html.erb
index 66fe31b531..6080d0ccb5 100644
--- a/app/views/questions/show.html.erb
+++ b/app/views/questions/show.html.erb
@@ -1,10 +1,14 @@
<%= javascript_include_tag('comment_expand') %>
<%= javascript_include_tag('notes') %>
<%= javascript_include_tag('textbox_expand') %>
-
+
<%= javascript_include_tag('question') %>
diff --git a/app/views/wiki/show.html.erb b/app/views/wiki/show.html.erb
index 2ddc8a1bec..ec21dd21f8 100644
--- a/app/views/wiki/show.html.erb
+++ b/app/views/wiki/show.html.erb
@@ -63,14 +63,18 @@
<% end %>