You are currently only viewing posts within the category: Site news
You are here: Home → Archive → Site news → 2004 → July → 29th → this post
29th July 2004, early evening | Comments (44)
Part two of the redesign explanations (or ‘Giving Away All My Secrets’, as I’m starting to think of it) is going to look at how I present images, code samples, and conversations on this site.
You may have noticed that although I’ve added some drop shadows to this site, I’ve also removed a few, namely the ones that used to adorn the in-post images. I killed them off because a) I thought I’d get out while the going was good, and b) the site looked like shadow-overkill with them in place. That aside, the reason I mention their absence is that it was the effort that went into removing them that inspired me to do what I describe here.
I’m a bit of a tidy-freak sometimes, I get all funny about dealing with code I don’t like, and will go through a tremendous amount of effort to optimise things, when any normal person would just shrug and work around the problem. Such was the case when, having decided I didn’t like the CSS class names I applied to images and their divs, I revisited every post I’ve ever made and altered the code to better fit in with my new way of thinking. Anal-o-rama, I know, but that’s me.
After checking all 166 entries, four things occurred to me:
Nothing motivates my brain like the opportunity to avoid future work, and so I came up with the idea outlined below.
To ensure I never had to go through this painfully boring process again I decided I’d replace all my image tags (and their accompanying divs and classes) with a single XML tag of my own devising: <imageins />. This tag would contain all the information necessary to fetch and display an image, and would be read and parsed as the page loaded. The PHP scripts that parsed <imageins /> would assemble whatever markup I was currently favouring, and insert it into the document in place of this home-made tag. In this way future rethinks of the code I used to present images would require the alteration of only one PHP file, not many hundreds of blog entries.
I also realised that this technique had the added benefit of removing the dimensions and file type of the images from the database entry, and letting PHP do the work of figuring that out. This meant that should a future redesign require smaller images than I currently use, or should I decide that GIFs are no longer acceptable and wish to convert to PNGs, it’d just be a case of running a Photoshop (or Imagemagick) batch script against my original, large images, and popping the newly created files onto the server in place of the old ones. These changes would be seamless as no file types or dimensions would ever be specified in the blog post entires, so there’d be nothing to break; no mismatch of information, so to speak. Now I could really go to town with CSS in the future, without having to worry about how my post content would be affected — it seemed like another step closer to the ideal of a truly visually-flexible site.
A final upside to this idea was that should an image be missing on page-load I could bypass the browser’s “image not found” error-handling and determine what action to take myself: present a “whoops, I can’t find that picture” message; output nothing at all; email me an alert, etc, etc. Similar error catching could be used for images that linked to more detailed, larger images: if those larger images couldn’t be found then there was no point in printing the link. In this way the number of user-perceived errors could be reduced, and I could be alerted should I ever forget to upload an image file.
Things were looking good.
It then occurred to me that there must be other elements in my blog entries whose markup I might want to change over time; elements (such as conversations, or code examples) for which no standardised set of markup existed. It would be a bit daft to go to all the trouble of fixing my images if, with every redesign, I still had to trawl through my blog posts to restructure problematic code.
My solutions to the problems of image, code sample, and conversation replacements are laid out below. If you like what you see and want to write a Word Press or Movable Type plugin, then please feel free, just ensure you give the appropriate attribution.
As always I’m happy to hear of better ways to do what I've done (I don’t claim to be a PHP-genius), so please feel free to post improvements if you’re a cleverer bugger than me.
Here’s the imageTransform() function:
<?php// transform my little image tagsfunction imageTransform($file, $alt, $caption, $class, $link, $linktitle){// set vars$html = '';$container_open = '';$container_close = '';$link_start = '';$link_end = '';$filetypes = array('jpg', 'gif', 'png');// get filetype for imageforeach ($filetypes as $type){if (file_exists('/home/d/www/blog/images/'.$file.'.'.$type)){$ext = '.'.$type;break;}}// if no filetype matched, then quitif (!isset($ext)){// insert error handling here// email yourself an error or whatever you wantreturn;}// get file info$file_info = getimagesize('/home/d/www/blog/images/'.$file.$ext);// set vars$add_width = ($caption <> '') ? 16 : 12;$width = ' style="width: '.($file_info[0] + $add_width).'px;"';$class = ($class == '') ? '' : $class.' ';// if there's a captionif ($caption <> ''){ $container_open .= '<div class="'.$class.'caption"'.$width.'>'."\n";$caption = $caption."\n";$container_close .= '</div>'."\n";$width = '';$class = '';}// if there's a linkif ($link <> ''){// if it's a link to a larger version of the same imageif ($link == 'large'){// get filetype for the larger imageforeach ($filetypes as $type){if (file_exists('/home/d/www/blog/images/large/'.$file.'.'.$type)){$ext2 = '.'.$type;break;}}// if a filetype was found, set the linkif (isset($ext2)){$link_start = '<a href="/blog/images/large/'.$file.$ext2.'" title="View a larger version of this image">';$link_end = '</a>';}else{// insert error handling here// email yourself an error or whatever you want}}// else set the link to the url specifiedelse{$link_start = '<a href="'.$link.'" title="'.$linktitle.'">';$link_end = '</a>';}}// build the html$html .= $container_open;$html .= '<div class="'.$class.'image"'.$width.'>';$html .= $link_start.'<img src="/blog/images/'.$file.$ext.'" '.$file_info[3].' alt="'.$alt.'" />'.$link_end;$html .= '</div>'."\n";$html .= $caption;$html .= $container_close;// send the html backreturn $html;}?>And here are nine examples of how to use it:
// examples of image insertions using my little <imageins /> tag// example 1a: plain image (displayed block left)<imageins="456a" alt="A cartoon of a duck" caption="" class="" link="" linktitle="" />// would produce:<div class="image" style="width: 216px;"><img src="/blog/images/456a.gif" width="200" height="186" alt="A cartoon of a duck" /></div>// example 1b: decorative image (displayed floated right)<imageins="456a" alt="A cartoon of a duck" caption="" class="decor" link="" linktitle="" />// would produce:<div class="decor image" style="width: 216px;"><img src="/blog/images/456a.gif" width="200" height="186" alt="A cartoon of a duck" /></div>// example 1c: gallery image (displayed floated left)<imageins="456a" alt="A cartoon of a duck" caption="" class="gallery" link="" linktitle="" />// would produce:<div class="image gallery" style="width: 216px;"><img src="/blog/images/456a.gif" width="200" height="186" alt="A cartoon of a duck" /></div>- - - - - - - - - - - - - - - - - - - - -// example 2a: plain image with caption<imageins="456a" alt="A cartoon of a duck" caption="A cartoon I drew" class="" link="" linktitle="" />// would produce:<div class="caption" style="width: 216px;"><div class="image"><img src="/blog/images/456a.gif" width="200" height="186" alt="A cartoon of a duck" /></div>A cartoon I drew</div>// example 2b: plain image with caption that includes HTML<imageins="456a" alt="A cartoon of a duck" caption="A cartoon I drew of a <a href='http://www.ducks.org/' title='Go see a site about ducks'>duck</a>" class="" link="" linktitle="" />// would produce:<div class="caption" style="width: 216px;"><div class="image"><img src="/blog/images/456a.gif" width="200" height="186" alt="A cartoon of a duck" /></div>A cartoon I drew of a <a href='http://www.ducks.org/' title='Go see a site about ducks'>duck</a></div>// example 2c: decorative image with caption<imageins="456a" alt="A cartoon of a duck" caption="A cartoon I drew" class="decor" link="" linktitle="" />// would produce:<div class="caption decor" style="width: 216px;"><div class="image"><img src="/blog/images/456a.gif" width="200" height="186" alt="A cartoon of a duck" /></div>A cartoon I drew</div>// example 2d: gallery image with caption<imageins="456a" alt="A cartoon of a duck" caption="A cartoon I drew" class="gallery" link="" linktitle="" />// would produce:<div class="caption gallery" style="width: 216px;"><div class="image"><img src="/blog/images/456a.gif" width="200" height="186" alt="A cartoon of a duck" /></div>A cartoon I drew</div>- - - - - - - - - - - - - - - - - - - - -// example 3a: plain image with link to larger image of same file name (although the filetype could be different. In this case the matching image turns out to be a JPEG)<imageins="456a" alt="A cartoon of a duck" caption="" class="" link="large" linktitle="" />// would produce:<div class="image" style="width: 216px;"><a href="/blog/images/large/456a.jpg" title="View a larger version of this image"><img src="/blog/images/456a.gif" width="200" height="186" alt="A cartoon of a duck" /></a></div>- - - - - - - - - - - - - - - - - - - - -// example 3b: plain image with link to some other URL and link text (this text is optional)<imageins="456a" alt="A cartoon of a duck" caption="" class="" link="http://sidesh0w.com/" linktitle="Ethan smells like a duck. Go see his site" />// would produce:<div class="image" style="width: 216px;"><a href="http://sidesh0w.com/" title="Ethan smells like a duck. Go see his site"><img src="/blog/images/456a.gif" width="200" height="186" alt="A cartoon of a duck" /></a></div>Here’s the result of example 3a:
I’ve always found code presentation to be a pain in the arse. There are just so many things you have to do to prepare code samples for insertion into a blog post, that it often takes the fun out of writing the post in the first place (look at the amount of code on this page, would you fancy prepping all of that?). And if you decide to skip all the preparation and go for <pre> tags instead, you have to work out what to do when long, non-breaking lines of code mess with your beautiful layout.
The transformation function outlined below hopefully gets around most of the problems you normally run into. It removes the hard work of preparing examples by asking that you simply place them in a text file, as is (or, with a bit of work, you could point the function directly at your live files, for an always up-to-date sample of your code). It removes the need to insert multiple instances of by using CSS to simulate tab characters. It removes the confusion that can arise when looking at long sections of code, by using Simon Willison’s idea of structuring the whole thing as an ordered list, thus providing line numbers (and some form of meaningful structure). It also highlights comments, so users can easily separate them from the code itself. And finally it provides a link to the original, unmolested text file, should users wish to download the code for their own use.
And how is all that achieved? Well, take a look for yourself:
<?// this function is used to find the last occurance of a string within another string// it's used because strrpos() only looks for a single character, not a stringfunction strLastPos($haystack, $needle){// flip both strings around and search, then adjust position based on string lengthsreturn strlen($haystack) - strlen($needle) - strpos(strrev($haystack), strrev($needle));} // transform my little code tagsfunction codeTransform($filename){$filename = 'code/'.$filename.'.txt';// set vars$list = '';$cmnt = '';$multi_line_cmnt = 0;$tab = '';$class = '';// open the file$file = fopen($filename, 'r');// for each line in the filewhile (!feof($file)){// get line$line = fgets($file, 4096);// convert tags to safe entities for display$line = htmlspecialchars($line);// count the number of tabs at the start of the line, and set the appropriate tab class$tab = substr_count($line, "\t");$tab = ($tab > 0) ? 'tab'.$tab : '';// remove any tabs at the start of the line$line = str_replace("\t", '', $line); // find position of comment characters$slashslash_pos = strpos($line, '//');$apos_pos = strpos($line, "'");$slashstar_pos = strpos($line, '/*');$starslash_pos = strLastPos($line, '*/');// if it's an ongoing multi-line commentif ($multi_line_cmnt == 1){$cmnt = 'cmnt';$multi_line_cmnt = 1;}// if it's not an ongoing multi-line commentif ($multi_line_cmnt <> 1){// if it's a single line commentif (($slashslash_pos === 0) || ($apos_pos === 0)){$cmnt = 'cmnt';$multi_line_cmnt = 0;}else{$cmnt = '';$multi_line_cmnt = 0;}// if it's potentially the start of a multi-line commentif ($slashstar_pos === 0){$cmnt = 'cmnt';// if multi-line comment end string is found on the same lineif ($starslash_pos == (strlen($line) - 3)){$multi_line_cmnt = 0;}// if the multi-line comment end string is not found on the same lineelse{$multi_line_cmnt = 1;}}}// if the line contains the multi-line end stringif ($starslash_pos == (strlen($line) - 3)){$cmnt = 'cmnt';$multi_line_cmnt = 0;}// if both cmnt and tab classes are to be appliedif ( ($cmnt <> '') && ($tab <> '') ){$class = ' class="'.$tab.' '.$cmnt.'"';}// if only one class is to be appliedelse if ( ($cmnt <> "") || ($tab <> "") ){$class = ' class="'.$tab.$cmnt.'"';}// if no classes are to be appliedelse{$class = '';}// remove return at the end of the line$line = str_replace("\n", "", $line);// if the line is blank, put a space in to stop some browsers collapsing the lineif ($line == ''){// insert all the information and close the list item$list .= '<li'.$class.'> </li>'."\n";}// otherwise inserts the line contentselse{// insert all the information and close the list item$list .= '<li'.$class.'><code>'.$line.'</code></li>'."\n";}}// close the finle handlefclose($file);// add in the link to the file$list .= '<li class="source">Download this code: <a href="/blog/'.$filename.'" title="Download the above code as a text file">/'.$filename.'</a></li>';// build the list$list = '<ol class="code">'."\n".$list."\n".'</ol>'."\n";// return the listreturn $list;}?>Here’s the accompanying CSS:
/* CSS for styling code examples */ol.code {border: 1px solid #eee;color: #333;font: normal 98%/110% Courier, monospace;margin: 0;overflow: auto;padding: 5px 5px 5px 45px;}ol.code li {background-color: #f4f8fb;margin: 0;padding: 0 5px;}ol.code li.source {background-color: #fff;list-style-type: none;padding: 5px;text-align: center;}ol.code li+li {margin-top: 2px;}ol.code li.tab1 {padding-left: 15px;}ol.code li.tab2 {padding-left: 30px;}ol.code li.tab3 {padding-left: 45px;}ol.code li.tab4 {padding-left: 60px;}ol.code li.tab5 {padding-left: 75px;}ol.code li.tab6 {padding-left: 90px;}ol.code li code {color: #333;}ol.code li.cmnt code {color: #824c88;}Here’s an example of how to use it all:
// to insert a section of code<codeins="456d" />And here’s a screenshot of the output (showing a nonsense function I just made up) as the user will see it :

And finally here’s an example of the XHTML output for the above example (jeez, this is getting loopy):
<ol class="code"><li><?php</li><li class="cmnt">// a made up function to demonstate the various aspects of the codeTransform() function</li><li></li><li>fooBar($a, $b)</li><li class="tab1">{</li><li class="tab1 cmnt">// here's a comment</li><li class="tab1">if ($a == $b)</li><li class="tab2">{</li><li class="tab2">print 'toodle-whoopsey';</li><li class="tab2">}</li><li class="tab1 cmnt">/* and here's a multi-line comment</li><li class="tab1 cmnt">it goes on...</li><li class="tab1 cmnt">and on...</li><li class="tab1 cmnt">and then ends */</li><li class="tab1">else</li><li class="tab2">{</li><li class="tab2">if ($b == 10)</li><li class="tab3">{</li><li class="tab3 cmnt">// now we're three tabs in</li><li class="tab3">print "RAAAAAA! I'M A LION!";</li><li class="tab3">}</li><li class="tab2">}</li><li class="tab1">}</li><li>?></li><li class="source">Download this code: <a href="/blog/code/456.txt" title="Download the above code as a text file">/code/456.txt</a></li></ol>Actually I haven’t got around to doing this yet, but here’s an example of how I might markup a conversation in XML:
<?xml version="1.0" encoding="utf-8"?><scene><person value="Marie"><event type="speech">What does that look like, Grandma?</event></person><person value="Grandma"><event type="thought">Hmm, it looks familiar… ah, I know!</event><event type="speech">It’s a clitoris!</event></person><person value="Everyone"><event type="action">Silence</event></person><person value="Grandma"><event type="speech">Oh no! I got muddled up, I mean a chrysalis!</event></person><person value="Everyone"><event type="speech">Grandma!</event></person></scene>(Some of that flies in the face of the advice that Tantek kindly gave me, but I’m still trying to decide which is better: to store human information in the <person> tag (as Herr Tantek advises) and have to use an extra tag to enclose each person’s contribution-block; or to store the info in an attribute of <person> (as I’m currently doing) and save using the extra tag. What do you think?)
Here’s an example of the parsed output, marked-up as I currently present conversations (at the time of writing):
<dl class="dialogue"><dt>Marie</dt><dd><q>What does that look like, Grandma?</q></dd><dt>Grandma</dt><dd>*Hmm, it looks familiar&&8230; ah, I know!*</dd><dd><q>It’s a clitoris!</q></dd><dt>Everyone</dt><dd>*Silence*</dd><dt>Grandma</dt><dd><q>Oh no! I got muddled up, I mean a chrysalis!</q></dd><dt>Everyone</dt><dd><q>Grandma!</q></dd></dl>(I forgot to add this bit in, so thanks to Charles Roper for reminding me that I hadn’t quite finished the article.)
So, you’ve got the transformation functions, and you understand how to replace your code and images with the two tags I made up (<codeins />, <imageins />) but how exactly do you get PHP to run the transformation functions? How do you get it to replace those XML tags with actual code examples and images? It’s easy, you do it like this:
<?php// include the file with the transformation scripts ininclude('blog/include/general_transform.php');// grab your post_body from the db, I do it like this:// $post_body = stripslashes($row['post_body']);// transform all the code listings$post_body = preg_replace('!<codeins="(.*?)" />!ie', "codeTransform('$1')", $post_body);// transform all the image tags$post_body = preg_replace('!<imageins="(.*?)" alt="(.*?)" caption="(.*?)" class="(.*?)" link="(.*?)" linktitle="(.*?)" />!ie', "imageTransform('$1', '$2', '$3', '$4', '$5', '$6')", $post_body);// $post_body now contains your complete, transformed post, ready to be printed out:print $post_body;?>(Thanks to Matt Mullenweg for showing me how to do that.)
And that’s it. Bloomin’ nora, that was a long post, eh?
Right, time for dinner…
Jump up to the start of the post ↑
A collection of miscellaneous links that don't merit a main blog posting, but which are interesting none-the-less.
Our enemies are innovative and resourceful, and so are we. They never stop thinking about new ways to harm our country and our people, and neither do we.— George W Bush (9)
Stuff from the intersection of design, culture and technology.(3)
A selection of blogs I read on a regular basis.