Making custom markup editors with markItUp and jqModal

While I’m a big fan of Mootools I work on a few projects using jQuery, and have recently been working on a project that needed a custom markup editor. I’m a big fan of the markItUp editor. Its a flexible and easily extensible editor, that affords a great deal of features in a small package. While markItUp is not a fully featured WYISWYG editor, I often prefer it because I favor markup syntaxes like Markdown or Wiki to full featured HTML editors. This makes markItUp an even more ideal solution as it excels in these use cases. markItUp comes with built in ‘magicMarkup’ which allows you to create a series of prompt() calls to create more complex elements. However, my requirements were that I needed to create custom HTML dialogs for certain markup elements.

markItUp editor API

markItUp offers the ability to create a markup set with an array of objects that define each button and syntax element. Each button object allows you to specify a replaceWith option which can either be a string or a function. The function option is far more interesting as it gives us the ability to capture the button trigger and inject content wholesale into the editor. A simple example of this would be.

Show Plain Text
  1.  
  2. var markItUpSettings = {
  3.     markupSet: [
  4.         {name: 'Test', replaceWith: function (markItUp) {
  5.             return 'This is a button that injects text!';
  6.         }}
  7.     ]
  8. };
  9.  

Clicking this button will replace the selection with This is a button that injects text!. Not what we are going for, but getting close.

Custom UI elements with modal dialogs

In order for us to create custom UI dialogs, we need to block the rest of the interface while the dialog has been opened. Modal dialogs are ideal for this, and jQModal is a favorite of mine. We can use jqModal to create a blocking UI element, force user interaction and then insert the results of the user interaction into our editor, pretty simple once you put all the pieces together.

Block and intercept.

First thing we will need to do is create an Object/Function to respond to the button press, block the UI, and prepare a form for interaction. We will need to trigger our modal dialog from the replaceWith callback. Once we have the modal dialog open, attached events can collect data and pass it back to the markItUp instance held in the closure.

Show Plain Text
  1. var markItUpSettings = {
  2.     onShiftEnter: {keepDefault:false, replaceWith:'\n\n'},
  3.     markupSet: [
  4.         {name: 'Link', className: 'link', key: 'L', replaceWith: function (markItUp) {
  5.             MarkupHelper.showDialog(markItUp);
  6.             return false;
  7.         }}
  8.     ]
  9. };
  10.  
  11. var MarkupHelper = {
  12.     eventsAttached: false,
  13.  
  14.     showDialog: function (markItUp) {
  15.         this.bindEvents(markItUp);
  16.         $('#dialog').jqm({overlay: 40, modal: true}).jqmShow();
  17.         $('#link-text').val('');
  18.         $('#link-url').val('');
  19.     },
  20.  
  21.     bindEvents: function (markItUp) {
  22.         if (this.eventsAttached) {
  23.             return;
  24.         }
  25.         this.eventsAttached = true;
  26.  
  27.         $('#link-submit').bind('click', function (event) {
  28.             event.stopPropagation();
  29.             event.preventDefault();
  30.             var text = $('#link-text').val();
  31.             var url = $('#link-url').val();
  32.             var linkElement = '[' + url + ' ' + text + ']';
  33.             $(markItUp.textarea).trigger('insertion', [{replaceWith: linkElement}]);
  34.             $('#dialog').jqmHide();
  35.         });
  36.  
  37.         $('#link-cancel').bind('click', function (event) {
  38.             event.preventDefault();
  39.             $('#dialog').jqmHide();
  40.         });
  41.     }
  42. };

insertion event.

Inside the $('#link-submit') click event, you can see an insertion event triggered. This insertion event does most of the magic. Triggering the insertion event and passing in an object that looks like a button definition tricks the editor into thinking a toolbar button has been pressed and that we are sending it the button definition. The ability to trigger the insertion event makes markItUp very powerful. As we can create arbitrary multi-step UI interactions to generate whatever text we may desire. It also enables us to easily extend the markItUp interface and features with custom and rich handling of markup creation.

Check here for the demo

Comments

Thanks Mark for this excellent tutorial.
markItUp! 2.0 will have “native” dialogboxes.

I think line 33 :
$(markItUp.textarea).trigger(‘insertion’, [{replaceWith: linkElement}]);

Could be replace by :
$.markItUp( {replaceWith: linkElement} );

Regards,
Jay

Jay Salvat on 22/7/09

Hmm, I recently went with using SimpleModal (also jQuery) in a recent (similar) project. Although I pieced together my own markup editor from a few different (reusable) libraries, I think I prefer jqModal. Thanks for pointing it out! Although the URL is only temporary, a publically accessible version (in case you wanted to take a look) is at http://www.sspl.org/javascript/text_selection.html (here’s hoping that doesn’t autolink so I don’t have to worry about 404’s later). I really should move that stuff to a lab page on my blog site. The use of jquery.hotkeys was extremely helpful.

Brendon Kozlowski on 22/7/09

Jay: Thanks, and thank you for the great plugin! I’m looking forward to native dialogs. The code you gave does work, but I find when I use overly terse code, and come back to it in 6 months I forget what it does. But having options is always nice too.

mark story on 23/7/09

Unfortunately not able to apply this code to IE. All the time vokus goes to the first character textarea.

Roman Koff on 26/8/10

Personally, I think MarkItUp is way too feature rich and bloathed for my needs. I found out about PhenoEdit recently ( http://babalin.com/phenoedit/ ) and have been using it as an alternative. It’s much easier to deploy, too. If you don’t need the advanced features MarkItUp allows, it’s great.

Hubert on 26/6/12

Have your say:

*
* You can use Textile markup, but be reasonable