Tweaking tinyMCE

I'm in the process of tweaking a tinyMCE install to use a custom set of plugins and a custom skin to better fit in the Caracole CMS. This would allow me to re-use CSS and JS code I've already coded.

Fortunatly, I'm getting better and better at understanding the way tinyMCE works and the various configuration options and how to tweak them.

I also want the tinyMCE CSS rules to be integrated into the main CSS file of my app (packed and minified using CSSTidy). So I defined a custom skin in the tinyMCE setup, named 'caracole'. This way, all the CSS skinning classes will be prefixed with caracoleas I don't want to mess with the default skins.

I also copied the ui.css file from the themes/advanced/skins/default to my own css directory, renamed it to tinymce.css, replaced all .default occurences to .caracole and added this file to the list of files being processed by the main packer.

This way I can easily update any css file from the same directory, without having to dive into the tinymce subdirectories.

Unfortunatly, by doing so, tinyMCE continue to load the tiny_mce/themes/advanced/skins/caracole/ui.css file (have a look at Firebug if you don't trust me). It took me some time to figure out where that call was made from. It is actually on editor_template.js (around line 150 in dev version).

As I didn't want to load this file twice, I tried to disable this call. I could have manually patched the source file but I don't want to, it would make any future update a pain. So I tried to find an other way.

Looking at the loadCSSmethod, it appears that there is a mechanism to prevent loading twice the same file. If the file is already present in the .files array, it won't be added. Ok, so now I just have to manually add the file before the call is made.

That's where the setup callback come into play. I just defined in my inyMCE.init :

tinyMCE.init({
  skin: 'caracole',
  editor_css: 'caracoleDoNotInclude',
  setup: function(editor) {
    tinymce.DOM.files['http://'+document.domain+'/caracoleDoNotInclude'] = true;
  }
}

Doing so, the loadCSSwill try to load the caracoleDoNotInclude file (clearly this name is a placeholder, I have no such file). And in the setup method I manually tell tinyMCE that this file is already loaded so when it will initiate, it won't load the file at all.

Same goes for the content_css, the file that handle the look and feel of the editable zone. I don't want to have my file in the tinyMCE skin directory, I'd rather have it with my other CSS files.

So I defined a custom content_css to point to my file. I also had to update the setup method to mark the file as already loaded. But this time, as the file is loaded on a per instance basis, this is the editor.dom.files that I have to update, not the general tinymce.DOM.

Fortunatly, the latest tinyMCE version expose their event API, so it was just a matter of defining the following event in the main setup :

setup: function(editor) {
  tinymce.DOM.files[baseUrl+'caracoleDoNotInclude'] = true;
  editor.onPreInit.add(function(editor) {
    editor.dom.files[baseUrl+'js/vendors/tiny_mce/themes/advanced/skins/caracole/content.css'] = true;
   });
 }

Edit :

Note that in the latest tinyMCE version, the code is slightly different because you have to call a special method on the url before adding it to the editor.dom.files

editor.onPreInit.add(function(editor) {
  editor.dom.files[editor.baseURI.toAbsolute('http://'+document.domain+'/js/vendors/tiny_mce/themes/advanced/skins/caracole/content.css')] = true;
});

Rendering bug with generated content on form fields in Opera

If you try the following code in Opera 10.51, you'll have some weird rendering bug :

<style>
  .test:after {
    content:"This should be on red background";
    background:red;
  }
 </style>
 Text input : <input type="text" value="I'm unstyled" class="test" />

The generated content is added to the page, but the background color isn't rendered. Worse, the input lose all styling, it does not have a background color nor borders anymore.

The same effect also apply to every input(radio, checkboxand password).

On a select tag, the generated content correctly have it's background color, but still lose all styling.

On a textarea, the styling is gone too, and the background color is here. Well, sort of, it is actually cropped after a while and the end of the text is on transparent background.

I sent a bug report to Opera about that.

Opening a new tab using Ctrl+Click on Opera

One thing that bugs me everytime I use Opera are the weird keyboard shortcuts. You open link in a new tab by using the middle mouse button (classic) or Shift+click (less classic, we are accustomed to use the Ctrl+Click, Shift+Click being used to open in a new window).

Today it was bugging me one too many times, so I decided to use the power of AutoHotKey to help fix this.

Here is the little snippet I added to my default AutoHotKey script :

;        Open page in new tab in Opera using Ctrl + Click
#ifWinActive, ahk_class OpWindow
^LButton::Send +{LButton}
#IfWinActive

It basically catch every Shift+Click in Opera and return a Ctrl+Click instead, allowing me to finally open tabs the way I'm used to.

Knowing if an element is in the DOM using jQuery

If you ever need to know if the element you have a reference to is really in your page (or if it is an old reference to an element you've already replaced), you can use the $.contains(container, contained) method.

I was just working on an AJAX paginated list of elements and I kept references to my table in vars to avoid re-querying it each time. But this reference where still 'correct' (ie. not null) even after having replaced the element with a new one (using .replaceWith() and even .empty() and .remove())

I added a check to see if the element was really in the DOM before using it, and if not requerying it and re-saving it in var

missing data in jQuery Ajax event ajaxSuccess

jQuery provides a nifty set of Ajax events that one could define to fire methods whenever an ajax request starts, stop, failed or succeed. It is quite useful actually, to display visual indicator once and for all without having to define the various callback on each ajax call.

But it has one flaw. The ajaxSuccesscallback only give access to the event, XMLHttpRequestobject and the option object. There is no direct way to get the return data from the call.

Well, you can still access it from the XMLHttpRequest responseText attribute but it is then your job to parse it according to its type, duplicating some of the code already called in the jQuery core.

You can use the $.httpData to directly parse it, giving the XHR as first argument and the data type as the second one.

$('#ajaxIndicator').bind('ajaxSuccess', function(event, xhr, options) {
  var data = $.httpData(xhr, options.dataType);
}