Introducing PixelPerfectJs

PixelPerfectJs allows developers to put an image overlay on top of your HTML page and achieve pixel precise results.

How to use:
———–
Save the following link as a bookmark, or drag it to the links/bookmarks bar in your browser:

Or if you don’t want to use the bookmarklet, just add the following line before your closing body tag:
<script type="text/javascript" src="//dl.dropbox.com/u/2369055/pixelperfectjs/_ui/js/pixelPerfect.all.js"></script>

The PixelPerfectJS panel will then be displayed.

Settings:
———
* Enable: Yes/No. Whether you want to enable PixelPerfectJs or not
* Designs: Enter the image URL you want to overlay on top of your HTML. It must be either an absolute or relative URL. You can add as many images as you want.
* Position: You can manually set the position of the overlay
* Opacity: Control the opacity of the overlay.

Features:
———
* Simple! Just add 1 line of javascript!
* Not an extension or addon. Will work in any browser!
* No image size restrictions!
* Multiple overlays support
* Your settings are saved between sessions.

Contact:
———
Please send bug reports and ideas for future versions to: [email protected]

You can find the code on GitHub too: https://github.com/asvinb/pixelperfectjs
Feel free to contribute!

Writing your first Greasemonkey script – adding menus to Facebook’s top menu

This post will show you how to write a basic Greasemonkey script that will add two more menu items to Facebook’s friends top menu as shown below.

These 2 menu items are :

  • “Recently Updated” – show all your friends with recent updates
  • “Status Updates” – show the latest status updates of your friends

About Greasemonkey

Greasemonkey is a Firefox extension that allows you to write scripts that alter the web pages you visit. You can

  • use it to make a web site more readable or more usable
  • fix rendering bugs that the site owner can’t be bothered to fix themselves.
  • alter pages so they work better with assistive technologies that speak a web page out loud or convert it to Braille
  • even automatically retrieve data from other sites to make two sites more interconnected.

Greasemonkey by itself does none of these things. In fact, after you install it, you won’t notice any change at all… until you start installing what are called “user scripts”. A user script is just a chunk of javascript code, with some additional information that tells Greasemonkey where and when it should be run. Each user script can target a specific page, a specific site, or a group of sites.

Install Greasemonkey addon for Firefox

Go to Greasemonkey addon page, click on the “Add to Firefox” button and follow the instructions. After restarting Firefox, you will notice a monkey’s head sitting at the bottom right its status bar.
If you right click on the monkey’s head and choose “Manage User Scripts” from the resulting menu, you will notice there isn’t any script which is quite normal since we have not installed any ;-).

Creating your first Greasemonkey script.

For a Greasemonkey to be recognized, it must end with .user.js, so let us create an empty file and save it as myfirstscript.user.js
Fire up your favourite text editor and open your newly created file.

Greasemonkey script metadata

All user scripts has a meta data section which tells Greasemonkey about the script itself, where it came from, and when to run it. For example, our script :

// ==UserScript==
// @name          My First GM script
// @namespace     https://htmlblog.net
// @description   basic Greasemonkey script
// @include       http://www.facebook.com/
// ==/UserScript==

You can find more information about the metadata here.

Remember we want our script available only for Facebook pages, that is why we use @include http://www.facebook.com

Show me some code.

Alright, to add our own menus, we need to get the parent element holding the drop down menu. After inspecting Facebook’s source code, we can rapidly come to the conclusion that the parent element’s id is “fb_menu_friends_dropdown”. So the current Facebook code looks like that :

<div class="fb_menu_dropdown hidden_elem" id="fb_menu_friends_dropdown">
<div class="fb_menu_item"><a href="http://www.facebook.com/friends/?added&amp;ref=tn">Recently Added</a></div>
<div class="fb_menu_item"><a href="http://www.facebook.com/friends/?everyone&amp;ref=tn">All Friends</a></div>
<div class="fb_menu_separator"></div>
<div class="fb_menu_item"><a href="http://www.facebook.com/invite.php?ref=tn">Invite Friends</a></div>
<div class="fb_menu_item"><a href="http://www.facebook.com/find-friends/?ref=friends">Find Friends</a></div>
</div>

and our goal is to insert the two menu items before the “Recently Added” item.

For us to do that, we need to get the first child of the parent element and then insert our elements before it and to help us, i.e walking through the DOM selecting parent and childs, I use a little piece of code found on Github, which eliminates whitespace issues between elements among other features. So our myfirstscript.user.js can be updated to :

