Javascript Zombies

Javascript examples tend to zombify quite fastly. It’s pretty easy to make up a page for showing something, but very soon that page becomes unusable. I’m constantly finding many broken javascript examples in the internet.

And the semantic of a broken javascript example is a logic problem nobody can solve. Is it broken by accident or on purpose? Was it already broken when created? Who, what, where, when did it break? Is it a browser test? Am I doing something wrong?

Maybe we should refrain from presenting online examples.

SPAM is not that bad

Every day SPAM comments remind me that my blog comment feature is properly working even if almost none of my readers is taking time to reply to my posts.

Metaobjects 1.1 Released Today

The Metaobjects plugin is going to become a jQuery official plugin, starting from the next jQuery 1.1 release, due out in a week or so. Meanwhile I’ve come up with some improvements that I’m going to share with you, releasing a new version of Metaobjects.

Changes

  • Added a new option for selecting the metaobjects to process. The option name is “selector” and its value is a jQuery selector, which defaults to “object.metaobject”, i.e. an OBJECT element with a ‘metaobject’ class (the metaobject definition).

Example: process metaobjects selected by “object.bar”

$.metaobjects({selector: 'object.bar'});
  • Added the concept of metaparam for configuring options local to a metaobject. If a PARAM element is given the ‘metaparam’ id, then it is treated as a configuration element, not a metadata element. Either one or none of the PARAM elements can be a metaparam. Its name is needed but not used. A good choice could be ‘options’ or ‘configuration’, anyway the ‘metaparam’ id garantees that it won’t clash with a metadata name you might need.
  • Fixed a bug that didn’t allow metaobjects to add metadata to elements without a closing tag, like images (thanks to John Resig for pointing it out). Now this is possible by means of the ‘target’ metaparam option, whose value is a jQuery selector, which defaults to the parent of the metaobject. The selector takes the document as its context.

Example: add this metadata to all the elements selected by “img.foo”

<div><object class="metaobject bar">
	<param name='options' value='{target: "img.foo"}' id='metaparam' />
	<param name='title'   value='"What a Foo!"' />
</object></div>

Code of Metaobjects 1.1

/* 
=============================================================================== 
Metaobjects is a jQuery plugin for setting properties of DOM elements  by means 
of metaobjects (OBJECT elements with a 'metaobject' class) 
...............................................................................                                                
                                               Copyright 2007 / Andrea Ercolino 
------------------------------------------------------------------------------- 
LICENSE: http://www.opensource.org/licenses/mit-license.php UPDATES: 
http://noteslog.com/metaobjects/ 
=============================================================================== 
*/

/** 
 * The metadata is added to the XHTML page by means of metaobjects whose PARAM  
 * elements define name/value pairs. The given 'value' attribute is evaluated and  
 * added to the metaobject's parent as a property with the given 'name' attribute.  
 * Finally the metaobject is removed from the DOM. 
 * 
 * Is possible to configure the target of a metaobject by means of a metaparam, 
 * i.e. a PARAM element with a 'metaparam' id, (one for each metaobject). The name 
 * of the metaparam is required, but currently not used, so it can be anything not 
 * null, like 'options'. The value of the metaparam must be an object like this: 
 * {target: selector}. The selector is a jQuery selector used for finding the 
 * target inside the document. For example this is used for targeting all the 
 * images in the current document
 * 
 * <param id='metaparam' name='options' value='{target: "img"}'>
 *  
 * @signature 
 * |* jQuery *| $.metaobjects(  
 *   |* Object *| options = { 
 *     |* Element *| context: document,  
 *     |* Boolean *|   clean: true,
 *     |* String *| selector: "object.metaobject" 
 *   }  
 * ) 
 * 
 * @type  
 *   jQuery 
 * @name 
 *   $.metaobjects 
 * @param 
 *   Object options = {context: document, clean: true, selector: "object.metaobject"} 
 * @option  
 *     Element context The context where the metaobjects are 
 * @option  
 *     Boolean   clean True means 'remove metaobjects after processing' 
 * @option  
 *     String selector The jQuery selector used for finding metaobjects to process
 * @cat  
 *   Plugins/Metadata 
 * 
 * @example  
 *   $.metaobjects(); 
 * @desc  
 *   load meta data from all of the meta objects in the document and remove them 
 *  
 * @before  
 * <html><head><title>Hi There</title> 
 * ... 
 * <script type="text/javascript"> 
 * $( function() {  
 *   $.metaobjects();  
 *   var p1 = $('#one')[0]; 
 *   $( 'body' ) 
 *     .append(  
 *         '<p>'  
 *       + 'width = ' + p1.meta_size.width 
 *       + '<br />' 
 *       + 'height = ' + p1.meta_size.height 
 *       + '</p>' 
 *     ); 
 * } ); 
 * </script> 
 * </head><body> 
 * <p id="one"> 
 *   <object class="metaobject"> 
 *     <param name  = 'meta_size'  
 *            value = '{ width: 400, height: "auto" }' /> 
 *     <param name  = 'title'  
 *            value = 'document.title' /> 
 *   </object> 
 *   Hello World 
 * </p> 
 * </body></html> 
 *  
 * @after 
 * <html><head><title>Hi There</title> 
 * ... 
 * <script type="text/javascript"> 
 * ... 
 * </script> 
 * </head><body> 
 * <p id="one" title="Hi There"> 
 *   Hello World 
 * </p> 
 * <p> 
 * width = 400 
 * <br/> 
 * height = auto 
 * </p> 
 * </body></html> 
 *  
 * @author Andrea Ercolino (http://noteslog.com/) 
 * @version 1.1  
 */ 

