A WMD markdown editor variant that works

by Stephen M. Redd 2. July 2010 15:46

I'm a big fan of markdown. If you've used Stack Overflow, then you are probably very familiar with markdown via their excellent variation of the WMD markdown editor. Markdown is popular in its way. A number of different web based content systems out there have adopted it, and there are plug-ins for a wide range of desktop utilities and development tools too.

But if you are just wanting a javascript based WYSIWYG style editor for use in your own web applications, you are kinda screwed; there just aren't a lot of markdown editors out there. I know of only two working markdown editors of any merit that aren't tied to some specific application; MarkItUp! by Jay Salvat and WMD by John Fraser.

WMD is fantastic, but John Fraser vanished from the net shortly after making WMD's initial code open source. His initial release was an obfuscated form, which makes it hard to deal with. He had plans to go ahead with a more developer friendly and advanced version of his editor, but it never happened. I have no idea what might have happened to John, but I suspect it was not good (active programmers don't typically disappear off the net entirely for years at a time without a trace). I do hope John is well, but the internet is a much poorer place without him I can tell you that!

MarkItUp! is also a fine editor in its own way, but it was not designed with Markdown as the primary target and it isn't exactly a WYSIWYG editor. Technically, MarkItUp! is just a markup editor with some macros on the toolbar. It does have support for the markdown syntax, and it does a decent job as a markup editor even with markdown's syntax. But using it to author content in markdown isn't very approachable for a public facing application. The editor is just a tad too "bare-metal".

WMD on the other hand is smoother and more viable. It is also more of a markup editor than a real WYSIWYG, but it does has some really subtle, but important features that help make authoring markdown content enjoyable. But John's baseline version has several pretty major problems, and since the source is obfuscated, fixing it up and customizing it is next to impossible.

Fortunately Dana Robinson and some others at Stack Overflow managed to de-obfuscate the original WMD source code, and published their their own Stack Overflow specific version over at github. Now! That was some fine work, and I really do appreciate Dana's contribution. But the Stack Overflow version stripped out a lot of the original WMD's configuration features, and has several Stack Overflow specific tweaks besides. The end result of the SO version is that you can't really control it well. For example, I've found it nearly impossible to instantiate the SO version of WMD via JavaScript in response to a user action (like showing the editor in a pop-up for example). The SO version also doesn't deal well with multiple instances on the same page.

There are quite a few branches from the SO version floating around on github. Fortunately there IS one branch that seems to have worked out most of the major problems and has actually advanced WMD quite a ways beyond the initial SO version. Anand Chitipothu maintains the openlibrary branch of WMD.

Basically, this version is a WMD in a JQuery wrapper. Anand has added back much of the configuration stuff that the SO branch broke, tweaked a few of the behaviors (in pleasant ways), fixed up the multi-editor support, and most importantly made the editor JavaScript/JQuery instantiable.

Overall, this is an excellent variant that can be used simply in just about any web application. It even has decent documentation too!

I'm using openlibrary / wmd Master branch tagged as 2.0, but there are a couple of minor catches I've found in this variant (as it was on July 1st 2010 anyway):

First, there is a random stray undeclared variable that blows up in chrome (I didn't check other browsers for this one).

The line with the problem is:

WMDEditor.Checks = Checks;

Commenting out this line fixes the problem simply enough.

The other problem is much more complex. Basically, when putting this together, Anand made some fundamental changes to how the code is arranged, and some of the stuff in an IE specific branch hasn't been property updated with the new object model yet (the author probably isn't testing in IE 8 yet). Fixing this is rather annoying to describe, but the simplest way to do it is:

  • do a search/replace for "wmd.ieCachedRange" and replace with "WMDEditor.ieCachedRange"
  • do a search/replace for "wmd.ieRetardedClick" and replace with "WMDEditor.ieRetardedClick"

I have to say that I'm not fully up-to-speed on the innards of WMD, so my fix may not be optimal here... but it seems to work OK in my limited usages so far.

For your convenience I've made my own variation of jquery.wmd.js and available for download. All of the lines I altered end with "//SMR". I am not including the minified version, but you should make your own minified for use in production environments.

openlibrary-wmd-2.0-modified.zip (43.40 kb)

Tags: , , , ,

Filed Under: Code

Fixing markitUp! 1.1.5 - bug in IE8 when closing preview iframe

by Stephen M. Redd 27. August 2009 09:29

