Post #456

Redesign explained: tag transformations

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.

Preface

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.

The problem

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:

  1. That was bloody boring.
  2. There’s no way I ever want to do that again.
  3. Hang on, what happens next time I redesign and I change my system again?
  4. There must be a better way to do this…

Nothing motivates my brain like the opportunity to avoid future work, and so I came up with the idea outlined below.

The idea

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.

Other benefits

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.

The solution

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.

Inserting images into posts

Here’s the imageTransform() function:

  1. <?php
  2. // transform my little image tags
  3. function imageTransform($file, $alt, $caption, $class, $link, $linktitle)
  4. {
  5. // set vars
  6. $html = '';
  7. $container_open = '';
  8. $container_close = '';
  9. $link_start = '';
  10. $link_end = '';
  11. $filetypes = array('jpg', 'gif', 'png');
  12.  
  13. // get filetype for image
  14. foreach ($filetypes as $type)
  15. {
  16. if (file_exists('/home/d/www/blog/images/'.$file.'.'.$type))
  17. {
  18. $ext = '.'.$type;
  19. break;
  20. }
  21. }
  22.  
  23. // if no filetype matched, then quit
  24. if (!isset($ext))
  25. {
  26. // insert error handling here
  27. // email yourself an error or whatever you want
  28. return;
  29. }
  30.  
  31. // get file info
  32. $file_info = getimagesize('/home/d/www/blog/images/'.$file.$ext);
  33.  
  34. // set vars
  35. $add_width = ($caption <> '') ? 16 : 12;
  36. $width = ' style="width: '.($file_info[0] + $add_width).'px;"';
  37. $class = ($class == '') ? '' : $class.' ';
  38.  
  39. // if there's a caption
  40. if ($caption <> '')
  41. {
  42. $container_open .= '<div class="'.$class.'caption"'.$width.'>'."\n";
  43. $caption = $caption."\n";
  44. $container_close .= '</div>'."\n";
  45. $width = '';
  46. $class = '';
  47. }
  48.  
  49. // if there's a link
  50. if ($link <> '')
  51. {
  52. // if it's a link to a larger version of the same image
  53. if ($link == 'large')
  54. {
  55. // get filetype for the larger image
  56. foreach ($filetypes as $type)
  57. {
  58. if (file_exists('/home/d/www/blog/images/large/'.$file.'.'.$type))
  59. {
  60. $ext2 = '.'.$type;
  61. break;
  62. }
  63. }
  64.  
  65. // if a filetype was found, set the link
  66. if (isset($ext2))
  67. {
  68. $link_start = '<a href="/blog/images/large/'.$file.$ext2.'" title="View a larger version of this image">';
  69. $link_end = '</a>';
  70. }
  71. else
  72. {
  73. // insert error handling here
  74. // email yourself an error or whatever you want
  75. }
  76. }
  77.  
  78. // else set the link to the url specified
  79. else
  80. {
  81. $link_start = '<a href="'.$link.'" title="'.$linktitle.'">';
  82. $link_end = '</a>';
  83. }
  84. }
  85.  
  86. // build the html
  87. $html .= $container_open;
  88. $html .= '<div class="'.$class.'image"'.$width.'>';
  89. $html .= $link_start.'<img src="/blog/images/'.$file.$ext.'" '.$file_info[3].' alt="'.$alt.'" />'.$link_end;
  90. $html .= '</div>'."\n";
  91. $html .= $caption;
  92. $html .= $container_close;
  93.  
  94. // send the html back
  95. return $html;
  96. }
  97. ?>
  98. Download this code: 456a.txt

