Using TinyMCE content_css, body_class, etc settings in Magento

Posted by stephen on July 15th, 2010

I needed to add a stylesheet and body class to the Magento TinyMCE editor, and had some trouble figuring out why my settings weren’t working. Here’s what I ended up doing…

First, rename /js/tiny_mce/themes/advanced/editor_template_src.js to editor_template.js (delete or rename the one that’s already there to something like editor_template.js.bak). Look for this block in the init method around line 65:

// Default settings
t.settings = s = extend({
theme_advanced_path : true,
theme_advanced_toolbar_location : 'bottom',
theme_advanced_buttons1 : "bold,italic,underline,strikethrough,|,justifyleft,justifycenter,justifyright,justifyfull,|,styleselect,formatselect",
theme_advanced_buttons2 : "bullist,numlist,|,outdent,indent,|,undo,redo,|,link,unlink,anchor,image,cleanup,help,code",
theme_advanced_buttons3 : "hr,removeformat,visualaid,|,sub,sup,|,charmap",
theme_advanced_blockformats : "p,address,pre,h1,h2,h3,h4,h5,h6",
theme_advanced_toolbar_align : "center",
theme_advanced_fonts : "Andale Mono=andale mono,times;Arial=arial,helvetica,sans-serif;Arial Black=arial black,avant garde;Book Antiqua=book antiqua,palatino;Comic Sans MS=comic sans ms,sans-serif;Courier New=courier new,courier;Georgia=georgia,palatino;Helvetica=helvetica;Impact=impact,chicago;Symbol=symbol;Tahoma=tahoma,arial,helvetica,sans-serif;Terminal=terminal,monaco;Times New Roman=times new roman,times;Trebuchet MS=trebuchet ms,geneva;Verdana=verdana,geneva;Webdings=webdings;Wingdings=wingdings,zapf dingbats",
theme_advanced_more_colors : 1,
theme_advanced_row_height : 23,
theme_advanced_resize_horizontal : 1,
theme_advanced_resizing_use_cookie : 1,
theme_advanced_font_sizes : "1,2,3,4,5,6,7",
readonly : ed.settings.readonly
}, ed.settings);

For general class selectors that you want to show in the dropdown, you can use the “theme_advanced_style_formats” setting.

theme_advanced_style_formats : "alert-text, happy-text",

I proceeded to add body_class and content_css settings to this object, then dug around for an hour or two trying to find why they weren’t being applied. There’s a hierarchy at work here that I’m sure would be apparent if I were more familiar with TinyMCE’s inner workings.From the looks of it, maybe it’s only properties that start with “theme_” that should be added here (readonly is probably a generic prop for all/many settings object). Anyways, I basically found that we have to edit the editor’s settings directly, rather than the theme’s. So, above t.settings = s = …, I added this:

extend(ed.settings, {
content_css:"/skin/frontend/enterprise/gf/css/content.css",
body_class:"main"
});

And there you have it!

jQuery.animate “too much recursion” from complete callback

Posted by stephen on June 11th, 2010

I was working on a class for sequencing a set of DOM elements, and ran into a snag using the animate function like this:

$.fn.animate.apply($item, this.transitionOutArgs);

My animate args (properties, options) looked something like this:

[{opacity:.1}, {duration:1500, complete: function(){ $(this).css("display","none"); }}]

Firebug kept saying “too much recursion”, and only after the second time the code was run. After a bit of head scratching and some digging into the jQuery code, I saw that it copies the options.complete callback and replaces it with a function that calls the old callback. So, after passing in my complete callback the first time, this.transitionOutArgs[1].complete() was reassigned to the proxy-ish function, and each time after, the proxy-ish function was calling itself.

It’d be nice if jQuery created another callback, such as options.$complete(), that proxies for our explicitly defined options.complete callback, instead of reassigning it, but they may have their reasons. To work around this, I just copied the args before passing them to the function.

$.fn.animate.apply($item, $.extend(true, [], this.transitionOutArgs));

If you’re declaring the transitionOutArgs in the same scope as where the animate function is called, there is no need to copy it, since it is not being referenced and reused. That is the typical use case.

Custom Events in JavaScript

Posted by stephen on December 29th, 2009

Usually I use jQuery for custom events, but recently I’ve been working on a library that I want to be independent of other libraries, so I needed to create something to handle events on custom classes/objects.

For this sample, I changed the namespace to “ns”. You can change that to whatever you’d like.

Eventify

To “eventify” an object, you’ll use the ns.events.eventify(obj) method. Once that is done, your object will have “events” and “__listeners” properties, in addition to “dispatchEvent(eventType, eventData)”, “addEventListener(eventType, callback, bubbles)”, and “removeEventListener(eventType, callback||listener)” methods.

You may pass one or many objects to the eventify() method.
ns.events.eventify(obj);
or
ns.events.eventify(obj1, obj2, obj3);

You do not need to interact with the “events” and “__listeners” properties at all, unless you want to predefine a set of events for a particular object, usually for documentation purposes. Here’s an example of a custom class that uses this event class and predefines its events. Just to clarify, I am using the “id” property in this class for the event bubbling example that follows. It is not needed at all for the events to work.

addEventListener() / dispatchEvent()


ns.MyClass = ({
function MyClass(id){
ns.events.eventify(MyClass.prototype);
this.id=id;
}
MyClass.prototype={
id:null,
events:{
ready:new ns.events.Event("ready"),
error:new ns.events.Event("error")
},
property:null,
method:function(){}
}
return MyClass;
})();

If you do not define the events explicitly in the events property, they will be added automatically when a listener is added to the object. For instance:

var my = new ns.MyClass("my");
my.addEventListener("ready", function(){
alert(this.id+" is ready");
});

Would define my.events.ready.

dispatchEvent() and passing data

To dispatch an event, use the dispatchEvent() method. To pass data with a dispatched event, use the eventData parameter. For example:

my.addEventListener("ready", function(evt, data){
alert(data.hello);
});
my.dispatchEvent("ready", {hello:"Hello from dispatcher."});

Event bubbling

Event bubbling is manually assigned in an event’s “bubbleTo” property, because these are custom objects with no inherent heirarchy. The object defined as the “bubbleTo” property must share the event type that is being dispatched on the current object (they do not need to be instances of the same class). Implement bubbling as follows:

var parent = new ns.MyClass("parent");
var child = new ns.MyClass("child");
child.events.ready.bubbleTo = parent;

child.addEventListener(”ready”, function(evt){
alert(”sincerely, “+evt.target.id+”.”);
});
parent.addEventListener(”ready”, function(evt){
alert(”Sincerely, “+evt.target.id+”. With regards, “+evt.currentTarget.id+”.”);
});

child.dispatchEvent(”ready”);

To prevent subsequent callbacks on an object, call evt.stopImmediatePropagation(). To stop the bubbling, call evt.stopPropagation().

You may also prevent the bubbling of an event when creating the listener by using the “bubbles” parameter of the “addEventListener()” method.

var listener = child.addEventListener("ready", function(evt){
alert("sincerely, "+evt.target.id+".");
}, false);

removeEventListener()

Finally, to remove a listener from an object, use the removeEventListener(listener||callback) method. This method will accept the listener or the callback function used. Note that in the above example I have assigned the listener to “var listener”.
child.removeEventListener("ready", listener);
You may also use the remove() method of the listener itself.
listener.remove();

Download

Right click and choose “Save Target As” to download events.js.

Javascript Date difference as milliseconds, minutes, hours, days, weeks, months, or years

Posted by stephen on November 13th, 2009

I needed to figure out the difference between two dates in Javascript, so I wrote up this function. It takes into account the days in each month as well as leap years. It will calculate years, months, weeks, days, hours, minutes, seconds, and milliseconds, or any combination of those. For example, if you want months and hours, this’ll do ya.

Let me know if anyone finds a discrepancy in the calculations.

sample.html
timeSpan.js

View source of the sample page above for usage. Samples include a date comparison and a date countown.

IE6 and form.submit() bug workaround

Posted by stephen on November 3rd, 2009

IE6………one of life’s great mysteries. I think we may need some federally enforced browser standards more than a new health care system, but that’s just me.

In IE6, programatically triggering the submit() method of a form element doesn’t *always* seem to work. There’s no logical reason - it just doesn’t. I’ve run into this problem twice now. The first time, I just ended up building a query string out of the form variables and using window.location to send them to the action page. That was with a static form. I just hit this wall again with a form I’m building dynamically.

For *whatever* reason, delaying the submit action by 100ms works like a charm. I wish I could tell you more, but as with all IE6 bug fixes, when they work, I try not to ask questions.

Here’s what I ended up doing:

I tried building the form through the browser’s native methods, using document.createElement() and element.appendChild(), which didn’t make a difference, so I reverted back to created the form with jQuery. So, here’s what I ended up doing….

/* ....build form elements using jQuery... */
$j("body").append(form);

setTimeout(function(){
	form.submit();
}, 100);

Hope this saves someone some time! If you’re dealing with a form that is not being built dynamically, I have an inkling that this may not work. Still, delaying the event another way might. Try this, and if it works for you, let me know!

<a href="javascript:;" onclick="setTimeout(function(){ form.submit() }, 100)">Submit</a>

That’s the onclick event of an anchor tag, but the same would apply for the onclick of any element.

jquery.tabPane()

Posted by stephen on February 26th, 2009

Everyone enjoys a good set of tabbed panes. I wrote jquery.tabPane to give us a simple way to do that, because this particular site is using Adobe Contribute as the CMS. Basically, you create some links to anchors, then tell this function what the containing elements for those are.

Usage

jQuery.tabPane(tabPaneSettings, callback);

Example

This isn’t so much of an example as a clarified description. If you want to see a sample, go here. It also has some HTML and CSS selectors you can use as a base, if you’d like.

Let’s say you have a list of anchor links, and several divs with content. You can point the tabPane settings at the containers of the links to those anchors (tabs), and the containers of those anchors (pane). The display/hide functionaly is handled for you, and you can style it any way you want.

Here you have it:

var paneSettings = {
	tabContainers:"div.pane-buttons li",
	contentContainers:"div.pane-content div.content-pane"
};
j.tabPane(paneSettings);

Javascript CSS Formatter

Posted by stephen on February 17th, 2009

I’ll probably post more on this later, but I just want to get it up for now.

Sometimes my CSS gets auto-formatted, which adds a whole bunch of “clutter” space that I don’t want. I find CSS easier to read when there is a visual hierarchy of the selectors and the HTML elements they represent, and if there is consistency in the order of the selector attributes.

The following CSS Formatter will place all of your selectors and their contents on a single line, and sort their attributes alphabetically.

For instance…

div#whatever, div.whatnot {
display:block;
background:transparent;
float:left;
}

becomes…

div#whatever,
div.whatnot {background:transparent; display:block; float:left;}

The formatter will IGNORE any space outside of the selector and its contents.

Try it out here:

http://blog.stephenrushing.com/wp-content/uploads/2009/02/cssFormat/cssformat.html


Copyright © 2007 Stephen Rushing. All rights reserved.