[UPDATE - 1/12/2010]: MarkItUp! version 1.1.6 inclues a fix for this issue (thanks Jay!)

I've been working with the wonderful markitUp! editor by Jay Salvat. Specifically, I'm using markitUp! as a markdown editor for TicketDesk and a few other apps I'm working on. I'll probably post more about using markitUp! as a markdown editor later, but for now I wanted to address a specific bug that markitUp! exhibits in IE8. 

By default, markitUp! uses an iframe element for a preview window. 

In IE 8, closing the preview iframe will cause IE 8 to try to close the entire hosting window or tab. IE 8 will prompt the user before it does this, but if you click yes when prompted it will indeed kill the window/tab... which is not good. 

After some digging, I've located the problem... 

 

Here is the relevant code with the part that is called when closing the preview window in bold:

// open preview window
// open preview window
function preview() {
	if (!previewWindow || previewWindow.closed) {
	    if (options.previewInWindow) {
		previewWindow = window.open('', 'preview', options.previewInWindow);
	    } else {
		iFrame = $('');
		if (options.previewPosition == 'after') {
		    iFrame.insertAfter(footer);
		} else {
		    iFrame.insertBefore(header);
		}
		previewWindow = iFrame[iFrame.length - 1].contentWindow || frame[iFrame.length - 1];
	    }
	} else if (altKey === true) {
  	    if (iFrame) {
 		iFrame.remove();
 	    }
 	    previewWindow.close();
 	    previewWindow = iFrame = false;
	}
	if (!options.previewAutoRefresh) {
	    refreshPreview();
	}
}

 

What is supposed to happen is that the code removes the iframe element, then calls the close method on previewWindow variable. That variable would normally have a reference to the content window within the iframe (you can see where that is set in blue in the code excerpt above). So calling close on the variable would normally just try to close that sub-window... or maybe it would do nothing at all because the iframe containing the sub-window would have already been removed. The behavior is internal to the browser and I suspect that specific mechanics are probably a little different from one browser to another. But either way, this works fine on all the browsers I've tested with except IE 8.

With IE8, this code appears to invoke the the close() call on the containing window instead (which is your page's main window in most cases). If you make the close call before the iframe is removed, IE8 will behave like the other browsers do, but when the close call happens after the iframe is removed IE 8 starts asking you if you want to close the whole browser window. for some reason, the contents of the previewWindow variable change after you remove the iframe.

Not a problem! my solution was to simply alter the code to only call the close method when there isn't an iframe being used. That way, close is called for cases where you are using the pop-up window, but doesn't get called when you are using an iframe.

// open preview window
 function preview() {
 	if (!previewWindow || previewWindow.closed) {
 	    if (options.previewInWindow) {
 		previewWindow = window.open('', 'preview', options.previewInWindow);
 	    } else {
 		iFrame = $('');
 		if (options.previewPosition == 'after') {
 		    iFrame.insertAfter(footer);
 		} else {
 		    iFrame.insertBefore(header);
 		}
 		previewWindow = iFrame[iFrame.length - 1].contentWindow || frame[iFrame.length - 1];
 	    }
 	} else if (altKey === true) {
     	    if (iFrame) {
   		iFrame.remove();
   	    }   	    else {
   		//SMR - else block added here to prevent this call when preview is in iframe
   		//      IE8 incorrectly tries to close the hosting window if you call it when using iframe
   		previewWindow.close();
   	    }
     	    previewWindow = iFrame = false;
 	}
 	if (!options.previewAutoRefresh) {
 	    refreshPreview();
 	}
}

 

 

 

This seems to fix the problem in IE 8, while not breaking anything in the other browsers I'm testing with (chrome, firefox, etc). I could have just moved the close call so it happened before the iFrame gets removed (which also seems to work), but I'm a little concerned that closing the window before removing the iframe might have unexpected results in browsers I'm not testing with... but it would probably be fine either way.

If you want, you can download my modified versions of the markitup! source. I have included a standard and minified one both.

Please note that this version also contains my own killPreview() function. By default markitUp! has only one toolbar button for the preview. If you click it, the preview opens and if you ALT + Click it the preview closes. But in my own implementations I prefer to have a separate toolbar button for closing the preview... users don't magically "know" about the ALT+Click trick and I get tired of people reporting "I can't close the preview window" as a bug in the apps that use markitUp!.

Tags: , , ,

Filed Under: Code

Powered by BlogEngine.NET 1.6.1.6
Theme by Stephen M. Redd