Tobiasz Cudnik

Archive for the ‘Ideas’ Category

Can CSS stylesheets be applied on the server-side ?

In Ideas, QueryTemplates, Workflow on 27.01.2009 at 3:23

The question is: can CSS stylesheet be applied in the server-side ? And if yes, would would be the purpose ? I should say yes, they can and i would explain how and why. So what for are stylesheets ? “Applying cascading styles onto the DOM” may be the answer. But i would focus more on that why actually stylesheets are used for such purpose?

  • Because they are clean ?
  • Because CSS selectors do so much job, that besides them only really needed things are simple properties with obligatory number of arguments ?
  • Because cascading inheritance is just right for the DOM ?
  • CSS selector are simply benefiting from DOM structure’s nature

So first thing which needs to be implemented on the server is the DOM. When we already got it, so we can query it thou selector, that’s the first half. Now the properties. What could be the property on the server ? border-color ? Rather not. Do we need to style the DOM on the server ? Is it shown anywhere ? Not right now. On the server, page’s DOM is being build. Typically by various ways of string concatenation.

So if we need to build a DOM and same time we can query it with selectors, then the style’s properties should build / modify it’s structure. For example, show some articles or blog posts on the page. So here is pure CSS parser-validable code with some proposed server properties.

.section.articles ul > li {
  /* loop "articles" variable as "article" using "num" as index
  after that remove all other-than-first LIs */
  loop-one: articles num article;

  /* apply filter method on next variable output */
  vars-filter: htmlentities;

  /* use "article" fields and populate them inside nodes
  matched by ".a-%k" where %k is field name, eg .a-title */
  vars-to-selector: articles var(articleFields) null ".a-%k";

  /* example custom method to adding class for 1st loop node using index "num" */
  add-class-to-first: num custom-class;
}
/* show .comments node if variable articleComments is true */
.comments {
  if-var: articleComments;
}
/* bubbled event */
.section.articles:inserted {
  /* another custom method */
  /* null means 0 arguments */
  strip-tables: null;
}

So what have we done here ?

  1. We simply build articles loop which prints it’s contents into proper child nodes automagically. It could have 3 fields or 345, but line in CSS is only one.
  2. We’ve also used a custom method do add class basing on the loop position.
  3. Comments presentance is conditional.
  4. One Mutation Event is used. Hidden in :insert pseudoclass, really named DOMNodeInserted will apply on all new nodes which matches prior selector and were just inserted into the DOM structure.
  5. Each unspecified event defaults to :load.

All properties should be simply methods which applies something on all matched nodes. Sound familiar ? That’s exactly what many of jQuery methods are doing. Take a look:

.section.articles {
  remove-class: section;
  attr: rel section;
  /* all css properties can be used
  but will result in inline "style" attributes */
  css: padding 2px;
}
/* this rule applies to all load-time nodes
and the new one too */
.change-me,
.change-me:inserted {
  text: "changed innerText";
  prepend: "
<div>changed</div>
";
  remove-class: change-me;
  /* reallocation, interesting... */
  insert-into: ".content";
  /* passing some data to forward ? */
  data: some-flag "some value for developer";
}

All from above properties are jQuery methods. Possibilities are huge. But let’s stick with simple things right now. How would it benefit ? Who will write those server-css-sheets ? I should say…

Read the rest of this entry »

Events in CSS selector-driven markup templates make them highly reusable

In Ideas, QueryTemplates on 25.01.2009 at 16:36

One of the biggest adventage of DOM oriented template engines such as QueryTemplates is events support. Using Event Delegation and some Mutation Events you can set global handlers for all templates. Purpose of such handlers may be varient. I’m presenting 6 of them as examples.

Let’s start from events table

Each event can easily have many handlers. One of them is a mutation event – DOMNodeInserted.