// ==UserScript==
// @name          My First GM script
// @namespace     https://htmlblog.net
// @description   basic <a href="https://addons.mozilla.org/firefox/addon/748" target="_blank">Greasemonkey</a> script
// @include       http://www.facebook.com/
// ==/UserScript==
DOM = function () {

    function get(id) {
        if (id && typeof id === 'string') {
            id = document.getElementById(id);
        }
        return id || null;
    }

    function walk(element, tag, walk, start, all) {
        var el = get(element)[start || walk], elements = all ? [] : null;
        while (el) {
            if (el.nodeType === 1 && (!tag || el.tagName.toLowerCase() === tag)) {
                if (!all) {
                    return el;
                }
                elements.push(el);
            }
            el = el[walk];
        }
        return elements;
    }

    return {

        // Get the element by its id
        get: get,

        walk: walk,

        // Returns the previousSibling of the Element (excluding text nodes).
        getPrevious: function (el, tag) {
            return walk(el, tag, 'previousSibling');
        },

        // Like getPrevious, but returns a collection of all the matched previousSiblings.
        getAllPrevious: function (el, tag) {
            return walk(el, tag, 'previousSibling', null, true);
        },

        // As getPrevious, but tries to find the nextSibling (excluding text nodes).
        getNext: function (el, tag) {
            return walk(el, tag, 'nextSibling');
        },

        // Like getNext, but returns a collection of all the matched nextSiblings.
        getAllNext: function (el, tag) {
            return walk(el, tag, 'nextSibling', null, true);
        },

        // Works as getPrevious, but tries to find the firstChild (excluding text nodes).
        getFirst: function (el, tag) {
            return walk(el, tag, 'nextSibling', 'firstChild');
        },

        // Works as getPrevious, but tries to find the lastChild.
        getLast: function (el, tag) {
            return walk(el, tag, 'previousSibling', 'lastChild');
        },

        // Works as getPrevious, but tries to find the parentNode.
        getParent: function (el, tag) {
            return walk(el, tag, 'parentNode');
        },

        // Like getParent, but returns a collection of all the matched parentNodes up the tree.
        getParents: function (el, tag) {
            return walk(el, tag, 'parentNode', null, true);
        },

        // Returns all the Element's children (excluding text nodes).
        getChildren: function (el, tag) {
            return walk(el, tag, 'nextSibling', 'firstChild', true);
        },

        // Removes the Element from the DOM.
        dispose: function (el) {
            el = get(el);
            return (el.parentNode) ? el.parentNode.removeChild(el) : el;
        }

    };
}();

To get the parent element and its first child, we then append the following code :

// get drop down menu
var parentNode = DOM.get('fb_menu_friends_dropdown');
// get its first child
var firstNode = DOM.getFirst('fb_menu_friends_dropdown');

Creating our nodes

Now we must create our two nodes which will be inserted afterward. This is done easily with the following code which is self explanatory :

/** For "Recently Updated" */
// create our div with class fb_menu_item
var recentDiv = document.createElement('div');
recentDiv.setAttribute('class', 'fb_menu_item');

// create our link
var recentLink = document.createElement('a');
recentLink.href = 'http://www.facebook.com/friends/?recent&ref=tn';
	
// add text to our link
var recentDivContent = document.createTextNode('Recently Updated');
recentLink.appendChild(recentDivContent);
	
// add link to our div
recentDiv.appendChild(recentLink);


/** For "Status Updates" */
// create our div with class fb_menu_item
var statusDiv = document.createElement('div');
statusDiv.setAttribute('class', 'fb_menu_item');
	
// create our link
var statusLink = document.createElement('a');
statusLink.href = 'http://www.facebook.com/friends/?status&ref=tn';
	
// add text to our link
var statusDivContent = document.createTextNode('Status Updates');
statusLink.appendChild(statusDivContent);
	
// add link to our div
statusDiv.appendChild(statusLink);

We can also add a separator, for having a cleaner look :

/** Creates a separator, just to look good */
var separatorDiv = document.createElement('div');
separatorDiv.setAttribute('class', 'fb_menu_separator');

Finally we insert these 3 nodes before the first child element we got earlier on :

// add all divs before first child of menu
parentNode.insertBefore(statusDiv, firstNode);
parentNode.insertBefore(recentDiv, firstNode);
parentNode.insertBefore(separatorDiv, firstNode);

Just to be on the safe side of things, we surround the whole block of code with a try and catch statement.

Putting it all together

So finally our code is like this :

// ==UserScript==
// @name          My First GM script
// @namespace     https://htmlblog.net
// @description   basic <a href="https://addons.mozilla.org/firefox/addon/748" target="_blank">Greasemonkey</a> script
// @include       http://www.facebook.com/
// ==/UserScript==

DOM = function () {

    function get(id) {
        if (id && typeof id === 'string') {
            id = document.getElementById(id);
        }
        return id || null;
    }

    function walk(element, tag, walk, start, all) {
        var el = get(element)[start || walk], elements = all ? [] : null;
        while (el) {
            if (el.nodeType === 1 && (!tag || el.tagName.toLowerCase() === tag)) {
                if (!all) {
                    return el;
                }
                elements.push(el);
            }
            el = el[walk];
        }
        return elements;
    }

    return {

        // Get the element by its id
        get: get,

        walk: walk,

        // Returns the previousSibling of the Element (excluding text nodes).
        getPrevious: function (el, tag) {
            return walk(el, tag, 'previousSibling');
        },

        // Like getPrevious, but returns a collection of all the matched previousSiblings.
        getAllPrevious: function (el, tag) {
            return walk(el, tag, 'previousSibling', null, true);
        },

        // As getPrevious, but tries to find the nextSibling (excluding text nodes).
        getNext: function (el, tag) {
            return walk(el, tag, 'nextSibling');
        },

        // Like getNext, but returns a collection of all the matched nextSiblings.
        getAllNext: function (el, tag) {
            return walk(el, tag, 'nextSibling', null, true);
        },

        // Works as getPrevious, but tries to find the firstChild (excluding text nodes).
        getFirst: function (el, tag) {
            return walk(el, tag, 'nextSibling', 'firstChild');
        },

        // Works as getPrevious, but tries to find the lastChild.
        getLast: function (el, tag) {
            return walk(el, tag, 'previousSibling', 'lastChild');
        },

        // Works as getPrevious, but tries to find the parentNode.
        getParent: function (el, tag) {
            return walk(el, tag, 'parentNode');
        },

        // Like getParent, but returns a collection of all the matched parentNodes up the tree.
        getParents: function (el, tag) {
            return walk(el, tag, 'parentNode', null, true);
        },

        // Returns all the Element's children (excluding text nodes).
        getChildren: function (el, tag) {
            return walk(el, tag, 'nextSibling', 'firstChild', true);
        },

        // Removes the Element from the DOM.
        dispose: function (el) {
            el = get(el);
            return (el.parentNode) ? el.parentNode.removeChild(el) : el;
        }

    };
}();