And here are nine examples of how to use it:

  1. // examples of image insertions using my little <imageins /> tag
  2.  
  3.  
  4. // example 1a: plain image (displayed block left)
  5. <imageins="456a" alt="A cartoon of a duck" caption="" class="" link="" linktitle="" />
  6.  
  7. // would produce:
  8. <div class="image" style="width: 216px;"><img src="/blog/images/456a.gif" width="200" height="186" alt="A cartoon of a duck" /></div>
  9.  
  10.  
  11.  
  12. // example 1b: decorative image (displayed floated right)
  13. <imageins="456a" alt="A cartoon of a duck" caption="" class="decor" link="" linktitle="" />
  14.  
  15. // would produce:
  16. <div class="decor image" style="width: 216px;"><img src="/blog/images/456a.gif" width="200" height="186" alt="A cartoon of a duck" /></div>
  17.  
  18.  
  19.  
  20. // example 1c: gallery image (displayed floated left)
  21. <imageins="456a" alt="A cartoon of a duck" caption="" class="gallery" link="" linktitle="" />
  22.  
  23. // would produce:
  24. <div class="image gallery" style="width: 216px;"><img src="/blog/images/456a.gif" width="200" height="186" alt="A cartoon of a duck" /></div>
  25.  
  26.  
  27. - - - - - - - - - - - - - - - - - - - - -
  28.  
  29.  
  30. // example 2a: plain image with caption
  31. <imageins="456a" alt="A cartoon of a duck" caption="A cartoon I drew" class="" link="" linktitle="" />
  32.  
  33. // would produce:
  34. <div class="caption" style="width: 216px;">
  35. <div class="image"><img src="/blog/images/456a.gif" width="200" height="186" alt="A cartoon of a duck" /></div>
  36. A cartoon I drew
  37. </div>
  38.  
  39.  
  40.  
  41. // example 2b: plain image with caption that includes HTML
  42. <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="" />
  43.  
  44. // would produce:
  45. <div class="caption" style="width: 216px;">
  46. <div class="image"><img src="/blog/images/456a.gif" width="200" height="186" alt="A cartoon of a duck" /></div>
  47. A cartoon I drew of a <a href='http://www.ducks.org/' title='Go see a site about ducks'>duck</a>
  48. </div>
  49.  
  50.  
  51.  
  52. // example 2c: decorative image with caption
  53. <imageins="456a" alt="A cartoon of a duck" caption="A cartoon I drew" class="decor" link="" linktitle="" />
  54.  
  55. // would produce:
  56. <div class="caption decor" style="width: 216px;">
  57. <div class="image"><img src="/blog/images/456a.gif" width="200" height="186" alt="A cartoon of a duck" /></div>
  58. A cartoon I drew
  59. </div>
  60.  
  61.  
  62.  
  63. // example 2d: gallery image with caption
  64. <imageins="456a" alt="A cartoon of a duck" caption="A cartoon I drew" class="gallery" link="" linktitle="" />
  65.  
  66. // would produce:
  67. <div class="caption gallery" style="width: 216px;">
  68. <div class="image"><img src="/blog/images/456a.gif" width="200" height="186" alt="A cartoon of a duck" /></div>
  69. A cartoon I drew
  70. </div>
  71.  
  72.  
  73. - - - - - - - - - - - - - - - - - - - - -
  74.  
  75.  
  76. // 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)
  77. <imageins="456a" alt="A cartoon of a duck" caption="" class="" link="large" linktitle="" />
  78.  
  79. // would produce:
  80. <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>
  81.  
  82.  
  83. - - - - - - - - - - - - - - - - - - - - -
  84.  
  85.  
  86. // example 3b: plain image with link to some other URL and link text (this text is optional)
  87. <imageins="456a" alt="A cartoon of a duck" caption="" class="" link="http://sidesh0w.com/" linktitle="Ethan smells like a duck. Go see his site" />
  88.  
  89. // would produce:
  90. <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>
  91. Download this code: 456b.txt

Here’s the result of example 3a:

A cartoon of a duck
GIF image with a link to a larger JPEG version

Inserting code examples into posts

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 &nbsp; 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:

  1. <?
  2. // this function is used to find the last occurance of a string within another string
  3. // it's used because strrpos() only looks for a single character, not a string
  4. function strLastPos($haystack, $needle)
  5. {
  6. // flip both strings around and search, then adjust position based on string lengths
  7. return strlen($haystack) - strlen($needle) - strpos(strrev($haystack), strrev($needle));
  8. }
  9.  
  10. // transform my little code tags
  11. function codeTransform($filename)
  12. {
  13. $filename = 'code/'.$filename.'.txt';
  14. // set vars
  15. $list = '';
  16. $cmnt = '';
  17. $multi_line_cmnt = 0;
  18. $tab = '';
  19. $class = '';
  20.  
  21. // open the file
  22. $file = fopen($filename, 'r');
  23.  
  24. // for each line in the file
  25. while (!feof($file))
  26. {
  27. // get line
  28. $line = fgets($file, 4096);
  29.  
  30. // convert tags to safe entities for display
  31. $line = htmlspecialchars($line);
  32.  
  33. // count the number of tabs at the start of the line, and set the appropriate tab class
  34. $tab = substr_count($line, "\t");
  35. $tab = ($tab > 0) ? 'tab'.$tab : '';
  36.  
  37. // remove any tabs at the start of the line
  38. $line = str_replace("\t", '', $line);
  39.  
  40. // find position of comment characters
  41. $slashslash_pos = strpos($line, '//');
  42. $apos_pos = strpos($line, "'");
  43. $slashstar_pos = strpos($line, '/*');
  44. $starslash_pos = strLastPos($line, '*/');
  45.  
  46. // if it's an ongoing multi-line comment
  47. if ($multi_line_cmnt == 1)
  48. {
  49. $cmnt = 'cmnt';
  50. $multi_line_cmnt = 1;
  51. }
  52.  
  53. // if it's not an ongoing multi-line comment
  54. if ($multi_line_cmnt <> 1)
  55. {
  56. // if it's a single line comment
  57. if (($slashslash_pos === 0) || ($apos_pos === 0))
  58. {
  59. $cmnt = 'cmnt';
  60. $multi_line_cmnt = 0;
  61. }
  62. else
  63. {
  64. $cmnt = '';
  65. $multi_line_cmnt = 0;
  66. }
  67.  
  68. // if it's potentially the start of a multi-line comment
  69. if ($slashstar_pos === 0)
  70. {
  71. $cmnt = 'cmnt';
  72.  
  73. // if multi-line comment end string is found on the same line
  74. if ($starslash_pos == (strlen($line) - 3))
  75. {
  76. $multi_line_cmnt = 0;
  77. }
  78. // if the multi-line comment end string is not found on the same line
  79. else
  80. {
  81. $multi_line_cmnt = 1;
  82. }
  83. }
  84. }
  85.  
  86. // if the line contains the multi-line end string
  87. if ($starslash_pos == (strlen($line) - 3))
  88. {
  89. $cmnt = 'cmnt';
  90. $multi_line_cmnt = 0;
  91. }
  92.  
  93. // if both cmnt and tab classes are to be applied
  94. if ( ($cmnt <> '') && ($tab <> '') )
  95. {
  96. $class = ' class="'.$tab.' '.$cmnt.'"';
  97. }
  98. // if only one class is to be applied
  99. else if ( ($cmnt <> "") || ($tab <> "") )
  100. {
  101. $class = ' class="'.$tab.$cmnt.'"';
  102. }
  103. // if no classes are to be applied
  104. else
  105. {
  106. $class = '';
  107. }
  108.  
  109. // remove return at the end of the line
  110. $line = str_replace("\n", "", $line);
  111.  
  112. // if the line is blank, put a space in to stop some browsers collapsing the line
  113. if ($line == '')
  114. {
  115. // insert all the information and close the list item
  116. $list .= '<li'.$class.'>&nbsp;</li>'."\n";
  117. }
  118. // otherwise inserts the line contents
  119. else
  120. {
  121. // insert all the information and close the list item
  122. $list .= '<li'.$class.'><code>'.$line.'</code></li>'."\n";
  123. }
  124. }
  125.  
  126. // close the finle handle
  127. fclose($file);
  128.  
  129. // add in the link to the file
  130. $list .= '<li class="source">Download this code: <a href="/blog/'.$filename.'" title="Download the above code as a text file">/'.$filename.'</a></li>';
  131.  
  132. // build the list
  133. $list = '<ol class="code">'."\n".$list."\n".'</ol>'."\n";
  134.  
  135. // return the list
  136. return $list;
  137. }
  138. ?>
  139. Download this code: 456c.txt

Here’s the accompanying CSS:

  1. /* CSS for styling code examples */
  2.  
  3. ol.code {
  4. border: 1px solid #eee;
  5. color: #333;
  6. font: normal 98%/110% Courier, monospace;
  7. margin: 0;
  8. overflow: auto;
  9. padding: 5px 5px 5px 45px;
  10. }
  11.  
  12. ol.code li {
  13. background-color: #f4f8fb;
  14. margin: 0;
  15. padding: 0 5px;
  16. }
  17.  
  18. ol.code li.source {
  19. background-color: #fff;
  20. list-style-type: none;
  21. padding: 5px;
  22. text-align: center;
  23. }
  24.  
  25. ol.code li+li {
  26. margin-top: 2px;
  27. }
  28.  
  29. ol.code li.tab1 {padding-left: 15px;}
  30. ol.code li.tab2 {padding-left: 30px;}
  31. ol.code li.tab3 {padding-left: 45px;}
  32. ol.code li.tab4 {padding-left: 60px;}
  33. ol.code li.tab5 {padding-left: 75px;}
  34. ol.code li.tab6 {padding-left: 90px;}
  35.  
  36. ol.code li code {
  37. color: #333;
  38. }
  39.  
  40. ol.code li.cmnt code {
  41. color: #824c88;
  42. }
  43. Download this code: 456d.txt

Here’s an example of how to use it all:

  1. // to insert a section of code
  2. <codeins="456d" />
  3. Download this code: 456e.txt

And here’s a screenshot of the output (showing a nonsense function I just made up) as the user will see it :

Code listing
Screen shot of the code presentation in action

And finally here’s an example of the XHTML output for the above example (jeez, this is getting loopy):

  1. <ol class="code">
  2. <li>&lt;?php</li>
  3. <li class="cmnt">// a made up function to demonstate the various aspects of the codeTransform() function</li>
  4. <li></li>
  5. <li>fooBar($a, $b)</li>
  6. <li class="tab1">{</li>
  7. <li class="tab1 cmnt">// here's a comment</li>
  8. <li class="tab1">if ($a == $b)</li>
  9. <li class="tab2">{</li>
  10. <li class="tab2">print 'toodle-whoopsey';</li>
  11. <li class="tab2">}</li>
  12. <li class="tab1 cmnt">/* and here's a multi-line comment</li>
  13. <li class="tab1 cmnt">it goes on...</li>
  14. <li class="tab1 cmnt">and on...</li>
  15. <li class="tab1 cmnt">and then ends */</li>
  16. <li class="tab1">else</li>
  17. <li class="tab2">{</li>
  18. <li class="tab2">if ($b == 10)</li>
  19. <li class="tab3">{</li>
  20. <li class="tab3 cmnt">// now we're three tabs in</li>
  21. <li class="tab3">print &quot;RAAAAAA! I'M A LION!&quot;;</li>
  22. <li class="tab3">}</li>
  23. <li class="tab2">}</li>
  24. <li class="tab1">}</li>
  25. <li>?&gt;</li>
  26. <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>
  27. </ol>
  28. Download this code: 456f.txt

Inserting conversations into posts

Actually I haven’t got around to doing this yet, but here’s an example of how I might markup a conversation in XML:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <scene>
  3. <person value="Marie">
  4. <event type="speech">What does that look like, Grandma?</event>
  5. </person>
  6.  
  7. <person value="Grandma">
  8. <event type="thought">Hmm, it looks familiar&#8230; ah, I know!</event>
  9. <event type="speech">It&#8217;s a clitoris!</event>
  10. </person>
  11.  
  12. <person value="Everyone">
  13. <event type="action">Silence</event>
  14. </person>
  15.  
  16. <person value="Grandma">
  17. <event type="speech">Oh no! I got muddled up, I mean a chrysalis!</event>
  18. </person>
  19.  
  20. <person value="Everyone">
  21. <event type="speech">Grandma!</event>
  22. </person>
  23. </scene>
  24. Download this code: 456g.txt

(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):

  1. <dl class="dialogue">
  2. <dt>Marie</dt>
  3. <dd><q>What does that look like, Grandma?</q></dd>
  4.  
  5. <dt>Grandma</dt>
  6. <dd>*Hmm, it looks familiar&&8230; ah, I know!*</dd>
  7. <dd><q>It&#8217;s a clitoris!</q></dd>
  8.  
  9. <dt>Everyone</dt>
  10. <dd>*Silence*</dd>
  11.  
  12. <dt>Grandma</dt>
  13. <dd><q>Oh no! I got muddled up, I mean a chrysalis!</q></dd>
  14.  
  15. <dt>Everyone</dt>
  16. <dd><q>Grandma!</q></dd>
  17. </dl>
  18. Download this code: 456h.txt

Putting everything together and transforming your posts

(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:

  1. <?php
  2. // include the file with the transformation scripts in
  3. include('blog/include/general_transform.php');
  4.  
  5. // grab your post_body from the db, I do it like this:
  6. // $post_body = stripslashes($row['post_body']);
  7.  
  8. // transform all the code listings
  9. $post_body = preg_replace('!<codeins="(.*?)" />!ie', "codeTransform('$1')", $post_body);
  10.  
  11. // transform all the image tags
  12. $post_body = preg_replace('!<imageins="(.*?)" alt="(.*?)" caption="(.*?)" class="(.*?)" link="(.*?)" linktitle="(.*?)" />!ie', "imageTransform('$1', '$2', '$3', '$4', '$5', '$6')", $post_body);
  13.  
  14. // $post_body now contains your complete, transformed post, ready to be printed out:
  15. print $post_body;
  16. ?>
  17. Download this code: 456i.txt

(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


Comments (44)

Jump down to the comment form ↓

  1. David:

    Wow. You really have too much time on your hands. :-)

    Posted 45 minutes after the fact
  2. Mike P.:

    Wow, and I liked how we swap heading levels for the homepage versus permalink page on our blog; peanuts that is, compared to this feast of cool work. Very nice Dunstan, and inspiring!

    Posted 46 minutes after the fact
  3. Rob Mientjes:

    Oooh... Nice! I'm definitely going to use some of that. And link back to you with a nice 25px font, yellow on red :op

    As Andrei [1] said, you are a PHP genius. Inventing new tags (well, actually letting PHP parse your nonsense in some logical way)... Pretty clevah.

    [1]: http://www.designbyfire.com/000119.html

    Posted 54 minutes after the fact
  4. David House:

    I've just skimmed over this, but it's curious that I developed a WordPress plugin [http://xmouse.ithium.net/archives/2004/07/25/grabcode-documentation] for inserting code snippets after reading the original 'version 2' post here. It's not quite as featured as yours (it doesn't differentiate between comment lines and normal ones) but it's a lot shorter and does the job.

    I thought it would be interesting to draw some parallels between our methods:

    1. you fopen() a file to read it line by line, but I use file().

    2. You use substr_count() to determine the number of tabs at the beginning of a line, whereas I (not knowing about that function) compare the strlen()s of the line as-is and ltrim()ed.

    3. We both use classes to give the lines tabs.

    4. You give the user the opportunity to download the code. I don't at the moment but it would be an easy development.

    5. You use considerably more comments than I!

    Posted 1 hour, 3 minutes after the fact
    Inspired: ↓ Dunstan
  5. Dougal Campbell:

    Great stuff, Dunstan! Thanks for continuing to share.

    BTW, minor typo in your last example:

    <dd>*Silence*</event>

    Posted 1 hour, 11 minutes after the fact
  6. David House:

    By the way, may I suggest using the XSLT functions [1] to display the conversations? It looks pretty complicated markup.

    Oh, and comment preview is having a little trouble. The spell check recognises XSLT as a spelling error early on in the post (maybe you should ignore words in all caps?) then recognises it again in the URL reference at the bottom of this comment, and spits out the string:

    XSLT.php" title="Go to [the address]">[the address]

    Seems like a bug to me.

    [1]: http://uk.php.net/manual/en/ref.xslt.php

    Posted 1 hour, 13 minutes after the fact
    Inspired: ↓ Dunstan
  7. Chad:

    Whew! That was a long post indeed, but very informative (as always). And that is some good lookin' bacon ya got there buddy!

    Posted 1 hour, 18 minutes after the fact
  8. David:

    Yum, yum! I like dinner! The rest is boring stuff (kidding, kidding. Very interesting, indeed. Thanks for sharing!)

    Posted 1 hour, 38 minutes after the fact
  9. Dunstan:

    David, the main reason our code differs so much in length is the fact that I _do_ check for the comment lines — that makes up the bulk of my code I suspect.

    But yes, it's interesting to compare the two approaches, thanks for writing them up :o)

    Posted 1 hour, 42 minutes after the fact
    Inspired by: ↑ David House
  10. Dunstan:

    Drew [1] also suggested using XSLT for the job. If I ever do choose to transform conversations then I'll certainly look into it; it seems like a good opportunity to give that technology a whirl.

    [1] http://allinthehead.com/

    Posted 1 hour, 48 minutes after the fact
    Inspired by: ↑ David House
  11. Zelnox:

    The food looks good. Share?

    Posted 3 hours, 43 minutes after the fact
  12. Scott:

    Wow Dunstan, you really are putting a lot of time into this blog. Although it's great I could never find myself doing all this =P. I'm the opposite of you, the 'non-neat-freak'. Great work though, especially displaying the code, I like it!

    PS: Your spellchecker helped me, again!

    Posted 5 hours, 43 minutes after the fact
  13. Jo-Pete Nelson:

    If I start posting more code in my own writings, then I'll definitely take a look at some of the things you do here. BTW, I don't see any pictures of food or bacon... Is the duck supposed to be food? Is this a bug on my end or yours, or was the picture changed?

    As for making sure that pictures don't break on changing the files, for my pictures section, I just hand it the folder I want to post and let PHP do the rest. I don't have to worry about making thumbnail previews, getting names right, or anything. If I want to remove a picture later, I just take it out of that folder.

    Posted 6 hours, 55 minutes after the fact
    Inspired: ↓ Dunstan
  14. Dunstan:

    JP, the picture of food is linked from the text "Right, time for dinner…" at the end of the article.

    And as for your method of displaying images, yes, that's a great way for something like a photo album, but when you're dealing with images that are inserted into specific posts, then you can't use your technique, you have to do something like I've described above, or insert images in the traditional fashion. Otherwise how would your code know where to put the images?

    I guess if you always wanted your images inserted in the same place (like at the end of a post) then you could just get a PHP script to look in an images folder and insert whatever images matched the number of the post; but that doesn't apply here, as far as I can see.

    Thanks for the comment though :o)

    Posted 12 hours, 51 minutes after the fact
    Inspired by: ↑ Jo-Pete Nelson
  15. S T E F:

    Dunstan, there's a little display bug in your example code converted to OL's, at least in Mozilla 1.5: every line is numbered "0."

    I'm sure you want a screenshot. Here goes:
    http://www.nota-bene.org/misc/tmp/dunstan_code.png

    Apart from that, I won't repeat the praise, but I do agree ;)

    Posted 13 hours, 54 minutes after the fact
    Inspired: ↓ David House
  16. Jim Amos:

    This is what Keith is talking about...details. Wow. I wish I had as much time to develop my PHP/XML knowledge. When do you work/sleep/eat etc?

    Posted 14 hours, 22 minutes after the fact
  17. David House:

    Odd, using Firefox 0.9 on windows XP, not getting that effect.

    Posted 16 hours, 19 minutes after the fact
    Inspired by: ↑ S T E F
  18. Niket:

    Wow! What separates you from most others is your attention to detail. Its amazing how much effort you put in your blog. No wonder the weekly average of your push-ups has gone down. :)

    Posted 21 hours, 20 minutes after the fact
  19. Seth Thomas Rasmussen:

    Amazing stuff. I just glanced over this, but it looks like a lot of great information that I will enjoy digesting this weekend.

    I enjoy your anal-attitude as I share it myself. On that note, kudos for the dedication, says I. :)

    Posted 23 hours, 18 minutes after the fact
  20. Steph:

    Now that's what I call clever - XML transformations are definitely the way forward.

    But why is it great inspiration only seems to come *after* all the hours of mind-numbing drudgery?

    Thanks for documenting the process and sharing.

    Posted 1 day after the fact
  21. Danithew:

    Dunstan,

    Your site is so amazing. It's fun to come here and just explore and poke around. The expertise with code and the attention to detail is really special in comparison to most blogs (mine included). It's really great of you to share your code and solutions to various problems that can come up in making posts.

    OK, that probably came across as way too much flattery, but I am being sincere.

    One of the aspects of your site that interests me the most (though there are a lot of aspects) is your exercise tracker or your pushup tracker. I don't know if you're planning to share that at all, but if so I'd definitely be interested. I used to do pushups and sittups daily and keep track of the # of them in a journal. So obviously I found that part of your blog especially interesting.

    Posted 1 day, 1 hour after the fact
  22. Taestell:

    This is excellent. The only improvement I can think of is maybe prefixing all of the tags with something unique, sort of creating your own namespace for these tags. (<dunstorch:scene></dunstorch:scene> instead of just <scene></scene>). It would keep things cleaner and separate the HTML and your custom tags.

    Also, I would've done something like <imageins id="456a" /> instead of <imageins="456a" />. The only reason is that your method isn't valid XML. Not that it matters much, since the tags will never be passed through to the browser, but I would prefer to keep custom tags as valid XML.

    Still, very good. This is very innovative.

    Final thought: While previewing this post, I just noticed the spell checker! Definitely write about this feature, too.

    Posted 1 day, 2 hours after the fact
  23. Chris Hester:

    Dunstan, I'd consider changing this line...

    "font: normal 98%/110% Courier, monospace;"

    ...to say "'Courier New', Courier" as just "Courier" on its own gives an ugly blocky font on Windows. (Whereas "Courier New" is nice and smooth like your screenshot.) Note the quote marks for the font name too.

    Wow. Now this post is just amazing. It sounds like you're half-way between HTML and XML. Why not go the whole way and convert everything to XML? That way any future redesigns should be much easier.

    I've just one question: Is all this conversion happening in real-time - every time a page is viewed? Or is it done once and a static page created?

    Oh, and your spellchecker (great touch!) for this post here is highlighting "monospace" (which was in your text!), "spellchecker" (!) and "screenshot", which I'm sure is all acceptable English.

    This blog has some truly innovative features. And I just love the use of orange.

    Posted 1 day, 2 hours after the fact
  24. Frank Hambach:

    Your code handling seems to introduce the slight disadvantage (or is it a feature?) of not being able to search inside the code examples anymore (using your build-in search). For example searching for "foreach" yields no results. Not that i can think of any reason to search for such a term.
    I presume your doing a full-text search on the untransformed blog entry in the underlying database. This also explains why searching for "imageins" lists all posts with images in it and searching for "codeins" lists all posts with code examples.
    Well, thinking about it ... this must also have worked in Version 1 of your blog and in almost every other blog (including mine) by searching for "img" and "code".

    Posted 1 day, 4 hours after the fact
    Inspired: ↓ Rob Mientjes
  25. Rob Mientjes:

    That's not all. How do you think you can find the blogged people? Sometimes, Morag Orchard is 'mother', sometimes 'mum' but never actually Morag Orchard [1]. That's a cool feature, and this proves you actually do search the XML back-end. And boy, do I love it.

    [1]: http://www.1976design.com/blog/archive/people/Morag+Orchard/

    Posted 1 day, 15 hours after the fact
    Inspired by: ↑ Frank Hambach
    Inspired: ↓ David House
  26. Chris Hester:

    It says on the blogged people list "(If you have Javascript enabled you can click the table headers to sort the data.)". Is this not working yet? It doesn't work for me. (Opera 7.53, Windows XP.)

    The blogged people list is extremely cool. But why is "The Girlfriend" not listed?

    Anyway, great work Dunstan! This blog is leading the way.

    Posted 2 days, 1 hour after the fact
  27. David House:

    I'm guessing he uses some form of name-mapping system, something like this:

    'mum mother "my mum" "Morag Orchard"' => Morag Orchard

    which then gets parsed and the system searched through the text for each of the matches. After that, my second guess would be that he reads through each of his posts and picks out each name manually.

    Posted 2 days, 21 hours after the fact
    Inspired by: ↑ Rob Mientjes
  28. Jina:

    Every time I stop by this site, I am wowed. Impressive work, Dunstan.

    Posted 2 days, 22 hours after the fact
  29. Jason:

    A template engine could have made the entire redesign process a lot easier. You wouldn't have had to go through and edit individual posts, just the templates.

    Does your CMS offer templates? If you're using an in-house written application with PHP, I would highly recommend looking into the Smarty template engine.

    http://smarty.php.net

    Posted 3 days, 5 hours after the fact
  30. ACJ:

    Very interesting and informative read, Dustin. I can't wait for the next re-design break-down (and hope you don't stop "Giving Away All [Your] Secrets").

    As for the (XML) markup of dialogues: to me it seems to make more sense for <person> to be a subset of <event>, than the other way around. Then again, I'm reading this at 5:30 in the morning.

    My 2 ct.

    Posted 3 days, 8 hours after the fact
    Inspired: ↓ Andrew Hayward
  31. Andrew Hayward:

    ACJ, I think what Dunstan means as an event might be more appropriately described as an "action" on the individual's part - <action type="speech">...</action>. However, I'm not sure how you'd get around the type="action" with this though.

    Posted 3 days, 16 hours after the fact
    Inspired by: ↑ ACJ
  32. Jonathan:

    I think your site and CSS style is really amazing. Thanks for being inspiring.

    Posted 4 days, 11 hours after the fact
  33. Landon:

    Dunstan, I'm not too good with xml so I'm trying to figure out how it comes into play when you are inserting code into a post.

    I'm currently using a system with <pre> tags as you mentioned, but it definitely has bugs. I would prefer to do it the way you've done so brilliantly.

    I'm getting the impression you have to manually upload the code as a text document to a specified location (i.e. /code). Then you would refer to it using xml in your blog entry? How does the xml come in? Perhaps there is an xml resource for us weak-minded ones that tells us how to inherit your amazing powers.

    Posted 5 days, 1 hour after the fact
  34. Greg Stewart:

    Inspiring work. In fact so much so, that I ported your code view to ColdFusion (http://www.tcias.co.uk/projects/codeView/index.cfm). Hope you don't mind!

    Cheers
    Greg

    Posted 1 week after the fact
    Inspired: ↓ Dunstan
  35. Dunstan:

    Excellent, thanks Greg!

    Posted 1 week after the fact
    Inspired by: ↑ Greg Stewart
  36. Isaac Schlueter:

    Thanks a lot Dunstan... You just keep making my to-do list get longer and longer with these great ideas! ;) Seriously, this is genius.

    You might be able to make the <codeins /> output even prettier with the highlight-string function http://us4.php.net/manual/en/function.highlight-string.php
    Of course, it uses FONT tags instead of CSS in php4, but that's changed in php5.

    Posted 1 week, 3 days after the fact
  37. Vincent Grouls:

    With regards to using PHP's built-in highlight_file, I prepared the following example: http://vincentg.sytes.net/view_source.phps

    You can use this to parse custom tags as well, by using a regular expression:
    $html = preg_replace('#<codeins="(.*?)" />#e', "view_source('1')", $html);

    I hope it's of use to someone :-).

    Posted 1 week, 6 days after the fact
  38. Mark J:

    This is certainly the first time I've ever had an idea that Dunstan has independently come up with as well. I happen to use a NICEIMG nonsense tag, and then use preg functions to format it the way I like. I even take the "title" attribute and stick it below the image. Also, doing this, you can center your DIVs, because you know how wide the image is, and a little math can figure out the rest.

    Example: http://txfx.net/2004/05/01/boring-day/

    Posted 2 weeks, 6 days after the fact
  39. Jesper Christiansen:

    These code snippets are awesome! I especially like the code-one! :)

    Posted 2 weeks, 6 days after the fact
  40. Justin French:

    The only addition I can make (and I think it's an important one) is to give any custom tags a prefix, so that there's less chance of you overlapping with future versions of published namespaces like XHTML, RSS, etc. Sure, there's no <person> tag yet, but there may be one day. Instead of <person />, I'd recommend <dun:person /> or <ndt:person /> (Naked Dunstan Technologies).

    Posted 1 month, 1 week after the fact
    Inspired: ↓ Dunstan
  41. Dunstan:

    Justin, I already do that: <dro:person value=""></dro:person>

    Posted 1 month, 1 week after the fact
    Inspired by: ↑ Justin French
  42. Ramin:

    Like some others have commented, a lot of the custom tag transformations that you are doing or planning on doing is best done using XSLT. But be aware of the overhead associated with doing xml/xsl transformations. There are libraries out there that will cache the results for you to enhance performance.

    If you were using a Java based solution, I would recommend the Apache Cocoon framework, as it is a completely xml based framework. But for a simple blog site (I use the word simple loosely here, as there isn't anything simple about this site!), Cocoon may be overkill.

    Great job on the site! Keep up the good work and don't let the perfectionist in you die out.

    Posted 9 months, 4 weeks after the fact
  43. Jan!:

    What happens if someone enters the following code?

    <codeins="'.`id`'" />

    Wouldn't that execute the "id" command on your web server? Or is it only limited to yourself (and possible other people you trust)?

    Posted 10 months, 3 weeks after the fact
  44. Marazmus:

    Sorry for my english, i am from Russia.

    Can you help me - how to "include" this method/function into my Textpattern-based site?

    Thank you very much.

    Posted 1 year, 6 months after the fact

Jump up to the start of the post


Add your comment

I'm sorry, but comments can no longer be posted to this blog.