$globalHandlers = array(
  array('unload', 'everyTableHaveTbody'),
  array('load', 'insertNavigation'),
  array('DOMNodeInserted', 'newListsAlwaysOrdered'),
  array('unload', 'noAds'),
  array('unload', 'clearEmptyClasses'),
  array('load', 'stripTables'),
);

Here’s our global handlers

// global handlers
// can be functions, methods, create_function and closures (in PHP 5.3)

function everyTableHaveTbody($e) {
  pq($e->target->ownerDocument)
    ->find('table')
      // nested closure should be here
      ->each('everyTableHaveTbodyCallback');
}
function everyTableHaveTbodyCallback($table) {
  if (pq('> tbody', $table)->length == 0)
    pq($table)->contents()->wrapAll('
<tbody>');
}
function noAds($e) {
  pq($e->target->ownerDocument)
    ->find('.ads')->remove();
}
function newListsAlwaysOrdered($e) {
  if (pq($e->target)->is('ul'))
    pq($e->target)->replaceWith(
      pq('
<ol>')->append(
        pq($e->target)->contents()
      )
    );
}
function insertNavigation($e) {
  $nav = pq($e->target->ownerDocument)
    ->find('.navigation')
      ->empty()
      // nested closure should be here
      ->each('insertNavigationCallback');
}
function insertNavigationCallback($nav) {
  pq($nav)->append(file_get_contents('navigation.html'));
}
function stripTables($e) {
  pq($e->target)->find('table.strip-me')
    ->removeClass('strip-me')
    ->find('> tr:odd, > tbody > tr:odd')
      ->addClass('odd');
}
function clearEmptyClasses($e) {
  $nav = pq($e->target->ownerDocument)
    ->find('*[class=""]')
      ->removeAttr('class');
}

Read the rest of this entry »

Utilizing Mutation Events for automatic and persistent event attaching

In Ideas, Snippets on 19.01.2009 at 19:43

Most of you probably heard about Event Delegation, a technique where you bind to event of interest on parent node and when triggered event bubbles from child node, action in taken inside the parent. This pattern is widely popularized by reglib and Live Query (plugin for jQuery). Newly released jQuery 1.3 has it’s own implementation thou $.live() method.

This patten has couple of advantages over classic attaching model. First, it’s faster for BIG number of (same) nodes. Secondly, which is most important i think, it allows easy content exchange via AJAX. But with all that it has also disadvantages and main is that you doesn’t really attach to a node, only to it’s parent. That means when you relocate node in the DOM it looses it’s behavior.

That’s why i’ve came with the idea of using Mutation Events to take best parts from both patterns. Mutation Events aren’t supported in IE, but i’ve rolled out some compatibility layer to target this problem. Works also with document fragment appends introduced in jQuery 1.3.

You can go straight to the demo, which was tested and works with:

  • Firefox 3 and 2 linux
  • IE6 SP2 win32
  • Opera 9.63 linux
  • Safari 3.6 win32
  • Chrome 1.0 win32

First lets see main code:

function attachEvents(node) {
	// node is an optional callback for filter()
	// that applies rules only for new nodes
	node = node || '*';
	$('.container1 .trigger1').filter(node).click(function(e) {
		alert('mutation events work');
		e.preventDefault();
	});
}

This is simple, common function that applies behaviors to the document. The only major difference from other such functions is node parameter, used as callback for filter() method, which limits found nodes to interesting ones. Now the actual event bindings:

// node inserted into DOM
$(document).bind('DOMNodeInserted', function(e) {
	attachEvents(function(node){
		return !e.target.skipEventAttaching &&
			this == e.target
	});
});
// node removed from DOM
$(document).bind('DOMNodeRemoved', function(e) {
	// add information property, that node has been detached
	e.target.skipEventAttaching = true;
});

Here the magic happens ;) First handler fires up our event attaching function with callback for filter as a parameter. Callback will check if found node is target of the mutation event or is it reinserted. Second handler adds flag about reinserting.