try{
	// get drop down menu
	var parentNode = DOM.get('fb_menu_friends_dropdown');
	// get its first child
	var firstNode = DOM.getFirst('fb_menu_friends_dropdown');
	
	/** For "Recently Updated" */
	// create our div with class fb_menu_item
	var recentDiv = document.createElement('div');
	recentDiv.setAttribute('class', 'fb_menu_item');
	
	// create our link
	var recentLink = document.createElement('a');
	recentLink.href = 'http://www.facebook.com/friends/?recent&ref=tn';
	
	// add text to our link
	var recentDivContent = document.createTextNode('Recently Updated');
	recentLink.appendChild(recentDivContent);
	
	// add link to our div
	recentDiv.appendChild(recentLink);

	
	/** For "Status Updates" */
	// create our div with class fb_menu_item
	var statusDiv = document.createElement('div');
	statusDiv.setAttribute('class', 'fb_menu_item');
	
	// create our link
	var statusLink = document.createElement('a');
	statusLink.href = 'http://www.facebook.com/friends/?status&ref=tn';
	
	// add text to our link
	var statusDivContent = document.createTextNode('Status Updates');
	statusLink.appendChild(statusDivContent);
	
	// add link to our div
	statusDiv.appendChild(statusLink);

	/** Creates a separator, just to look good */
	var separatorDiv = document.createElement('div');
	separatorDiv.setAttribute('class', 'fb_menu_separator');
	
	// add both divs before first child of menu
	parentNode.insertBefore(statusDiv, firstNode);
	parentNode.insertBefore(recentDiv, firstNode);
	parentNode.insertBefore(separatorDiv, firstNode);
}
catch(e){};

Installing and testing our script

Fire up Firefox and select “File” -> “Open File…” in the menu bar and then browse for your script, i.e myfirstscript.user.js in our case. You will be prompted to install the script by Greasemonkey. After installation is complete, head to Facebook, login and hover over the “Friends” menu, you will see your newly created menus.

Download script
Install script

WP To Top – a WordPress plugin that takes you to the top

About WP To Top

WP To Top is a WordPress plugin that adds a “Back to top” link in your blog without modifying your template files. This is useful especially if you have long posts or long pages. You will have a nice “Back to top” or whatever-text-you-want link floating at the bottom right/left of your page.

Features

  • Smooth scrolling animation and fade in, fade out effect, powered by the YUI library
  • Customizable options via the admin panel, from the text to the position of the link
  • Works on almost all browsers including IE6 (yes!)

Download

You can get the plugin here

Demo

You have a demo on this page. Just scroll a little bit and you will see a nice “Take me up” link appearing at the bottom of your page.

Installation

  • Extract wptotop.zip in the “/wp-content/plugins/” directory
  • Activate the plugin through the “Plugins” menu in WordPress
  • Go to “Settings” and then “WP To Top” to configure the plugin. The options are self-explanatory and easy to understand.

Thanks

I got the inspiration to write this plugin after having a look at David Walsh’s jQuery topLink Plugin. Thanks to the YUI team for the great YUI library and Semih’s Animated Page Scroll with YUI.

twitree.com – see on which leaves the birds are…

I am happy to announce the launch of twitree.com. It’s a twitter application which allows a twitterer to view his followers in a tree-like navigation, which can be expanded.

twitree makes extensive use of the YUI library. The following components were used :

  • grids css – for the layout
  • yahoo dom event – for DOM and event handling
  • connection manager – for AJAX requests like getting the list of followers for a user
  • container family – to display modal dialogs like prompting for a twitter username, displaying the loading panel
  • JSON utility – to parse the JSON data twitree receives from the twitter servers
  • cookie utility – to fetch username information from a cookie so that the user doesn’t have to input his username every time
  • and finally the treeview control which provides a nice tree and dynamically loads the data upon clicking on a node

The application is very much in an initial phase and there are some limitations, like only 100 followers are being returned, rate limiting, etc…

Stay tuned for further information

Latest update :

twitterers can now

  • update their status via twitree
  • follow a user by right clicking on the user and choose “Follow” from the contextual menu
  • send a direct message to a fellow twitterer by right clicking on the user and choose “Send message” from the contextual menu

FAQ has also been added

Alternate colors to table rows with javascript (YUI)

This article will show you how to add alternate colors to table rows without any server side development or hard coding your scripts, thus providing good readability for your users and great flexibility for you. We will use the YUI library for that.

The HTML

We need to create our basic HTML file containing a table and adding a class name to it.

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
	<head>
		<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
		<title>Alternate colors</title>
	</head>
	<body>
		<table class="alternateRows">
			<tr>
				<td>First Row</td>
			</tr>
			<tr>
				<td>Second Row</td>
			</tr>
			<tr>
				<td>Third Row</td>
			</tr>
			<tr>
				<td>Fourth Row</td>
			</tr>
			<tr>
				<td>Fifth Row</td>
			</tr>
		</table>
	</body>
</html>

The Javascript

