yusef .ui and filter for markuper
Monday, November 23, 2009 12:06:54 AM
The yusef toolkit example app comes with the ui plugin. This plugin is a big wrapper around markuper, intercepting calls to yusef's request handler, to apply a template to any page on the fly. In this post, I discuss some basic usage of ui, the inner working of its hooking into yusef, and markuper filters.
Using UI
Using ui is demonstrated in the yusef example app: there is nothing to do, except for exporting the content inside the content property of a hash. However, when trying to extend this behavior to other sections of an application, it simply won't work. This is because by default, ui applies its templates only to the 'content' and _index sections (no, it's not a bug, it's a feature!). To enable it on any page, one must add the 'ui' option in his/her listener, as follows:
An easy way to give users access to different sections of your application is to create tabs. The ui plugin also provides this through the ui.generateTabs function. The parameters are an array of hashes, each describing one tab, and the current connection. Tabs are described by the following parameters:
Once generated through the generateTabs functions, tabs must be exported by the listener as the tabs proerty of an hash, as follows1:
UI hooking
For those interested in the inner working of ui, and most notably how it intercepts the page handling, here is how it works. Yusef offers an extend method, basically allowing to override any of yusef core function. This extend function requires as arguments the name of a function to be overridden and a new, overriding, function. To this new function, yusef.extend adds a "proceed" method which allows the new function to call the overriden one. This allows for a call chain from the extending function to the (possibly many) extended function, towards the original yusef core method.
In particular, ui extends the Yusef.addSectionListener. This allows ui to intercept creation of sections, and in particular to catch and modify the listener used to render the page. Actually, ui replace the listener with a wrapper around the original listener. This wrapper will conveniently add the template. When yusef gets a request for the section, it calls the new wrapping listener which in turn calls the original listener to get the content, builds a new page with the template filled with this content, and finally sends the result back to yusef. This effectively wraps the content into a template.
This same technique is used in the acl plugin to prevent access to some content under defined conditions, as well as in the translator plugin to translate strings embedded in the page. Obviously, acl does not only get a hook on the sections, but also on the actions registration.
And now for something completely different: markuper filters
Data generated by a controller for a page may need some processing before being okay for the viewer. For example, escaping HTML or capitalizing the fist letter of a string are such common processing.
One way to deal with this problem is directly in javascript, for example using yusef HTMLEntities to escape strings:
Another way is to apply markuper filters. For example, to escape someHTMLstring, one can use the following built-in filter2:
But is it possible to add one's own filters? The answer lies in markuper.evaluator.registerFilter. The following example shows how to capitalize the first letter of a string:
Be warned though that filters are registered globally (i.e. for all templates). Maybe some form of template-local filter will be available in the future?
By the way, filters can be chained. The following example shows someHTMLstring escaped and capitalizeFirstLetter-ed:
Enjoy!
Using UI
Using ui is demonstrated in the yusef example app: there is nothing to do, except for exporting the content inside the content property of a hash. However, when trying to extend this behavior to other sections of an application, it simply won't work. This is because by default, ui applies its templates only to the 'content' and _index sections (no, it's not a bug, it's a feature!). To enable it on any page, one must add the 'ui' option in his/her listener, as follows:
Yusef.addSectionListener("mysection",
function(connection) { /* your own listener here */ },
{ui: true});
An easy way to give users access to different sections of your application is to create tabs. The ui plugin also provides this through the ui.generateTabs function. The parameters are an array of hashes, each describing one tab, and the current connection. Tabs are described by the following parameters:
- title (required): The title of the tabs; A lower-cased version of the title is used the section if none is explicitly specified.
- section: The specific section to which the tab redirects
- root: Overrides the section with the application root page.
- hideFromVisitors: If the tab should only be visible to the administrator.
Once generated through the generateTabs functions, tabs must be exported by the listener as the tabs proerty of an hash, as follows1:
{
/* your listener code here */
var mycontent = document.createElement('div');
div.innerHTML = "Hello ui!";
var mytabs = Yusef.plugins.ui.generateTabs([
{ title: opera.io.webserver.currentServiceName, isRoot: true },
{ title: "My very own tab", hideFromVisitors: true, section: 'the_section/' }
], connection);
return {
tabs: mytabs,
styles: [a list of your stylesheets, as stored in the public_html directory],
script: [a list of your user-side scripts, as stored in the public_html directory],
content: mycontent
}
}
UI hooking
For those interested in the inner working of ui, and most notably how it intercepts the page handling, here is how it works. Yusef offers an extend method, basically allowing to override any of yusef core function. This extend function requires as arguments the name of a function to be overridden and a new, overriding, function. To this new function, yusef.extend adds a "proceed" method which allows the new function to call the overriden one. This allows for a call chain from the extending function to the (possibly many) extended function, towards the original yusef core method.
In particular, ui extends the Yusef.addSectionListener. This allows ui to intercept creation of sections, and in particular to catch and modify the listener used to render the page. Actually, ui replace the listener with a wrapper around the original listener. This wrapper will conveniently add the template. When yusef gets a request for the section, it calls the new wrapping listener which in turn calls the original listener to get the content, builds a new page with the template filled with this content, and finally sends the result back to yusef. This effectively wraps the content into a template.
This same technique is used in the acl plugin to prevent access to some content under defined conditions, as well as in the translator plugin to translate strings embedded in the page. Obviously, acl does not only get a hook on the sections, but also on the actions registration.
And now for something completely different: markuper filters
Data generated by a controller for a page may need some processing before being okay for the viewer. For example, escaping HTML or capitalizing the fist letter of a string are such common processing.
One way to deal with this problem is directly in javascript, for example using yusef HTMLEntities to escape strings:
escapedHTML = someHTMLstring.HTMLEntities();
Another way is to apply markuper filters. For example, to escape someHTMLstring, one can use the following built-in filter2:
This is some {{someHTMLstring|escape}}
But is it possible to add one's own filters? The answer lies in markuper.evaluator.registerFilter. The following example shows how to capitalize the first letter of a string:
markuper.evaluator.registerFilter('capitalizeFirstLetter', function(data)
{
if( typeof data != 'string' ) { return input; };
return data[0].toUpperCase() + data.slice(1);
});
Be warned though that filters are registered globally (i.e. for all templates). Maybe some form of template-local filter will be available in the future?
By the way, filters can be chained. The following example shows someHTMLstring escaped and capitalizeFirstLetter-ed:
This is some {{someHTMLstring|escape|capitalizeFirstLetter}}
Enjoy!
- The section listener will not listen to a section without a trailing /. The culprit the following line of yusef/core.js: connection.request.section = tmp.length<2?'':tmp.shift().toLowerCase();. Removing tmp.length<2?'': obviously resolves the issue, but this test is probably there for some good reason
.
- Markuper seems to HTML-escape every {{string} anyway, so this example may not be the best.






Mikhail MezheninManiulo # Saturday, February 6, 2010 8:42:16 PM
François Picalausafpicalausa # Sunday, February 7, 2010 11:49:25 AM