You can see demo which compares this technique with jQuery 1.3 and reglib. It consists of 3 different implementation:

  • jQuery 1.3 using Mutation Events
  • jQuery 1.3 using $.live
  • reglib

Test it like this: click add node (couple of times), click .trigger (first one, last one) then do move node and again click moved node. Each time when clicking on .trigger, one (and only one) alert should pop out saying that certain technique works.

Additionally there’s a IE compatibility for mutation events in conditional comment. Great help writing it was from ieproto example.

Of course this is proof-of-concept and souldn’t be used in production until fully tested and fixed.

Test cases for websites using phpQuery and SimpleTest

In Ideas, phpQuery on 13.01.2009 at 23:09

Using phpQuery and some UnitTest framework (SimpleTest in this example) you can automatically test web page for presentence of specific part and it’s position. Set of such tests can save a lot of time during website development and after that even more.

Not only you can do simple tests like “are articles visible” but using WebBrowser plugin you can test whole process, let’s say a user registration. Example of such test i would like to present. This test will include following steps:

  1. Enter main page and follow the registration link
  2. Fill registration form and submit it
  3. Check if result is expected

Like i sad before, SimpleTest is framework of choice, but it doesn’t matter so much. What’s important:

  • WebBrowser needs callbacks
  • Callbacks should be declared as functions (for PHP < 5.3)
  • Inside callbacks, $this variable is unavailable
  • First step of the test should check if last step has succeeded

Full code below:

require('simpletest/autorun.php');
require('phpQuery/phpQuery.php');

class CustomerTest extends UnitTestCase {
	public static $_this;
	public static $registration = array(
		'username' => null,
		'success' => false
	);
	function testRegistration() {
		self::$_this = $this;
		phpQuery::browserGet('http://localhost/tested-site/',
			array('CustomerTest', '_testRegistrationLink')
		);
		$this->assertTrue(
			self::$registration['success'], "Registration unsuccessful"
		);
	}
	function _testRegistrationLink($browser) {
		$registrationLink = null;
		$browser->find('a:contains(rejestracja)')
			->WebBrowser(array('CustomerTest', '_testRegistrationForm'))
			->toReference($registrationLink)
				// jump to _testRegistrationForm
				->click();
		self::$_this->assertTrue(
			$registrationLink->length, "Registration link missing"
		);
	}
	function _testRegistrationForm($browser) {
		$registrationForm = null;
		$username = md5(microtime());
		$browser['.customers.form form']
			->toReference($registrationForm)
			->WebBrowser(array('CustomerTest', '_testRegistrationResult'))
			->find('input[name*=login]')->val($username)->end()
			->find('input[name*=email]')->val($username.'@test.com')->end()
			// jump to _testRegistrationResult
			->submit();
		self::$_this->assertTrue(
			$registrationForm->length, "Registration form missing"
		);
		self::$registration['username'] = $username;
	}
	function _testRegistrationResult($browser) {
		$loginForm = $browser->find('h2:text(Logowanie)');
		self::$_this->assertTrue($loginForm->length, "Login form missing");
		if ($loginForm->length)
			self::$registration['success'] = true;
	}
}

WebBrowser doesn’t support AJAX, so not all sites can be tested like this (although you can do it with AHAH after some work), but cookies and HTTP authentication should satisfy most needs.

Of course that’s noting new, projects accomplishing similar goal exist quite time now, eg jWebUnit, but neither of them have jQuery under the hood  ;)

Modify arrays easily using BAM – BulkArrayModifier

In Chainable, Ideas on 30.12.2008 at 22:46

Have you ever needed to modify certain fields in deeply nested array structure ? I’ve had such tasks writing CakePHP views quite often. Tried to use Set, but it only extracts data. When you modify it, you can use it standalone, but not with rest of the data it was stick to before extracting. That’s why i’ve always wanted to write something what will make this process cleaner and more natural, removing the need to write redundant code.

That’s why one evening i sat down and here it is – BulkArrayModifier. Standalone, chainable, ~120 lines class which extracts array’s content via references. It utilizes callbacks as much as possible. Consider this example:

// get all comments from all posts
bam($posts)->all()->key('Comment')->all()->each('modifyTitle');
// and use this callback to modify each one
function modifyTitle($comment) {
  $comment['title'] = "<a>{$comment['title']}</a>";
  return $comment;
}

There is also one-method query, like this:

bam($posts)->path('*.Comment.*')->each('modifyTitle');

You can also apply several callbacks to one bam() object

bam($posts)->path('*.Comment.*')->each('modifyTitle')->reset()
  ->path('*.Post.author')->each('modifyAuthor');

Read the rest of this entry »

Pure HTML templates theory

In Ideas on 19.11.2008 at 23:51

In this post i would like to sum up my thought about using pure HTML files as fully functional templates. I’ve examined this pattern for about a year and i have to say it creates real order in application structure and lifecycle.

What means “pure” ?

  • Pure like plain, nothing else than HTML
  • Fully validable
  • Readable in any browser
  • Editable in any HTML editor

Read the rest of this entry »

plainTemplates – CSS driven templating engine

In Ideas, plainTemplates on 30.07.2007 at 11:22

This post is deprecated and informations below are outdated. Please follow up to QueryTemplates and Pure HTML templates theory.

Introduction

plainTemplates is a PHP and JS library that aims to divide template into 2 separate layers:

  1. CSS selectors driven data attaching mechanism
  2. Plain HTML

It’s simply creates your templates from plain HTML files on-the-fly, filling it with data and fetching parts you like.

It’s basen on phpQuery – a jQuery port to PHP.

Pros

  • True separation of data and visual layer
  • HTML can be filled with example data
  • High reusability (several templates from one HTML, one template from several HTMLs)
  • Framework independed template organisation
  • Transparent refreshes (including PHP code changes!)
  • phpQuery integration
  • No need to lear new, template-only language
  • Saves a lot of time ;)

Cons

  • You have to know CSS selectors (XPath will also help)
  • In most cases you have to know jQuery/phpQuery
  • You have to format data outside template (eg date).

Here is simple example, which inserts data from $data array into template in template.htm:

// get the class
require_once('../../plainTemplates/plainTemplates.php');
// always end dir with a slash
plainTemplates::$cacheDir = '../../plainTemplates/cache/';
$data = array(
	array(
		'title' => 'News 1 title',
		'body'	=> 'News 1 body',
	),
	array(
		'title' => 'News 2 title',
		'body'	=> 'News 2 body',
	),
	array(
		'title' => 'News 3',
		'body'	=> 'News 3 body',
	),
);
// include to fully preserve scope
include(
	plainTemplates::createTemplate(
		// first the template source
		'template.htm',
		// second the var info
		array(
			// var name in scope
			'$data',
			// and var value
			$data,
			// finally the selectors (of course there are defaults, that is UL and LI)
			array(
				'container' => '.someclass ul',
				'row'		=> 'li'
			)
		)
	)
);

Here is template.htm itself:

<div class='someclass'>
	<p>UL below will be container, and every LI will be searched agains classes with field names.</p>
	<ul>
		<li>

			<p>Only tags with field classes are replaced.</p>
			<p class='title'>This will be replaces by the title</p>
			<p class='body'>And this by the body...</p>
		</li>

		<li>
			Only first row will be in the result and this wont be considered by any chance... so You can use as many row examples as You want.
		</li>
	</ul>
</div>

Now the result’s source:

<ul><?php foreach( $data as $dataRow ): ?>

			<li>
			<p>Only tags with field classes are replaced.</p>
			<p class="title"><?php print $dataRow['title']; ?></p>
			<p class="body"><?php print $dataRow['body']; ?></p>

		</li><?php endforeach; ?></ul>

Those are the basics, it can do a lot more, but i think its good that it can do simple things in a simple way… Read the rest of this entry »