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 %>