Next we need to include the YUI libraries, served from their CDN. We need the

  • YAHOO global object which contains a number of methods that are used throughout the library
  • DOM collection which provides methods that simplify common DOM-scripting tasks, including element positioning and CSS style management
  • Event utility for various event handling
<!-- Combo-handled YUI JS files: --> 
<script type="text/javascript" src="http://yui.yahooapis.com/combo?2.6.0/build/yahoo-dom-event/yahoo-dom-event.js"></script>

And also our javascript file, alternate.js

<script type="text/javascript" src="alternate.js"></script>

So our HTML page looks like this:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
	<head>
		<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
		<!-- Combo-handled YUI JS files: --> 
<script type="text/javascript" src="http://yui.yahooapis.com/combo?2.6.0/build/yahoo-dom-event/yahoo-dom-event.js"></script>
		<script type="text/javascript" src="alternate.js"></script>
		<title>Alternate colors</title>
	</head>
	<body>
		<table class="alternateRows">
			<tr>
				<td>First Row</td>
			</tr>
			<tr>
				<td>Second Row</td>
			</tr>
			<tr>
				<td>Third Row</td>
			</tr>
			<tr>
				<td>Fourth Row</td>
			</tr>
			<tr>
				<td>Fifth Row</td>
			</tr>
		</table>
	</body>
</html>

Invoking the javascript

To add the alternate colors to your table rows, you just have to put the following javascript code in your HTML page.

	YAHOO.util.Event.on(window, 'load', function(){
		YAHOO.htmlblog.alternateRows.init('alternateRows', '#fff', '#ccc');
	});

That means that after the page has been loaded, we call the alternateRows.init function, with 3 parameters:

  • the table class name, in our case it’s “alternateRows”
  • the first color in hex, e.g #fff
  • the second color in hex, e.g #ccc

Our complete HTML page

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
	<head>
		<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
		<!-- Combo-handled YUI JS files: --> 
<script type="text/javascript"src="http://yui.yahooapis.com/combo?2.6.0/build/yahoo-dom-event/yahoo-dom-event.js"></script>
		<script type="text/javascript" src="alternate.js"></script>
		<script type="text/javascript">
			YAHOO.util.Event.on(window, 'load', function(){
				YAHOO.htmlblog.alternateRows.init('alternateRows', '#fff', '#ccc');
			});
		</script>
		<title>Alternate colors</title>
	</head>
	<body>
		<table class="alternateRows">
			<tr>
				<td>First Row</td>
			</tr>
			<tr>
				<td>Second Row</td>
			</tr>
			<tr>
				<td>Third Row</td>
			</tr>
			<tr>
				<td>Fourth Row</td>
			</tr>
			<tr>
				<td>Fifth Row</td>
			</tr>
		</table>
	</body>
</html>

Bubble menu javascript or playing with YUI’s event delegation

bubble
Image courtesy of jobee59’s photostream

Event delegation refers to the use of a single event listener on a parent object to listen for events happening on its children (or deeper descendants). Event delegation allows developers to be sparse in their application of event listeners while still reacting to events as they happen on highly specific targets. This proves to be a key strategy for maintaining high performance in event-rich web projects, where the creation of hundreds of event listeners can quickly degrade performance. More on event delegation including examples.

This post illustrates the use of event delegation to create a “bubble” menu, inspired by Bedrich Rios’ post on Nettuts.

The HTML

We’ll need the following components from the YUI library:

  • yahoo-dom-event – for DOM/Event handling
  • animation – for sliding effects

So our basic HTML page is like this:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
	<head>
		<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
		<!-- Combo-handled YUI JS files: -->
		<script type="text/javascript" src="http://yui.yahooapis.com/combo?2.6.0/build/yahoo-dom-event/yahoo-dom-event.js&2.6.0/build/animation/animation-min.js"></script>
	</head>
	<body>

	</body>
</html>

Next we’ll add some structural markup, a simple list, with “menu” as class name and with 5 items.

<ul class="menu">
	<li>Menu item 1</li>
	<li>Menu item 2</li>
	<li>Menu item 3</li>
	<li>Menu item 4</li>
	<li>Menu item 5</li>
</ul>

Updated HTML page:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
	<head>
		<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
		<!-- Combo-handled YUI JS files: -->
		<script type="text/javascript" src="http://yui.yahooapis.com/combo?2.6.0/build/yahoo-dom-event/yahoo-dom-event.js&2.6.0/build/animation/animation-min.js"></script>
	</head>
	<body>
		<ul class="menu">
			<li>Menu item 1</li>
			<li>Menu item 2</li>
			<li>Menu item 3</li>
			<li>Menu item 4</li>
			<li>Menu item 5</li>
		</ul>
	</body>
</html>

The Javascript

Then we just have to add the bubble.js javascript file to our page and it’s done, we’ll have a nice “bubble” menu.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
	<head>
		<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
		<!-- Combo-handled YUI JS files: -->
		<script type="text/javascript" src="http://yui.yahooapis.com/combo?2.6.0/build/yahoo-dom-event/yahoo-dom-event.js&2.6.0/build/animation/animation-min.js"></script>
		<!-- our bubble file -->
		<script type="text/javascript" src="bubble.js"></script>
	</head>
	<body>
		<ul class="menu">
			<li>Menu item 1</li>
			<li>Menu item 2</li>
			<li>Menu item 3</li>
			<li>Menu item 4</li>
			<li>Menu item 5</li>
		</ul>
	</body>
</html>

bubble.js