( function($) {
	$.metaobjects = function( options ) {

		function getParam( elem ) {
			var $param = $( elem );
			var pName = $param.attr( 'name' );
			if ( '' == pName) return null;
			var pValue = $param.attr( 'value' );
			var data;
			eval( 'data = ' + pValue + ';' );
			return { name: pName, value: data };
		}

		var global_settings = { 
			  context: document
			, clean: true
			, selector: 'object.metaobject' 
		};
		$.extend( global_settings, options );
		var $metaobjects = $( global_settings.selector, global_settings.context );
		$metaobjects.each( function() {
			var settings = {};
			$( '> param#metaparam', this )
				.each( function() {

					var p = getParam( this );
					if( ! p ) return;
					$.extend( settings, p.value );

				} );
			var $target;
			if( settings.target ) {
				$target = $( settings.target );
				if( 0 == $target.length ) {
					return;
				}
			}
			else {
				$target = $( this.parentNode );
			}
			$( '> param', this )
				.not( '#metaparam' )
				.each( function() {
				
					var p = getParam( this ); 
					if( ! p ) return;
					$.map( $target.get(), function( elem ) {
						elem[ p.name ] = p.value;
						return elem;
					} );

				} );
			if( global_settings.clean ) {
				$( this ).remove();
			}
		} );
		return $metaobjects;
	} 
} ) ( jQuery );
Tests

These tests show what happens when the metaobjects() function is called with different options on the same page. (only the name of the file changes)

  • test.hml
  • test1.hml
  • test2.hml
  • test3.hml

XHTML page

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3c.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html><head><title>Hi There</title>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<script type="text/javascript" src="jquery-891.pack.js"></script>
<script type="text/javascript" src="metaobjects.js"></script>
<script type="text/javascript" src="setup.js"></script>
<style type="text/css">
p#comment {
	border: 1px dashed silver;
	padding: 10px;
}
</style>
</head><body>

<p id="one">
	<object class="metaobject">
		<param name='meta_size' value='{ width: 400, height: "auto" }' />
		<param name='title'     value='document.title' />
	</object>
	Hello World
</p>

<p><img class="foo" src="jquery-icon.png" alt="jquery icon" /></p>

<p id="two">
	<object class="metaobject bar">
		<param name='meta_size' value='{ width: "auto", height: 300 }' />
		<param name='title'     value='"Goodbye!"' />
	</object>
	See you soon
</p>

<div><object class="metaobject bar">
	<param name='options' value='{target: "img.foo"}' id='metaparam' />
	<param name='title'   value='"What a Foo!"' />
</object></div>

<p id="comment"></p>

<p>
	<a href="http://validator.w3.org/check?uri=referer"><img
		src="http://www.w3.org/Icons/valid-xhtml10"
		alt="Valid XHTML 1.0 Strict" height="31" width="88" /></a>
</p>

</body></html>

setup.js

$( function() { 
	var loc = document.location.href;
	$( '#comment' ).html( "what happens if $.metaobjects() is not called" );

	if( loc.indexOf( "test1.html" ) >= 0 ) {
		$( '#comment' ).html( "what happens after calling: $.metaobjects()" );
		$.metaobjects();
//		$.metaobjects({selector: 'object.bar'});
//		$.metaobjects({clean: false}).hide(); 
		var p1 = $('#one')[0];
		var p2 = $('#two')[0];
		$( 'body' )
			.append( '<p>' + 'width = ' + p1.meta_size.width + '<br />'
				+ 'height = ' + p1.meta_size.height + '</p>'
			)
			.append( '<p>' + 'width = ' + p2.meta_size.width + '<br />'
				+ 'height = ' + p2.meta_size.height + '</p>'
			)
		;
	}

	if( loc.indexOf( "test2.html" ) >= 0 ) {
		$( '#comment' ).html( "what happens after calling: $.metaobjects({selector: 'object.bar'});" );
//		$.metaobjects();
		$.metaobjects({selector: 'object.bar'});
//		$.metaobjects({clean: false}).hide(); 
		var p1 = $('#one')[0];
		var p2 = $('#two')[0];
		$( 'body' )
//			.append( '<p>' + 'width = ' + p1.meta_size.width + '<br />'
//				+ 'height = ' + p1.meta_size.height + '</p>'
//			)
			.append( '<p>' + 'width = ' + p2.meta_size.width + '<br />'
				+ 'height = ' + p2.meta_size.height + '</p>'
			)
		;
	}

	if( loc.indexOf( "test3.html" ) >= 0 ) {
		$( '#comment' ).html( "what happens after calling: $.metaobjects({clean: false}).hide();" );
//		$.metaobjects();
//		$.metaobjects({selector: 'object.bar'});
		$.metaobjects({clean: false}).hide(); 
		var p1 = $('#one')[0];
		var p2 = $('#two')[0];
		$( 'body' )
			.append( '<p>' + 'width = ' + p1.meta_size.width + '<br />'
				+ 'height = ' + p1.meta_size.height + '</p>'
			)
			.append( '<p>' + 'width = ' + p2.meta_size.width + '<br />'
				+ 'height = ' + p2.meta_size.height + '</p>'
			)
		;
	}


} );