Skip to content

Commit

Permalink
Refactor editor.js with Object-Oriented Principles (#9104)
Browse files Browse the repository at this point in the history
* 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>
  • Loading branch information
noi5e and Sagarpreet authored Feb 3, 2021
1 parent 56ef498 commit 8a98a57
Show file tree
Hide file tree
Showing 8 changed files with 116 additions and 113 deletions.
177 changes: 86 additions & 91 deletions app/assets/javascripts/editor.js
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -23,117 +26,109 @@ $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'])){
$E.textarea.val($E.templates[template])
}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,
Expand All @@ -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
Expand Down
10 changes: 3 additions & 7 deletions app/assets/javascripts/editorToolbar.js
Original file line number Diff line number Diff line change
Expand Up @@ -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('<a href="'+data.result.url.split('?')[0]+'"><i class="fa fa-file"></i> ','</a>', {'newline': true, 'fallback': data.result['filename'].replace(/[()]/g , "-")}) // on its own line; see /app/assets/js/editor.js
$E.wrap('<a href="'+data.result.url.split('?')[0]+'"><i class="fa fa-file"></i> ', '</a>', 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) {
// $('<p/>').text(file.name).appendTo(document.body);
//});
},
fileuploadfail: function(e, data) {
console.log(e);
Expand Down
2 changes: 1 addition & 1 deletion app/assets/javascripts/post.js
Original file line number Diff line number Diff line change
Expand Up @@ -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') })
Expand Down
2 changes: 1 addition & 1 deletion app/views/editor/_editor.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,6 @@

<script>
jQuery(document).ready(function() {
$E.initialize();
$E = new Editor();
})
</script>
7 changes: 6 additions & 1 deletion app/views/notes/_comments.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@
<%= translation('layout._header.login.login_title') %> </a> to comment.
</p>
<% end %>

</div>
</div>
<script>
$(function() {
// create an instance of the editor
$E = new Editor();
});
</script>
1 change: 0 additions & 1 deletion app/views/notes/show.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,6 @@
$(this).remove();
});
$(function() {
$E.initialize();
$("img").lazyload();
});
</script>
12 changes: 8 additions & 4 deletions app/views/questions/show.html.erb
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
<%= javascript_include_tag('comment_expand') %>
<%= javascript_include_tag('notes') %>
<%= javascript_include_tag('textbox_expand') %>
<script> var comments_length = <%= @node.comments.length %>; $(function(){
$E.initialize();
$("img").lazyload();
});</script>
<script>
var comments_length = <%= @node.comments.length %>;
$(function(){
$("img").lazyload();
// create an instance of the editor
$E = new Editor();
});
</script>
<%= javascript_include_tag('question') %>
<div class="col-lg-9 note-show question-show">

Expand Down
18 changes: 11 additions & 7 deletions app/views/wiki/show.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -63,14 +63,18 @@
<% end %>

<script>
(function(){
$("img").lazyload();
$(function() {
$("img").lazyload();

<% if @fancy %>
$('#content img').addClass('img-rounded');
<% end %>
<% if @fancy %>
$('#content img').addClass('img-rounded');
<% end %>
setupWiki(<%= @node.id %>, "<%= @node.title %>", <%= params[:raw] == 'true' %>, <%= current_user.nil? != true %>);
<% if controller.action_name == 'comments' %>
// create an instance of the editor
$E = new Editor();
<% end %>

})()
setupWiki(<%= @node.id %>, "<%= @node.title %>", <%= params[:raw] == 'true' %>, <%= current_user.nil? != true %>);
});
</script>

0 comments on commit 8a98a57

Please sign in to comment.