Mar 18 2008
Metaobjects 1.5 Released
Changes
- Added support for easy namespacing
- Added support for jQuery 1.2.3 cache
- Rewritten the manual from scratch
Download
You can download metaobjects from Google Code
I'd like there to be no software patents at all,
and to back off from the ones that have been issued (Ewald Detjens)
Mar 18 2008
You can download metaobjects from Google Code
Feb 22 2007
I’ve recently realized that copying to the clipboard a snippet highlighted by Chili works differently in Internet Explorer (IE) and in Mozilla Firefox (FF). They both copy two versions of the selected section: TEXT and HTML.
The net result of all this is that IE does the right thing with HTML while FF does the right thing with TEXT, and neither is completely right or wrong.
UPDATE (2007/02/24): What follows is obsolete, because I’ve implemented a transparent method in Chili 1.7
In Chili 1.6 I’ve implemented a workaround for IE, so that IE TEXT is like FF TEXT. It’s a workaround because you need to add a button to each highlighted section, and it will copy all the text in that section when clicked.
The button can be as simple as a DIV placed before the PRE, like this:
<div class="ie_copy" title="copy this snippet to the clipboard">
copy all
</div>
<pre><code class="javascript">
alert( "Hello World!" );
</code></pre>
Chili will hide any element with a class ie_copy in any browser but IE, where instead it will associate a click handler to it, for copying all the next PRE content as a properly formatted section. You can style the button element as you like, because Chili uses the class only for accessing the element.
You can also place the button wherever you like. If so, i.e. if the button element is not the element preceding the PRE section, then you have to feed Chili the method for getting the PRE content. You can do it globally or locally.
When Chili associates the handler to the button, it will look for a getPRE method inside the button element. If there is one, then it will be used, else Chili will look for a getPRE method inside the global ChiliBook object. If there is one, then it will be used, else Chili will give up and not associate a handler.
You can change the global behavior by changing the declaration of the getPRE method inside the ChiliBook object, and you can change the local behavior by adding a chili metaobject (an object with a class chili) inside the button element, with a getPRE param whose value is the function declaration.
The getPRE function shipped with Chili 1.6 is the following, where the button element is passed into this and the PRE destination is found starting from that origin:
function() { return $( this ).next( "pre" )[0]; }
A button element after the PRE element could then be locally configured like this:
<pre><code class="javascript">
alert( "Hello World!" );
</code></pre>
<div class="ie_copy" title="copy this snippet to the clipboard">
<object class="chili">
<param name="getPRE" value="function() { return $( this ).prev( 'pre' )[0]; }"/>
</object>
copy all
</div>
Jan 26 2007
Changes
Files
Jan 07 2007
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.
Example: process metaobjects selected by “object.bar”
$.metaobjects({selector: 'object.bar'});
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>
/*
===============================================================================
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 );
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)
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>'
)
;
}
} );
Jan 04 2007
Metaobjects is a jQuery plugin for setting properties of DOM elements by means of metaobjects (OBJECT elements with a ‘metaobject’ class).
There is an official Metadata plugin which lets you add metadata to elements by means of three alternative methods, always providing a JSON object to plug the data in:
<p id="one" class="some_class {width: 400, height: 'auto'}">
Hello World
</p>
<p id="one" class="some_class" data="{width: 400, height: 'auto'}">
Hello World
</p>
<p id="one" class="some_class">
<script>
{width: 400, height: 'auto'}
</script>
Hello World
</p>
To access a piece of metadata, you write something like this
$( function() {
var p1 = $('#one')[0];
$( 'body' )
.append(
'<p>'
+ 'width = ' + p1.width
+ '<br />'
+ 'height = ' + p1.height
+ '</p>'
);
} );
Each method available in the Metadata plugin has a drawback.
My metaobjects.js plugin tries to solve all those problems introducing metaobjects, i.e. OBJECT elements with a ‘metaobject’ class.
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.
One of the best positions to place the call to the metaobjects() function is inside the jQuery “document ready” setup, possibly before any use of a clean DOM.
Here is how you make jQuery load metadata and clean the DOM afterwards
$( function() {
$.metaobjects();
...
} );
<html><head><title>Hi There</title>
...
</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>
<html><head><title>Hi There</title>
...
</head><body>
<p id="one" title="Hi There">
Hello World
</p>
</body></html>
To access a piece of metadata, you write something like this
$( function() {
var p1 = $('#one')[0];
$( 'body' )
.append(
'<p>'
+ 'width = ' + p1.meta_size.width
+ '<br />'
+ 'height = ' + p1.meta_size.height
+ '</p>'
);
} );
/* jQuery */ $.metaobjects(
/* Object */ options = {
/* Element */ context: document,
/* Boolean */ clean: true
}
)The metaobjects() function gets a JSON options argument, with members context and clean. context is a DOM element whose metaobjects you want to process. clean is a boolean which tells wether the metaobjects are to be removed (true) or not (false). options is an optional argument and it defaults to { context: document, clean: true }
The metaobjects() function returns an array containing processed metaobjects (DOM elements) belonging to context. If clean was true, then the metaobjects returned have been removed from the DOM. Else if clean was false, you have a chance to hide() them (e.g. if you didn’t add a CSS definition like “.metaobject {display: none;}”)
Here is how you make jQuery load metadata without cleaning the DOM afterwards. In this case you also hide() the objects (in IE you need to, but in FF they’re already hidden)
$( function() {
$.metaobjects( {clean: false} ).hide();
...
} );
(see: http://noteslog.com/?p=100)
There is a bug (#750) in jQuery 1.0.4 (Rev 696) that makes this plugin not usable. I’ve found a simple fix, though.
Find the following snippet in the source (lines 49-56)
// See if an extra function was provided
var fn = arguments[ arguments.length - 1 ];
// If so, execute it in context
if ( fn && typeof fn == "function" )
this.each(fn);
return this; and make it like this one // See if an extra function was provided
var fn = arguments[ arguments.length - 1 ];
// If so, execute it in context
if ( fn && typeof fn == "function" && fn.nodeType == undefined )
this.each(fn);
return this;