/**
*	Bubble Menu
*	@author Asvin Balloo (https://htmlblog.net)
*/
bubbleMenu = {
	// private variables
	// class name of our menu
	menuClassName: null,
	// by how much we are sliding
	moveToLength : 10,
	
	/**
	*	Init the whole process here
	*	@method init
	*	@params menuClassName {string} class name of our menus
	*/
	init: function(menuClassName){
		// set our class name to be used later one
		bubbleMenu.menuClassName = menuClassName;
		
		// get all the lists with the class name
		var list = YAHOO.util.Dom.getElementsByClassName(menuClassName);
		
		// loop through them
		for(var i=0; i<list.length; i++) {
			// add the onmouseover event to it, call the mouseOverHandler function
			YAHOO.util.Event.on(list[i], "mouseover", bubbleMenu.mouseOverHandler);
			
			// get the first child and its padding left for future reference. Useful when using onmouseout to 

restore the item to original state
			var listItems = list[i].getElementsByTagName('li');
			var startingPadding = parseInt(YAHOO.util.Dom.getStyle(listItems[0], 'paddingLeft'));
			
			// add the onmouseout event, call the mouseOutHandler function
			YAHOO.util.Event.on(list[i], "mouseout", bubbleMenu.mouseOutHandler, {'list':list[i], "padding" : 

startingPadding});
		}
	},
	
	/**
	*	Mouse over handler
	*	@params e {Event} the event
	*/
	mouseOverHandler: function(e){
		// get the target
		var elTarget = YAHOO.util.Event.getTarget(e);
		
		// calculate the current padding left
		var paddingLeft = parseInt(YAHOO.util.Dom.getStyle(elTarget, 'paddingLeft'));
		
		// call the delegate method, with the target and the padding left value
		bubbleMenu.delegate(elTarget, paddingLeft);
	},
	
	/**
	*	Mouse out handler
	*	@params e {Event} the event
	*	@params o {JSON} JSON data containing the list + padding value, eg. {'list': mylist, 'padding': 10} 
	*/
	mouseOutHandler: function(e, o){
		// get the target
		var elTarget = YAHOO.util.Event.getTarget(e);
		
		// calculate the left padding, the original padding value - by how much we have padded
		var paddingLeft = o.padding - bubbleMenu.moveToLength;
		
		// call the delegate method with the target and padding value
		bubbleMenu.delegate(elTarget, paddingLeft);
	},
	
	/**
	*	Cool things go here, like animation
	*	@params elTarget {Target} our target
	*	@params padding {Int} by how much we are going to padd
	*/
	delegate: function(elTarget, padding){
		//walk up the DOM tree looking for an <li> in the target's ancestry; desist when you reach the container with our class name
		while (elTarget.className != bubbleMenu.menuClassName) {
			// are you an LI?
			if(elTarget.nodeName.toUpperCase() == "LI") {
				// if so then animate, set the attributes, in our case the left padding
				var attributes = { paddingLeft: {"to": [padding + bubbleMenu.moveToLength] } };
				
				// create our animation
				var anim = new YAHOO.util.Motion(elTarget, attributes, 0.6, YAHOO.util.Easing.bounceOut);
				
				// then animate
				anim.animate();
				break;
			}
			else {
				// it's not an li, so we keep looking and looking
	            elTarget = elTarget.parentNode; 
	        } 
		}
	}
};

// init the whole thing here
YAHOO.util.Event.on(window, "load", function(){
	bubbleMenu.init("menu");
});

You can have unlimited (don’t abuse) number of bubble menus by just adding the “menu” class to each one of them and including the bubble.js file.
If you’re not happy with the menu class or you found another cool class name, then you’ll have to update the bubble.js file towards the end:

// init the whole thing here
YAHOO.util.Event.on(window, "load", function(){
	bubbleMenu.init("mycoolclass");
});

Also you can modify by how much an item slides by modifying the moveToLength variable (default 10):

// by how much we are sliding
moveToLength : 30,

Note that the demo is using reset-fonts-grids CSS and the page has been styled with the following:

body {
	margin: 0;
	padding: 0;
	background: #1d1d1d;
	font-family: "Lucida Grande", Verdana, sans-serif;
	font-size: 100%;
}
			
.menu li{
	padding-left: 20px;
	line-height: 2em;
	width: 150px;
	color: #999;

	background: #222;
	border: 1px solid #1a1a1a;
	-moz-border-radius: 10px;
}

8 practical tips to make your web pages faster

faster web pages

Yahoo’s Exceptional Performance team has compiled a list of 34 best practices to have faster web pages. In this post I’ll show you 8 tips which helped me to get a B grade in YSlow! for web pages that I develop. Currently YSlow’s web page analysis is based on 13 identified basic rules that affect web page performance. In decreasing order of priority they are:

  1. Make Fewer HTTP Requests
  2. Use a Content Delivery Network
  3. Add an Expires Header
  4. Gzip Components
  5. Put CSS at the Top
  6. Move Scripts to the Bottom
  7. Avoid CSS Expressions
  8. Make JavaScript and CSS External
  9. Reduce DNS Lookups
  10. Minify JavaScript
  11. Avoid Redirects
  12. Remove Duplicate Scripts
  13. Configure ETags

You can get more info by clicking on each item. Of course using a CDN is not affordable by everybody, including me. This didn’t prevent me to have a B grade though. I will guide you through some of the items which worked for me.


Make Fewer HTTP Requests
If you’re using the YUI library, the good news is that recently the team graciously offered a Combo Handler Service, served from their CDN. This helps us to eliminate HTTP requests by requesting a single file. For example, to use the Rich Text editor, it needed 6 separate HTTP requests:

 
 
 

 

Now with the Combo Handler service, it has been stripped to only 1 file:


If you’re not using the YUI library or not interested in using the Combo Handler service, you can try Minify!
Minify is a PHP5 app that can combine multiple CSS or Javascript files, compress their contents (i.e. removal of unnecessary whitespace/comments), and serve the results with HTTP encoding (gzip/deflate) and headers that allow optimal client-side caching.

Another tool which can be helpful is Dojo Shrinksafe which not only minifies your javascript code but also allows you to upload several javascript files and have them compressed in 1 single file.

Another way to make fewer HTTP requests is by using CSS sprites which are big images containing lots of smaller ones. An online tool which can make CSS sprites out of your smaller ones is the CSS Sprite Generator. You just have to upload your pics and it will generate your sprite together with the CSS code. More info about CSS sprites can be found on CSS Tricks.


Add an Expires Header
For this to work on Apache, you’ll need to have mod_expires enabled. Then create an .htaccess file or edit your httpd.conf with the following content:

ExpiresActive On
ExpiresByType image/jpg "access plus 2 years"
ExpiresByType image/jpeg "access plus 2 years"
ExpiresByType image/gif "access plus 2 years"
ExpiresByType application/javascript "access plus 2 years"
ExpiresByTYpe text/css "access plus 2 years"

In the above listing, all images(jpg/jpeg/gif) will have an expires header 2 years in the future.


Gzip Components
You must have mod_deflate enabled and drop these lines in your httpd.conf file

SetOutputFilter DEFLATE
SetEnvIfNoCase Request_URI \.(?:gif|jpe?g|png)$ no-gzip dont-vary
SetEnvIfNoCase Request_URI \.(?:exe|t?gz|zip|bz2|sit|rar)$ no-gzip dont-vary
SetEnvIfNoCase Request_URI \.pdf$ no-gzip dont-vary

Put CSS at the Top
All CSS must be between your head tag, like that :

	
		
			
		
		
		
		
	

Move Scripts to the Bottom
While the CSS are sitting at the top of your page, javascripts must be moved towards the lower part of your page, typically in the body tag. I usually put them before the closing body tag.

	
		
		
		
		...
		...
		
		
	

Reduce DNS Lookups
I don’t usually have more than 2 DNS lookups in my scripts, else YSlow will be removing some points.


Minify JavaScript
As I mentioned earlier on, Dojo Shrinksafe is an online tool which minifies your code.

Another great command line tool is Julien Lecomte YUI Compressor.

In order to use it you must have Java installed (>= 1.4).

Douglas Crockford also has something similar in JSMin


Configure ETags
Just drop this line in your httpd.conf:

FileETag None

😉

Pixidou – an Open Source AJAX Image Editor (PHP and YUI based)

Pixidou is a new open source AJAX image editor which will allow you to :

  • adjust brightness
  • adjust contrast
  • cropping
  • flipping
  • negative
  • resizing
  • rotating
  • tint

images.

What you need to get running

  • PHP5.2+ installed on your webserver
  • GD2
  • a web browser

Demo

Installation
Unzip/untar the software in your web directory and that’s all 😉
Also ensure the images folder is writable by your webserver.
Have fun !!!

Download
Get it and follow it at github

All the image manipulation is PHP-based with the help of the great class.upload.php class by Colin Verot which I highly recommend.

The frontend is the most interesting part, where I’ve use the one and only YUI library. Here are the component I used (served from Yahoo CDN) :

  • reset-fonts.css – for all resetting, style information
  • utilities – for all AJAX, animation, drag drop, event handling stuff
  • container – for all panels, dialogs, alerts
  • menu – for the top navigation menu together with the submenus
  • button – provides nice buttons
  • slider – for adjusting contrast and brightness
  • color picker – to choose the color to tint the image
  • resize – to resize the image
  • image cropper – image cropping utility
  • json – to parse all JSON data returned from PHP scripts
  • layout – the general layout
  • tabview – for the about information

I’ve not used the grids.css since it caused some problems while using the color picker.

It’s only the beginning of Pixidou and I’am currently looking for some collaborators. So, if you think you can give a helping hand in any way to Pixidou, don’t hesitate to contact me (asvin.balloo [@] gmail.com), I’ll happily add you as a contributor to the repository on github.

Since it’s an early version, there must be some bugs lying everywhere, don’t hesitate to drop me an email.

AJAX image cropper with YUI and PHP

This post will show you how to build an AJAX crop image tool using the image cropper control from YUI library and PHP.


The ImageCropper Control from the YUI library gives you an interactive interface for getting the dimensions to crop an image and using these dimensions in PHP, we can do some cropping.

The script we are going to build will

  • allow users to upload an image via AJAX
  • then allow them to select an area for cropping
  • lastly, provide a download link for the cropped image.

There are 3 files we are going to use

  • index.php – will contain the form for image upload as well as the cropping interface
  • upload.php – provides uploading functionality
  • crop.php – provides cropping functionality

From a technical point of view, the flow will be like this :

  1. user uploads jpg image (index.php)
  2. index.php then posts the image asynchronously to upload.php which will do the upload on the server, returning JSON data containing the image file name, its width and its height.
  3. with the JSON data and innerHTML we put the image in our page
  4. initialize the javascript cropping tool
  5. generate a download link (crop.php)


Let’s have a look at index.php

The index.php is our main file where users will be able upload images and then download the cropped ones.

We’ll need the following components from the YUI library :

  • yahoo-dom-event.js – for DOM manipulation and Event handling
  • dragdrop – dependency for the image cropper control
  • element – dependency for the image cropper control
  • resize – dependency for the image cropper control
  • connection – for AJAX requests, in our case for image uploads via AJAX
  • json – for parsing JSON data
  • imagecropper – our most important control

Of course we’ll use Yahoo combo handling and add the js to our page along with the CSS needed for the above controls :

<link rel="stylesheet" type="text/css" href="http://yui.yahooapis.com/2.5.2/build/resize/assets/skins/sam/resize.css" />
<link rel="stylesheet" type="text/css" href="http://yui.yahooapis.com/2.5.2/build/imagecropper/assets/skins/sam/imagecropper.css" />
<!-- js -->
<script type="text/javascript" src="http://yui.yahooapis.com/combo?2.5.2/build/yahoo-dom-event/yahoo-dom-event.js&2.5.2/build/dragdrop/dragdrop-min.js&2.5.2/build/element/element-beta-min.js&2.5.2/build/resize/resize-beta-min.js&2.5.2/build/imagecropper/imagecropper-beta-min.js&2.
5.2/build/connection/connection-min.js&2.5.2/build/json/json-min.js"></script>

Next users must be able to upload images via AJAX, so we add a form to our page:

<form action="upload.php" enctype="multipart/form-data" method="post" name="uploadForm" id="uploadForm">
	Image : <input type="file" name="uploadImage" id="uploadImage" />
	<input type="button" id="uploadButton" value="Upload"/>
</form>

We have an onclick event to the upload button to fire the uploading process.

// add listeners
YAHOO.util.Event.on('uploadButton', 'click', uploader.carry);

We’ll also need 2 containers :

  • imageContainer – will contain our uploaded image
  • downloadLink – will contain the download link
<div id="imageContainer"></div>
<div id="downloadLink"></div>

Both containers will be updated via innerHTML afterwards.

AJAX upload

For the AJAX upload, Code Central

has an excellent tutorial which I highly recommend. I took the code sample and modified it a bit to fit my needs. Finally I came up with a nice JSON object called uploader which has one method, carry. The latter just posts form data to a specified URL.

uploader = {
	carry: function(){
		// set form
		YAHOO.util.Connect.setForm('uploadForm', true);
		// upload image
		YAHOO.util.Connect.asyncRequest('POST', 'upload.php', {
			upload: function(o){
				// parse our json data
				var jsonData = YAHOO.lang.JSON.parse(o.responseText);
				
				// put image in our image container
				YAHOO.util.Dom.get('imageContainer').innerHTML = '<img id="yuiImg" src="' + jsonData.image + '" width="' + jsonData.width + '" height="' + jsonData.height + '" alt="" />';
						
				// init our photoshop
				photoshop.init(jsonData.image); 
							
				// get first cropped image
				photoshop.getCroppedImage();
			}
		});
	}
};

When upload is complete, we get the JSON data we mentioned earlier on. For e.g :

{"image" : "images/myimage.jpg", "width" : "500", "height" : 400}

With this data and using innerHTML we update our imageContainer div to put our image which will have yuiImg as id :

YAHOO.util.Dom.get('imageContainer').innerHTML = '<img id="yuiImg" src="' + jsonData.image + '" width="' + jsonData.width + '" height="' + jsonData.height + '" alt="" />';

It’s very important to specify the image width and height else the image cropper won’t work.
Next we initialize another JSON object, photoshop which we’ll have a look now.

Our photoshop object

photoshop = {
	image: null,
	crop: null,
	
	init: function(image){
		// set our image
		photoshop.image = image;
					
		// our image cropper from the uploaded image					
		photoshop.crop = new YAHOO.widget.ImageCropper('yuiImg');
		photoshop.crop.on('moveEvent', function() {
			// get updated coordinates
			photoshop.getCroppedImage();
		});
	},
	
	getCroppedImage: function(){
		var coordinates = photoshop.getCoordinates();
		var url = 'crop.php?image=' + photoshop.image + '&cropStartX=' + coordinates.left +'&cropStartY=' + coordinates.top +'&cropWidth=' + coordinates.width +'&cropHeight=' + coordinates.height;
		YAHOO.util.Dom.get('downloadLink').innerHTML = '<a href="' + url + '">download cropped image</a>';		

	},

	getCoordinates: function(){
		return photoshop.crop.getCropCoords();
	}
};

The init function iniatializes the YUI image cropper from the uploaded image which has yuiImg as id.

photoshop.crop = new YAHOO.widget.ImageCropper('yuiImg');

We also subscribe to the moveEvent for the cropper since we’ll need to update the download link for the cropped image. So whenever the image cropper is moved/resized, we call the getCroppedImage function.

photoshop.crop.on('moveEvent', function() {
	// get updated coordinates
	photoshop.getCroppedImage();
});

The getCroppedImage function will generate the download link for the cropped image. To do image cropping in PHP we’ll need

  • the image we want to crop
  • the X,Y coordinates
  • height and width of the to be cropped area

Fortunately the YUI cropper utility has a function which will give us what we want, it’s the getCropCoords() method. So, whenever the getCroppedImage function is called, we get the coordinates of the cropped area, build a URL and finally put the download link in our downloadLink container.

// get coordinates
var coordinates = photoshop.getCoordinates();

// build our url
var url = 'crop.php?image=' + photoshop.image + '&cropStartX=' + coordinates.left +'&cropStartY=' + coordinates.top +'&cropWidth=' + coordinates.width +'&cropHeight=' + coordinates.height;

// put download link in our page
YAHOO.util.Dom.get('downloadLink').innerHTML = '<a href="' + url + '">download cropped image</a>';

This is all we need for the index page.

upload.php

if(!empty($_FILES["uploadImage"])) {
  	// get file name
	$filename = basename($_FILES['uploadImage']['name']);
		
	// get extension
  	$ext = substr($filename, strrpos($filename, '.') + 1);
  		
  	// check for jpg only
  	if ($ext == "jpg") {
      		// generate unique file name
  		$newName = 'images/'.time().'.'.$ext;
  			
  		// upload files
        	if ((move_uploaded_file($_FILES['uploadImage']['tmp_name'], $newName))) {
			
        		// get height and width for image uploaded
        		list($width, $height) = getimagesize($newName);
        		
        		// return json data
           		echo '{"image" : "'.$newName.'", "height" : "'.$height.'", "width" : "'.$width.'" }';
        	}
        	else {
           		echo '{"error" : "An error occurred while moving the files"}';
        	}
  	} 
  	else {
     		echo '{"error" : "Invalid image format"}';
  	}
}	

The upload.php file too is self explanatory, we check for a jpg image only, then generate an unique filename, put it in the images folder and finally build json data which we’ll use for DOM manipulation. Of course the images folder must be writable by the web server.

crop.php

// get variables
$imgfile = $_GET['image'];
$cropStartX = $_GET['cropStartX'];
$cropStartY = $_GET['cropStartY'];
$cropW = $_GET['cropWidth'];
$cropH = $_GET['cropHeight'];

// Create two images
$origimg = imagecreatefromjpeg($imgfile);
$cropimg = imagecreatetruecolor($cropW,$cropH);

// Get the original size
list($width, $height) = getimagesize($imgfile);

// Crop
imagecopyresized($cropimg, $origimg, 0, 0, $cropStartX, $cropStartY, $width, $height, $width, $height);

// force download nes image
header("Content-type: image/jpeg");
header('Content-Disposition: attachment; filename="'.$imgfile.'"');
imagejpeg($cropimg);

// destroy the images
imagedestroy($cropimg);
imagedestroy($origimg);

Crop.php allows us to crop our uploaded image. First we get all the variables passed to us via the AJAX request,

// get variables
$imgfile = $_GET['image'];
$cropStartX = $_GET['cropStartX'];
$cropStartY = $_GET['cropStartY'];
$cropW = $_GET['cropWidth'];
$cropH = $_GET['cropHeight'];

Then we create 2 images, the original one and the cropped one, and use the imagecopyresized function to generate the cropped image. We add some header information to tell the world it’s an image and prompt for a save dialog.

Show/hide containers by only adding CSS classes without writing javascript code (YUI-based)

This post will show you how to add the collapse/expand functionality in your HTML pages, perfect for FAQs by only adding two class names to your existing page and including the scripts below. Of course it’s all YUI-based and also a demo of the fantastic YUI selector utility.

What does the script do ?

OK, suppose you have a FAQ page where you have traditional anchor methods, i.e you click on the question and then you go to the bottom of the page to get the answer or you get redirected to another page. But now your users are complaining your site’s not trendy enough and want to have the answer displayed immediately after clicking the question.

The solution.

After downloading the javascript file, include it at the bottom of your page with the following HTML code:

<script type="text/javascript" src="http://yui.yahooapis.com/combo?2.5.2/build/yahoo-dom-event/yahoo-dom-event.js&2.5.2/build/selector/selector-beta-min.js"></script> 
<script type="text/javascript" src="js/toggler.js"></script>

We need to include the selector utility which will be used to retrieve a collection of elements that match a given CSS selector. For e.g if we want to retrieve all div nodes in HTML page having the class “foo”, we can write :

var myNodes = YAHOO.util.Selector.query('div.foo');

The selector is used in the toggler.js file.

After adding the scripts, now you have to add CSS classes to your containers. For example for the FAQ, for questions (or main, call it whatever you want), you add the class “htmlblog-main” and for the answers (or sub), add the “htmlblog-sub” class.

Before :

<div>1. Question</div>
<div>Answer1</div>
<div>2. Question 2</div>
<div>Answer 2</div>

After :

<div class="htmlblog-main">1. Question</div>
<div class="htmlblog-sub">Answer1</div>
<div class="htmlblog-main">2. Question 2</div>
<div class="htmlblog-sub">Answer 2</div>

If you already had a class for that container, no problem you can add the htmlblog-main or htmlblog-sub class :

<div class="myclass htmlblog-main">1. Question</div>

What next ?

That’s all ! All your answer containers will be hidden and questions will have a [+] besides them. Pretty easy

Am I obliged to use the htmlblog-main and html-sub classes ? And I don’t want to use div tags, I want li tags.

Don’t worry, if you don’t like the class names you can put whatever you want but you’ll have to edit the toggler.js file at the following lines :

YAHOO.util.Event.addListener(window, 'load', function(){
	YAHOO.htmlblog.init('div', 'htmlblog-main', 'htmlblog-sub');
});

For e.g, if you’re using li tags instead of divs with the “myMainClass” as main and “mySubClass” as sub, the code goes :

YAHOO.util.Event.addListener(window, 'load', function(){
	YAHOO.htmlblog.init('li', 'myMainClass', 'mySubClass');
});

I’am getting an alert saying “Number of main and subs incorrect”.

That means the number of main/subs are not the same. You need to have the number of the main class equals the number of the sub class.