Skip to content

Instantly share code, notes, and snippets.

@johnlane
Last active September 14, 2020 14:56
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save johnlane/bdc069ca8c05c95b1797249604c757e3 to your computer and use it in GitHub Desktop.
Save johnlane/bdc069ca8c05c95b1797249604c757e3 to your computer and use it in GitHub Desktop.
An integrated Markdown WYSIWYG editor
$(document).on('ready page:load ajaxComplete', function() {
$('textarea.wysiwyg').each(function() {
var textarea = $(this)
textarea.hide().uniqueId();
var textarea_id = textarea[0].id;
var textarea_class = textarea[0].className;
var wysiwyg_id = "wysiwyg_" + textarea_id;
var wysiwyg_class = textarea_class + " wysiwyg";
if (document.getElementById(wysiwyg_id)) return;
textarea.after("<div id='"+wysiwyg_id+"' class='"+textarea_class+"'></div>");
var wysiwyg = $('#'+wysiwyg_id);
wysiwyg.hallo({
plugins: {
'halloformat': {},
'halloheadings': {},
'hallolists': {},
'halloreundo': {}
},
toolbar: 'halloToolbarFixed'
});
var markdownize = function(content) {
var html = content.split("\n").map($.trim).filter(function(line) {
return line != "";
}).join("\n");
return toMarkdown(html);
};
var converter = new Showdown.converter();
var htmlize = function(content) {
return converter.makeHtml(content);
};
// Update text area with given HTML content
var update_textarea = function(content) {
var markdown = markdownize(content);
if (textarea.val() != markdown) {
textarea.val(markdown);
}
};
// Update WYSIWYG HTML content with given Markdown
var update_wysiwyg = function(content) {
if (markdownize(wysiwyg.html()) != content) {
wysiwyg.html(htmlize(content));
}
};
// Update text area if the WYSIWYG changes
wysiwyg.bind('hallomodified', function(event, data) {
update_textarea(data.content);
});
// Update WYSIWYG if the text area canges
textarea.bind('input', function() {
update_wysiwyg(this.value);
});
// Seed WYSIWYG from Markdown
update_wysiwyg(textarea.val());
});
});

Supplementary information for my answer to the Stack Overflow question about an Integrated Markdown WYSIWYG text editor.

It’s all implemented by a single wysiwyg.js file, plus its three dependencies to-markdown.js, showdown.js, and hallo.js. Hallo itself depeds on jQueryUI and Rangy - these dependencies can be satisfied with the jquery-ui-rails and rangy-rails gems.

I place the four files in a new app/assets/javascripts/wysiwyg directory.

$ ls app/assets/javascripts/wysiwyg
hallo.js  showdown.js  to-markdown.js  wysiwyg.js

Just add to the Javascript assets:

//= require jquery-ui
//= require rangy-core
//= require_tree ./wysiwyg

The below is a rails view partial, a form containing a wysiwyg text input field:

<div id=item-cell-edit>
  <p><%= operation.model.title %></p>

  <%= simple_form_for operation.contract, remote: true do |f| %>
  <%= f.input :title %>
  <%= f.input :description, as: :text, input_html: {class: 'wysiwyg'} %>

  <%= f.submit %>
<% end %>

(if that looks rails-wierd, take a look at trailblazer)

The [wysisyg.js] is triggered when the page is ready. It selects each <textarea> on the page with a CSS class id of wysiwyg, appends a new <div> element after it and then runs hallo on the <div> (hallo populates the <div>). So, all you should need in your code is:

<textarea class='wysiwyg'>
  ...
</textarea>

(plus, of course loading the Javascript).

An optional div.wysiwyg CSS class can be used to customise styling around the WYSIWYG <div>. For example, the following is useful for Bootstrap:

div.wysiwyg {
  height:auto !important;
  min-height: 34px;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment