jQuery-plugin: jquery.rightclick

I’ve written a plugin for jQuery to handle right click events.

The plugin provides two features:

$( … ).disableContextMenu( condition_event );

This function disables the browsers build-in contextmenu (right-click menu on PC’s).

condition_event  – is an optional parameter, it’s meant to be a function-reference to determine if the contextmenu should be shown, the function must return true if the menu is allowed to be shown and must return false if the menu is not to be shown. If condition_event is not provided or returns a non-boolean value, the contextmenu will be disabled.

Please note: This is not a way to stop your users from downloading images or viewing source code, this is only meant to be used as when it’s better for the usability.

$( … ).rightClick( callback, contextmenu_condition_event );

This function will let you create your own event for right click, it can be used to create your own contextmenu.

It takes two paramters.

callback – Is the event which will be called on right click. (required)

contextmenu_condition_event – will be parsed to disableContextMenu.

Extra functions:

I also provide functions to remove these event from an element:

$( … ).enableContextMenu( );

Will undo the function $( … ).disableContextMenu( );

$( … ).removeRightClick( );

Will remove the rightClick event from the element.

License:

   Copyright (c) 2009 Morten Sjoegren <m_abs@mabs.dk>
   Permission is hereby granted, free of charge, to any person obtaining a copy
   of this software and associated documentation files (the "Software"),

 to deal
   in the Software without restriction, including without limitation the rights
   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   copies of the Software, and to permit persons to whom the Software is
   furnished to do so, subject to the following conditions:
   The above copyright notice and this permission notice shall be included in
   all copies or substantial portions of the Software.
   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
   THE SOFTWARE.

Download:

You can download the plugin including a simple example here.

Banana cake with chocolate glaze

This is the recipe for my favorite cake, being a big guy there is some competition for this title. 🙂

This is a recipe I got from a friends mother, who is a great cook. It’s a banana cake with chocolate glaze.
Please note that this cake is in no way healthy to eat, but it taste great.


Dough:
225 grams of butter
460 grams of sugar
4 whole eggs
5 very ripe banana.
50 grams of chopped hazel nuts
50 grams of oatmeal
1 ¼ deciliter of buttermilk
½ tablespoon salt
2 tablespoon baking soda
2 tablespoon vanilla sugar
375 grams of flour
100 grams of chopped dark chocolate

Glaze:
100 grams of smelted dark chocolate

This is what you do:
Smelt the butter and mix it with the sugar, add the eggs one at the time.
Squeeze the bananas and add them in the dough with the hazel nuts and the oatmeal.
Mix the flour, soda, baking soda, vanilla sugar and sieved it into the dough.
Stir the dough until it is uniform.

Bake the cake in a greased form at 200 degrees Celsius for approx. 35 minutes.
When cake has cooled, melt the chocolate and glaze the cake with the smelted chocolate.


Here is the original recipe in Danish:

Dej:
225 gr. Smør
460 gr. Sukker
4 stk. Æg
5 stk. Meget modne bananer
50 gr. Hakkede hassel nødder
50 gr. Havregryn
1 ¼ dl. Kærnemælk
½ tsk. Salt
2 tsk. Natron
2 tsk. Vanillesukker
375 gr. Mel
100 gr. Hakket mørk chokolade

Pynt:
200 gr. Smeltet mørk chokolade

Fremgangsmåde:
Smøret smeltes og blandes godt med sukkeret, æggene tilsættes et
af gangen. Bananerne moses og kommes i sammen med de
hakkede nødder og havregryn, kærnemælken hældes i.
Mel, natron, vanillesukker og salt blandes og sigtes i, dejen røres til
den er ensartet.
Bages i smurt form ved 200 grader i ca. 35 minutter
Når kagen er kølet af, smeltes chokoladen og fordeles over.

Creating a custom dialog with jquery and jquery.blockUI

The goal of this article is to demostrate how to create a dialog with it own self-contained functionality using jQuery and blockUI.

For this we need jQuery (v1.3.x)1 and blockUI(v2.15+)2. I assume you have at least basic understanding of jQuery and JavaScript to make use of this article.

I tend to think of a dialog like an interface with it’s own enclosure containing attributes, private functions and public functions. The idea is that we can reuse the functionality easily, just include the javascript-file in the webpage and call a function.

The dialog I’ll create here is a dialog from which a user will select the size of a pair of jeans.

I pack my code into a namespace, so I create the namespace first3. My dialog will be a subset of UI.dialog.

var UI = {
  dialog : {}
};

Now I can create the basic structure of our interface in a new file:

( function( UI, $ ) {
  // Make sure the namespace exists.
  if ( !UI.dialog ) {
    UI.dialog = {};
  }

  // It this interface already loaded?
  if ( UI.dialog.selectSize ) {
    // If so don't intiate it again
    return;
  }

  // Attributes

  // Private functions

  // Public functions
  UI.dialog.selectSize = function( ) {
  };
} )( UI, jQuery );

This piece of code shows the basic code-structure I use to create an interface.

First I create an enclosure which is an anonymous function4,

which is called immediately with the UI and jQuery parameter. Everything inside this enclosure is unreachable from the outside, unless I intentionally make it public by binding functions or variables to a global variable like UI.
I these three sections in my enclosure:

  1. Attribute section
  2. Private function section
  3. Public function section

In the same way I would have it in a class in C++ or Java, which is the model I used to come up with this.

Now I’ll create the HTML part of this dialog. I’ll add the function setupHTML to the private functions section.

	function setupHTML( q, msg, sizes ) {
		// We need a group name to make exclusive selection in the radio buttons we are going to create.
		var group_name = "size_" + parseInt( Math.random( ) * 100, 10 );

		// Write the HTML much like the way, we would write it directly in our .html-file.
		var dd = "<div style='text-align : left; cursor : default;'>";
		dd += "<div style='font-weight: bolder; padding-left:20px;padding-right:20px;'>" + msg + "</div>";

		// Add a radio-button for each size in our sizes, array.
		$.each( sizes, function( i, size ) {
			dd += "<div>";
			dd += "<input type='radio' id='" + group_name + "_" + i + "' style='margin-right: 8px;' name='" + group_name + "' value='" + size + "' />";
			dd += "<label for='" + group_name + "_" + i +  "'>" + size + "</label>";
			dd += "</div>";
		} );
		
		// Add buttons
		dd += "<div style='text-align: right; padding : 5px;'>";
		dd += "<button xvalue='okay' disabled='disabled'>Okay</button>";
		dd += "<button xvalue='cancel'>Cancel</button>";
		dd += "</div>";
		dd += "</div>";
		
		q.html( dd );
	}

Now that we have the function for creating the HTML, we need to create the function to open the dialog.
This function is called UI.dialog.selectSize(…) and takes three parameters:

  1. msg // The message to the user
  2. sizes // An array containing the available sizes
  3. callback // The function is called after the user clicks “okay”. The first parameter in the callback function is the selected size
	UI.dialog.selectSize = function( msg, sizes, callback ) {
		// There is no point in showing the dialog without both msg and sizes
		if ( msg && sizes ) {
			// Block the whole page
			$.blockUI( {
				message : $( "<div id='select_size_overlay'></div>" ),


				css : {
					width : "auto",
					height : "auto"
				}
			} );
			
			// The container for the dialog is the div I created above,
			// we need to find it and add the content to it.
			var q = $( "#select_size_overlay" );
			setupHTML( q, msg, sizes );
			
			// TODO: Handle events
		}
	};

In this function I haven’t added the events yet, this is because I haven’t created the event functions yet.
To limit the potential memory leaks in browsers like IE6, I choose not to make them inline event-functions but to make them private functions.
The potential memory leak is that referencing DOM-element in inline function, creates an extra reference to that element which can confuse the garbage collector. The problem is increased if the function is inline, since it’s created every time the parent function is call which could be many time.

I put my event functions in the private functions-section of my interface.

	function button_click_event( e ) {
		// Get the element that was clicked, which is e.target.
		// This should work in all browsers since jQuery have been so kind as to fix broken event-objects.
		var el = $( e.target );
		
		// Get data for this event
		// This is important since it holds reference to data from the UI.dialog.selectSize-function.
		var data = e.data;
		
		// The xvalue-attr tells the event-handler which action we want to perform.
		var xvalue = el.attr( "xvalue" );
		if ( xvalue == "okay" ) {
			// Is something selected?
			var radios = data.radios;
			var selected_val = radios.filter( ":checked" ).val( );
			if ( selected_val ) {
				if ( $.isFunction( data.callback ) ) {
					data.callback( selected_val );
				}
				
				$.unblockUI( );
			} else {
				// Should never happen, since the okay button should be disabled
				alert( "You must select a size." );
			}
		} else if ( xvalue == "cancel" ) {
			$.unblockUI( );
		}
	}
	
	function radio_click_event( e ) {
		// Get the element that was clicked.
		var el = $( e.target );
		
		// Get data for this event
		var data = e.data;

		// Toggle okay-button on/off on selection
		var radios = data.radios;
		var okay_button = data.buttons.filter( "[xvalue=okay]" );
		if ( radios.filter( ":checked" ).size( ) > 0 ) {
			okay_button.removeAttr( "disabled" );
		} else {
			okay_button.attr( "disabled", "disabled" );
		}
	}

Now that these event-functions have been created I can bind them in the UI.dialog.selectSize-function:

	UI.dialog.selectSize = function( msg, sizes, callback ) {
		// There is no point in showing the dialog without both msg and sizes
		if ( msg && sizes ) {
			// Block the whole page
			$.blockUI( {
				message : $( "<div id='select_size_overlay'></div>" ),


				css : {
					width : "auto",
					height : "auto"
				}
			} );
			
			// The container for the dialog is the div I created above,
			// we need to find it and add the content to it.
			var q = $( "#select_size_overlay" );
			setupHTML( q, msg, sizes );
			
			// Inorder for the dialog to work, we need to bind some event to the elements in it.
			var buttons = q.find( "button[xvalue]" );
			var radios = q.find( "input[type=radio]" );
		
			// Handle events
			
			// I'm using jQuery's bind function to bind the events to the elements and to parse data to those events.
			// I can't use buttons.click( button_click_event ),

 because I need to parse some extra data to the events.

			// Handle click event for the buttons
			buttons.bind( "click", {
				callback : callback,
				buttons : buttons,
				radios : radios
			}, button_click_event );
			
			// Handle click event for the radio-buttons
			radios.bind( "click", {
				buttons : buttons,
				radios : radios
			}, radio_click_event );
		}
	};

Now I’m done with writing the dialog interface, here is the entire code including jslint parameters5:

/*jslint undef: true, browser: true */

/*global jQuery, UI */

( function( UI, $ ) {
	if ( !UI.dialog ) {
		// This is part of the UI.dialog namespace, if the namespace doesn't exist create the namespace object
		UI.dialog = {};
	}

	// It this interface already loaded?
	if ( UI.dialog.selectSize ) {
		return;
	}

	// Attributes

	// Private functions
	function setupHTML( q, msg, sizes ) {
		// We need a group name to make exclusive selection in the radio buttons we are going to create.
		var group_name = "size_" + parseInt( Math.random( ) * 100, 10 );

		// Write the HTML much like the way, we would write it directly in our .html-file.
		var dd = "<div style='text-align : left; cursor : default;'>";
		dd += "<div style='font-weight: bolder; padding-left:20px;padding-right:20px;'>" + msg + "</div>";

		// Add a radio-button for each size in our sizes, array.
		$.each( sizes, function( i, size ) {
			dd += "<div>";
			dd += "<input type='radio' id='" + group_name + "_" + i + "' style='margin-right: 8px;' name='" + group_name + "' value='" + size + "' />";
			dd += "<label for='" + group_name + "_" + i +  "'>" + size + "</label>";
			dd += "</div>";
		} );
		
		// Add buttons
		dd += "<div style='text-align: right; padding : 5px;'>";
		dd += "<button xvalue='okay' disabled='disabled'>Okay</button>";
		dd += "<button xvalue='cancel'>Cancel</button>";
		dd += "</div>";
		dd += "</div>";
		
		q.html( dd );
	}

	/*
	 * Event for handling click on any button in the overlay
	 */
	function button_click_event( e ) {
		// Get the element that was clicked. jQuery fixes the incompatibility between browsers' event-object (e).
		var el = $( e.target );
		
		// Get data for this event
		var data = e.data;
		
		// The xvalue-attr tells the event-handler which action we want to perform.
		var xvalue = el.attr( "xvalue" );
		if ( xvalue == "okay" ) {
			// Is something selected?
			var radios = data.radios;
			var selected_val = radios.filter( ":checked" ).val( );
			if ( selected_val ) {
				if ( $.isFunction( data.callback ) ) {
					data.callback( selected_val );
				}
				
				$.unblockUI( );
			} else {
				// Should never happen, since the okay button should be disabled
				alert( "You must select a size." );
			}
		} else if ( xvalue == "cancel" ) {
			$.unblockUI( );
		}
	}
	
	function radio_click_event( e ) {
		// Get the element that was clicked. jQuery fixes the incompatibility between browsers' event-object (e).
		var el = $( e.target );
		
		// Get data for this event
		var data = e.data;

		// Toggle okay-button on/off on selection
		var radios = data.radios;
		var okay_button = data.buttons.filter( "[xvalue=okay]" );
		if ( radios.filter( ":checked" ).size( ) > 0 ) {
			okay_button.removeAttr( "disabled" );
		} else {
			okay_button.attr( "disabled", "disabled" );
		}
	}
	
	// Public functions
	UI.dialog.selectSize = function( msg, sizes, callback ) {
		if ( msg && sizes ) {
			$.blockUI( {
				message : $( "<div id='select_size_overlay'></div>" ),


				css : {
					width : "auto",
					height : "auto"
				}
			} );
			
			var q = $( "#select_size_overlay" );
			setupHTML( q, msg, sizes );
			
			var buttons = q.find( "button[xvalue]" );
			var radios = q.find( "input[type=radio]" );
		
			// Handle events

			// Handle click event for the buttons
			buttons.bind( "click", {
				callback : callback,
				buttons : buttons,
				radios : radios
			}, button_click_event );
			
			// Handle click event for the radio-buttons
			radios.bind( "click", {
				buttons : buttons,
				radios : radios
			}, radio_click_event );
		}
	};
} ) ( UI, jQuery );

Using the dialog is as simple as this:

	UI.dialog.selectSize( "Select size of the jeans", [
	  "xx-small", "x-small", "small", "medium", "large", "x-large", "xx-large", "xxx-large", "xxxx-large"
	], function( size ) {
		// TODO: Do something usefull with the size
		alert( size );
	} );
  1. jQuery (v1.3.x) from jquery.com []
  2. blockUI(v2.15+) from malsup.com []
  3. Covered in Namespacing in JavaScript []
  4. Covered in Reintroducing a little sanity in working with JavaScript []
  5. I prefer to run all my JS-code through jslint to find hidden errors. []

Namespacing in JavaScript

To structure my javascript code I’ve developed a namespace scheme1. Namespace is used in languages like C++ to group functionality and data, it’s also used in Java but referred to as packages.

Coming from a C++ background with structure I wanted to impose a kind of order to my JavaScript code. Prior the using namespace almost all my functions were global functions and they had longer and longer names like this:
data_sentences_get( sent_id ) {…}

To get some structure on this I decided to have two global objects:

	var UI = [];
	var Data = [];

UI is the top-level namespace for all my UI functions.
Data is the top-level namespace for all data-processing and retrival functions.

In this scheme the above mentioned function would be defined like this:

( function( Data ) {
	if ( Data.sentences ) {
		return; // Already defined, don't do it again.
	}

	// Create the sentences namespace object.
	Data.sentences = [];

	// Create a short hand reference to it.
	var ref = Data.sentences;

	ref.get = function( sent_id ) {
		// TODO
	};
} )( Data );

In this example I create the namespace Data.sentences (I know this is actually an object) and create the function “Data.sentences.get( sent_id )”.

This structure gives me some benefits. The first is the grouping of functions which help me structure my code, the second is that I can have private data and functions for each namespace2 and the third is that I can use dynamic load functions as needed.

Jquery have a useful function called $.getScript, it’s used to dynamically load JavaScript-files to the webpage. Since we have the namespace objects and JavaScript allows us to extend already initiated objects, we have an easy way to load new functions and to check if they have been loaded.

….
	if ( Data.sentences ) {
		Data.sentences.get( 1 );
	} else {
		$.getScript( “data_sentences.js”, function( ) {
			Data.sentences.get( 1 );
		} );
	}
...

If you have a large web application, you might want to save memory or bandwidth by only loading functionality as needed. You might need 100kB+ of JavaScript code for a component your users might use less then 1% of the time, this costs bandwidth and load time.

But if you do this you need to consider what could go wrong. If the server or connection is slow, loading code might make the interface fell slow since the user will have to wait for the code to be loaded. There is the possibility that the request fails or times out, you need to handle this problem or the interface might be appear to have frozen. There is also the possibility, that the version of code on the server is incompatible with the version of code the browser has (this shouldn’t happen. If it does something is wrong with you development process).

  1. Javascript doesn’t really have namespaces so we need to fake them []
  2. Covered in the article
    Reintroducing a little sanity in working with JavaScript []