Category: Implementation Main Categories

Updated Today “Lite” v0.2: slight tweaks should be more compatible

Chris K was kind enough to review the tweaks I had made to his original Updated Today plugin . He endorsed the removal of pngfix.js but complained that my use of bloginfo() caused the new plugin to fail on his WordPress install. He also revealed his intent to make certain features of the plugin available to an admin page, requiring that portions of the output be salted away in variables for future manipulation. I was also dissatisfied with the amount of repetition in my otherwise very short plugin.

Here is the new code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/* config variables (later to be made available to an admin menu) */
 
$UTL['element']    = 'div';
$UTL['elementId']  = 'updated';
$UTL['imageUrl']   = get_bloginfo('url') . '/wp-content/plugins/updatedtodaylite/updatedbanner.png';
$UTL['style']      = 'position: absolute; display: block; top: 0px; left: 0px; height: 120px; width: 120px; z-index: 99;';
 
/* set up the action */
 
function showUpdateBanner() {
    global $UTL;
	$today = date("Y-m-d");
    $query = "SELECT post_date, id FROM wp_posts WHERE wp_posts.post_date LIKE '" . $today . "%'"; //will need to sort by date/time to enable the element to be a link to the most recet content
    $results = mysql_query($query);
    if (mysql_num_rows($results) > 0) {
	    $msieRegex = '/msie\s(5\.[5-9]|[6]\.[0-9]*).*(win)/i';
		if( isset($_SERVER['HTTP_USER_AGENT']) && preg_match($msieRegex,$_SERVER['HTTP_USER_AGENT']) && !preg_match('/opera/i',$_SERVER['HTTP_USER_AGENT']) ) {
			$UTL['style'] .= " filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(enabled=true, sizingMethod=scale, src=\"$UTL[imageUrl]\")";
		}
		else {
			$UTL['style'] .= " background-image: url($UTL[imageUrl])";
		}
		echo "<$UTL[element] id=\"$UTL[elementId]\" style=\"$UTL[style]\"></$UTL[element]>";
	}
}
 
/* make it happen */
 
add_action('wp_footer', 'showUpdateBanner');

This has the following improvements over my previous attempt:

  • You can choose what sort of element to wrap the image in.
  • Uses get_bloginfo() rather than bloginfo() to prevent parsing errors
  • Exposes inline styles, element type, element ID, and image URL as variables
  • These config variables are wrapped up in an array to prevent stepping on toes
  • USER_AGENT condition adds only the appropriate style to the inline styles
  • Tolerant of an empty style variable, allowing folks to handle placement in a separate stylesheet if they like
  • We only output any HTML once in the script

Still not much to it. Download the new plugin: Updated Today “Lite” v0.2

In the future, it would be nice to:

  • Make this a clickable link to the most recent article, ideally on a page containing all recent content.
  • Make it appear if the site has been updated in the past 23 hours or so, since I like to post at night, causing the banner to be visible for a only few hours at most.

Updated Today “Lite” – A lesser and lighter version of the “Updated Today” WordPress plugin

Courtesy of Weblog Tools Collection comes Chris K ’s Updated Today plugin , which displays a banner at the top left of your WordPress blog if a post exists in the loop with today’s date as its post date. It is a simple and effective idea, and coded without much fuss; it uses the wp_head and wp_footer hooks to add styles to the page, write a SCRIPT tag, and conditionally write an IMG wrapped in a DIV . And it seems to work great.

Upon further inspection I noticed that the plugin relies on pngfix.js to make IE 5.5 and IE 6 show alpha transparency on the PNG used for the banner. This is necessary because of the nice drop shadow that on the “updated today” banner; that shadow has to work well overlaid on any content that happens to be present, so alpha blending is essential. pngfix.js iterates over all of the IMG elements in the document; whenever it finds a PNG it replaces that PNG with a SPAN and uses the Microsoft-proprietary DXImageTransform.Microsoft.AlphaImageLoader to display the PNG within that SPAN.

There’s nothing wrong with this technique, selectively used. But I didn’t want pngfix.js tromping all over my site rewriting my DOM just because I used PNGs. I like PNGs, and I don’t want them messed with willy-nilly. (Never mind that pngfix.js also does not pass jslint .) I wanted a quick fix that would not require this script.

I also wasn’t sure I wanted an additional stylesheet added to the page containing one selector and rules to style the added DIV . I could rip these styles out and place them in my own stylesheet, but in the interest of a quick fix, I decided to leave them in the plugin.

Here is the original PHP code for the plugin.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
/* ### Configuration Variables ### */
 
$conf_manual_placement = false;
$conf_manual_style = false;
$conf_use_pngfix = true;
 
/* ### End Configuration Variables ### */
 
$banneradded = null;
 
add_action('wp_head', 'ck_wp_head');
if (!$conf_manual_placement) {
    add_action('wp_footer', 'ck_wp_footer');
}
 
function ck_wp_head ()
{
    global $conf_manual_style, $conf_use_pngfix;
    if (!$conf_manual_style) {
        echo '<style type="text/css" media="screen"> #updated { position: absolute; display: block; top: 0px; left: 0px; height: 120px; width: 120px; z-index: 99; } </style>';
    }
    if ($conf_use_pngfix) {
        echo '<!--[if lt IE 7]><script defer type="text/javascript" src="wp-content/plugins/updatedtoday/pngfix.js"><!--[endif]-->';
    }
}
 
function updated_banner()
{
    $today = date("Y-m-d");
    $query = "SELECT post_date, id FROM wp_posts WHERE wp_posts.post_date LIKE '".$today."%'";
    $results = mysql_query($query);
    $num_results = mysql_num_rows($results);
    if ($num_results > 0) {
        $results_assoc = mysql_fetch_assoc($results);
        $postid = $results_assoc['id'];
	echo '<p id="updated"><img src="<?php bloginfo('url'); ?>/wp-content/plugins/updatedtoday/updated.png" border="0" /></p>';
    }
}
 
function ck_wp_footer ()
{
    updated_banner();
}

Not much to it, really, but more than I’d like. Near as I can tell, $banneradded is not used. That bit about $results_assoc = mysql_fetch_assoc($results); $postid = $results_assoc['id']; seems to be the beginnings of making the banner a link to the most recent post. To make sure that would work we’ll need to change the SELECT to ensure the results are sorted by date, so we’ll trim that for now, and leave it on the to-do list for a later version.

If we put the styles inline on the DIV and make the image a background of that DIV , we solve the transparency problem in IE for indexed PNGs without need of pngfix.js and eliminate our need to hook the wp_head event. But we’d like a nice dropshadow even in IE. The quickest way is to choose one of two similar DIV s, one containing the proprietary AlphaImageLoader CSS filter, based on PHP’s HTTP_USER_AGENT . This leaves the much shorter:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
add_action('wp_footer', 'showUpdatedBanner');
 
function showUpdatedBanner() {
    $today = date("Y-m-d");
    $query = "SELECT post_date, id FROM wp_posts WHERE wp_posts.post_date LIKE '".$today."%'";
    $results = mysql_query($query);
    $num_results = mysql_num_rows($results);
    if ($num_results > 0) :
        $msie='/msies(5.[5-9]|[6].[0-9]*).*(win)/i';
        if( isset($_SERVER['HTTP_USER_AGENT']) && preg_match($msie,$_SERVER['HTTP_USER_AGENT']) && !preg_match('/opera/i',$_SERVER['HTTP_USER_AGENT']) ) :
            echo("<div id=\"updated\" style=\"position: absolute; display: block; top: 0px; left: 0px; height: 120px; width: 120px; z-index: 99; filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(enabled=true, sizingMethod=scale, src='" . bloginfo('url') . "/wp-content/plugins/updatedtodaylite/updatedbanner.png');\"></div>");
        else :
            echo("<div id=\"updated\" style=\"position: absolute; display: block; top: 0px; left: 0px; height: 120px; width: 120px; z-index: 99; background-image: url( " . bloginfo('url') . "/wp-content/plugins/updatedtodaylite/updatedbanner.png );\"></div>");
        endif;
    endif;
}

This is much simpler, does the same work, and can easily be altered to be more semantically appropriate (a topic for later). Download the new plugin: Updated Today “Lite” .

I’m picky about pullquotes

No, really.

In a magazine, a pullquote can pull a browsing reader into an article by highlighting a provocative comment or deft turn of phrase, or help steel a reader’s resolve to press on, teasing them with a little deferred gratification. That magazine-ey feel can be helpful on sites with long, multi-page articles that are otherwise relatively unbroken by pictures, blockquotes, or other places for the eye to rest.

There are many examples of pullquotes on the web. But each fails to satisfy in some way.

Many authors add the content of the pullquote to the document , unnecessarily repeating content or interrupting the HTML stream with a non-sequitur. This could have a jarring effect on a reader browsing without benefit of CSS, or using a screen reader (unless the CSS rule speak: none were used by the author and supported by the reader).

Many go a step further, identifying content that would make a nice pullquote, wrapping it in a span and using JavaScript to replicate it in the DOM. Roger Johansson’s technique is the definitive example of this method. This is great because we do not have excess content for what is essentially a presentational effect. It does put the pullquote right next to its source text, though, which is unfortunate.

Also, the above method requires your pullquote to be a direct quote of the source text. Pullquote text often does not exactly match its source. Dependent clauses are trimmed, prepositions adjusted, other editing is done to allow the pullquote to stand alone. This requirement led me to toy with the notion of using JavaScript to collect one or more spans selected from the text into a single pullquote, but I stopped when I realized that that would only allow me to drop words from the source text, not change them if necessary.

So far we’ve identified the following requirements:

  1. No repeated or extra content
  2. Control over pullquote placement in source text
  3. Pullquote text not necessarily a direct copy of source text

If we relax the first requirement slightly, this can be done. We might add an empty span to our HTML where we want the pullquote to appear, and place the content for the pullquote in its title attribute. We then use some JavaScript to find this empty span and put its title text inside. A little CSS makes it pretty. I’ve made an example page with very simple code so the effect is obvious.

A JavaScript date formatter

Some time ago I was asked to build a multi-day date-picking interface that was to accept dates selected from a calendar and display them in a list without making a round-trip to the server. Since this was for a web application with an international audience, I needed to handle different date formats (localization) and languages (translation). Translation was no big deal; we had long ago established the convention of receiving from the server a JSON object with the necessary terms and phrases, populated by resource bundles. But the date formatting was another matter. And since the back-end was in Java, it would be nice to use the same date-formatting strings that the back-end guys were using, to make adding new locales easier. My formatDate() was the result:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
/**
 * formatDate()
 * - follows Java SimpleDateFormat pattern conventions but does not support time zones
 * - depends on dateLanguage object
 *
 * @param   dDate        a javascript date object
 * @param   patternStr   a Java-style date format string
 * @return  string representation of the date
 */
function formatDate(dDate, patternStr) {
 
    /* interior functions */
 
    //countTokenLength() counts the number of repetitions of the individual date format token appearing next in the string
    function countTokenLength(str) {
        var re = /^([a-zA-Z])(1){0,100}/g;
        var matchStr = str.match(re);
        if (!matchStr) {
            return 1;
        }
        return matchStr[0].length;
    }
 
    //padResult() adds copies of padStr to num until it is as wide as necessary
    //in retrospect, this could be made much shorter and more efficient
    function padResult(num, width, padStr) { //dependent function
        var returnStr = "";
        if (width &gt; 1) {
            var padLength = width - num.toString().length;
            if (padLength) {
                for (i=0; i&lt;padLength; i++) {
                    returnStr += padStr;
                }
            }
        }
        returnStr += num.toString();
        return returnStr;
    }
 
    //here is where the action begins
    var resultStr = "";
    var subPatternLength = 0;
    while (patternStr.length &gt; 0) {
        subPatternLength = countTokenLength(patternStr);
        var token = patternStr.substr(0,1);
        switch (token) {
        case "G":
            resultStr += (dDate.getFullYear() &gt;=0) ? dateLanguage.eraNames[1] : dateLanguage.eraNames[0];
            break;
        case "y":
            var x = dDate.getFullYear().toString();
            if (dDate.getFullYear() &gt; 999) {
                if (subPatternLength &gt; 2) {
                    resultStr += x;
                }
                else {
                    resultStr += x.substring(x.length - 2, x.length);
                }
            }
            else {
                resultStr += x + " " + ((dDate.getFullYear() &gt;=0) ? dateLanguage.eraNames[1] : dateLanguage.eraNames[0]);
            }
            break;
        case "M":
            if (subPatternLength &gt; 2) {
                resultStr += (subPatternLength == 3) ? dateLanguage.monthNamesShort[dDate.getMonth()] : dateLanguage.monthNames[dDate.getMonth()];
            }
            else {
                resultStr += padResult(dDate.getMonth(), subPatternLength, '0');
            }
            break;
        case "w":
            var resultLength = (subPatternLength &gt;= 2) ? 2 : 1;
            resultStr += "[week in year]";
            break;
        case "W":
            var resultLength = (subPatternLength &gt;= 2) ? 2 : 1;
            resultStr += "[week in month]";
            break;
        case "D":
            var resultLength = (subPatternLength &gt;= 2) ? 2 : 1;
            resultStr += "[day in year]";
            break;
        case "d":
            var resultLength = (subPatternLength &gt;= 2) ? 2 : 1;
            resultStr += padResult(dDate.getDate(), resultLength, '0');
            break;
        case "F":
            var resultLength = (subPatternLength &gt;= 2) ? 2 : 1;
            resultStr += "[day of week in month]";
            break;
        case "E":
            switch (subPatternLength) {
            case 1:
                resultStr += dateLanguage.weekDayNamesAbbrev[dDate.getDay()];
                break;
            case 2:
                resultStr += dateLanguage.weekDayNamesTiny[dDate.getDay()];
                break;
            case 3:
                resultStr += dateLanguage.weekDayNamesShort[dDate.getDay()];
                break;
            default:
                resultStr += dateLanguage.weekDayNames[dDate.getDay()];
                break;
            }
            break;
        case "a":
            resultStr += (dDate.getHours() &lt; 12) ? dateLanguage.amPmNames[0] : dateLanguage.amPmNames[1];
            break;
        case "H":
            var resultLength = (subPatternLength &gt;= 2) ? 2 : 1;
            resultStr += padResult(dDate.getHours(), resultLength, '0');
            break;
        case "k":
            var resultLength = (subPatternLength &gt;= 2) ? 2 : 1;
            if (dDate.getHours() == 0) resultStr += "24";
            else resultStr +=padResult(dDate.getHours(), resultLength, '0');
            break;
        case "K":
            var resultLength = (subPatternLength &gt;= 2) ? 2 : 1;
            var adjust = (dDate.getHours() &gt;= 12) ? -12 : 0;
            resultStr += padResult((dDate.getHours() + adjust), resultLength, '0');
            break;
        case "h":
            var resultLength = (subPatternLength &gt;= 2) ? 2 : 1;
            var adjust = (dDate.getHours() &gt;= 12) ? -12 : 0;
            if ((dDate.getHours() + adjust) == 0) {
                resultStr += "12";
            }
            else {
                resultStr += padResult((dDate.getHours() + adjust), resultLength, '0');
            }
            break;
        case "m":
            var resultLength = (subPatternLength &gt;= 2) ? 2 : 1;
            resultStr += padResult(dDate.getMinutes(), resultLength, '0');
            break;
        case "s":
            var resultLength = (subPatternLength &gt;= 2) ? 2 : 1;
            resultStr += padResult(dDate.getSeconds(), resultLength, '0');
            break;
        case "S":
            resultStr += dDate.getTime();
            break;
        case "z":
            resultStr += "[general time zone]";
            break;
        case "Z":
            resultStr += "[rfc 822 time zone]";
            break;
        case "'":
            if (patternStr.substr(1,1) == "'") {
                resultStr += "'";
                subPatternLength = 2;
            }
            else {
                var txt = patternStr.match(/^'([^']|'')*'/g)[0];
                subPatternLength = txt.length;
                var strippedTxt = txt.substring(1, txt.length-1).replace(/''/g, "'");
                resultStr += strippedTxt;
            }
            break;
        default:
            var alphaRe = new RegExp("[A-Za-z]", "g")
            if (!token.match(alphaRe)) {
                resultStr += token;
                subPatternLength = 1;
            }
            break;
        }
        patternStr = patternStr.substring(subPatternLength, patternStr.length);
    }
    return resultStr;
}
 
var dateLanguage = {
    monthNames : ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
    monthNamesShort : ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    weekDayNames : ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
    weekDayNamesShort : ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    weekDayNamesTiny : ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'],
    weekDayNamesAbbrev : ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    eraNames : ['BC', 'AD'],
    amPmNames : ['AM', 'PM']
};

The core of this routine is a simple regular expression that counts the number of subsequent occurrences of the first token in the string. Once we have the token and its length, we add the correctly formatted date string to the result, trim the used token, and go on to the next one.

There are a few items not yet implemented. The Java date tokens w (week in year), W (week in month), D (day in year), and F (day of week in month) weren’t necessary for the project, nor were the two time zone tokens, z and Z, because we were recording and redisplaying unadjusted dates and times reported by the user, making time zone immaterial.

This might come in handy later: ECML v1.1: Field Specifications for E-Commerce

Google has an interesting collection of pages up treating statistics surrounding the construction of web pages . An interesting read, if you’re into that sort of thing.

Actual user testing of pages read by a screen reader? To determine if source order and skip links really matter? Brilliant !

Making overloading the class attribute easier

It is becoming fashionable to use JavaScript to add behavior to a page without writing it into the (x)HTML. This is a good thing, in my view; it further distinguishes content from interface behavior, makes the code tighter, smaller, more human-readable, and makes it more resilient to the well-meaning machinations of the back-end guys.

Recently, noting the many different forms of (front-end) validation that would be necessary in a web application, I considered getting into using custom attributes to carry relevant metadata. I was considering something like this:

<input type="text" required="true" validation="float" value="" />

which would have all of the aforementioned advantages, and allow all manner of interesting behaviors to be attached at runtime with minimal difficulty. But since no flavor of XHTML or HTML allows the use of custom attributes without inclusion of a custom DTD , which is a big hassle, and since I had recently made validation a portion of some automated test cases, I had to abandon that route. I’ll pursue it elsewhere, naturally. But if you want your pages to validate, or if you care that your web-building friends will clown you mercilessly if your pages do not, this fact can spell trouble.

The obvious alternative is to overload the class attribute. This is already becoming common for styling reasons, since the modern browsers and recent flavors of Interner Explorer all support putting multiple class names in the class attribute, delimited by spaces. Since we can easily get the contents of the class attribute via JavaScript, why not stuff class full of metadata? It is safe and it validates.

Two reasons. One, I like to label my metadata. When you are no longer working at your current job, or on your current project, or when this phase of this project is over, someone else is going to have to look at the code, come to an understanding of it. You should be making it easy for them, or for yourself (because you’ll forget what you meant by class="topmatter required multi" ). Two, It has also become common to change or destroy classes as part of CSS-enabled JavaScript interface behaviors. When the class name is no longer a single value, this becomes more complicated.

They are trivial, really, but I’ve whipped up some simple functions to help manage this situation. Unfortunately Ineternet Explorer doesn’t let you add methods to the Node prototype, or these would be even simpler, even tighter, even more useful. But here they are (mostly self-documenting):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
function addClass(element, classStr) {
  if (!element.className) {
    element.className = classStr;
  }
  else {
    element.className = element.className + " " + classStr;
  }
  return this;
}
 
function removeClass(element, classStr) {
  if (!!element.className) {
    var re = new RegExp(classStr);
    element.className = element.className.replace(re, "");
  }
  return this;
}
 
function containsClass(element, classStr) {
  var re = new RegExp(classStr);
  if (!!element.className &amp;&amp; element.className.match(re)) {
    return true;
  }
  return false;
}

Granted, these aren’t exactly as squeaky-clean as they could be. removeClass() does nothing about any extraneous spaces that might be left in the class name, which shouldn’t matter unless you use these functions to change the add and remove classes on a single element many many times in a page. And addClass() doesn’t check to see whether or not the new class is already present, it just tacks it on to the end. I’ll extend these later, but I wanted to make sure they were very tight, very efficient.

Per-site user stylesheets, a friendlier way

I’m intrigued by the possibilities of user stylesheets , currently supported reasonably well by the Mozilla browsers (although I have to bounce Firefox 0.8x to see changes; I haven’t investigated the capabilities of other browsers). The basic idea is that in addition to the site author’s stylesheet, you should be able to apply a stylesheet of your own choosing/making to whatever site you like, thereby changing fonts, colors, layout, what have you to suit your as-yet-nascent nefarious aims.

Eric Meyer had a post or two related to this a while ago, regarding ” css signatures ” for sites. He advocated putting an id on the body tag, wherein the site is identified. For example, I might put an id on this site’s body tags of misterjon-com . I was immediately taken aback by this suggestion, both in 1) the simplicity and appropriateness of the central idea and 2) the potential misguidedness of the implementation. Yes, a CSS-accessible way to identify the site is a splendid idea. No, the body tag might not be the right place for it, since I think of the body as the document and most sites are composed of several documents; to give each docment the same ID would be, well, odd, like naming all of your sons George . And I (and many others) have recently been known to use an id on the body to identify where the heck I am in a site (which document, perhaps), for purposes of highlighting a portion of the navigation, etc. Perhaps an id on the html tag would work?

This is sort of a warmish topic lately, a handful of the big bloggers have been on about it briefly.

Meanwhile, what about all of those sites that aren’t putting CSS signatures somewhere useful? Ryan Tomayko proposes a solution. In his article, ” Per Site User Stylesheets ,” Ryan outlines a way to insert signatures for sites that don’t have them. At its core, his code munges the host portion of window.location and sets an id on the body tag to match, allowing you to refer to body#slashdot-org or what have you in your user stylesheet. Read his article for the details when you finally get fed up with the default font on Slashdot, for example.

Trouble is, Mr. Tomayko’s technique mercilessly breaks sites that already use an id on the body, since whatever was there is replaced by the results of his script.

But there is an alternative. Set a class on body. The modern browsers all support multiple classes, space-delimited. The critical portion of Mr. Tomayko’s code is as follows:

1
2
3
4
<![CDATA[
    var sig = window.location.host.replace(/./g, '-');
    this.setAttribute('id', sig);
]]>

Changing that to

1
2
3
4
5
<![CDATA[
    var sig = window.location.host.replace(/./g, '-');
    classStr = !!this.className ? this.className + " " + sig : sig;
    this.className = classStr;
]]>

sets a class on body without destroying existing work, allowing you to neatly grab the body tag in your user stylesheet with body.slashdot-org . Problem solved.

I haven’t yet tried an id on html. Anyone?

Quick and dirty PMS/RGB chart

A number of individuals have posted on their sites handy Pantone Matching System (PMS) to RGB color conversion charts. A freshly-minted example of such a chart has made the blog circuit recently (but I’ve lost the link already). Since there are so many PMS colors, even the standards-based online PMS charts can be hefty downloads.

Poking around the net, I found one such chart that seemed official, it being Pantone branded and all (sorry, lost it just as fast). It was a pretty simple matter to use a regular expression to strip the color names and their RGB approximations out of the HTML and slap ‘em into a JSON object, then write a little script to crawl that object and write some simple XHTML to display a panel of swatches.

You can grab it at http://jonplummer.com/misc/pms.html . I suspect I’ll be making this prettier (and searchable, etc.) later, but at the moment it is reasonably useful, and a svelte 24KB (as opposed to the 240KB or so of many other examples). I’d love to hear your ideas about how to make this set of swatches more useful.

Semiautomatic syntax checking of JavaScript

Recently, Dave Shea lamented that ” there are no automated tools to validate Javascript. ” This is true, but such a tool is well within reach. And with a little bit of work, you can have your own.

Every person poking around with JavaScript needs to be aware of the fine work of Douglass Crockford , JavaScript guru of gurus. On his site he has, in addition to very nice articles about Object-Oriented JavaScript, remedial JavaScript, and the very useful JavaScript Object Notation (JSON), a tool of alarming genius called jsLint . He describes it as follows:

When C was a young programming language, there were several common programming errors that were not caught by the primitive compilers, so an accessory program called lint was developed which would scan a source file, looking for problems. […] jslint takes a JavaScript source and scans it. If it finds a problem, it returns a message describing the problem and an approximate location within the source. The problem is not necessarily an error, although it often is. jslint looks at some style conventions as well as structural problems. It does not prove that your program is correct. It just provides another set of eyes to help spot problems.

This is great, super helpful. And it is written in JavaScript! Kick it around a bit; throw your favorite scripts at it and let it tell you just what an amateur you are. But at best it is a semi-automatic syntax checker; you have to paste in the script and pull the trigger. By bring in yet another excellent and largely underutilized JavaScript tool, we can take this “automated JavaScript validation tool” idea the rest of the way.

Somewhat less easy to use, but equally useful, is jsUnit , Edward Hieatt ’s JavaScript port of the popular Java unit testing framework jUnit.

What? You don’t unit test your JavaScript? It is true that in most projects you can get away without, but as I work on more complex things (especially when I’m validating a lot of user input, or localizing numbers and dates [more about this later]) I’m finding it increasingly useful. As my scripts become more interdependent it is ever more important to regression test (Did this change I just made break anything that depends on this bit of script?).

I won’t go into how to set up and use jsUnit, as it can seem a bit fiddly and installation-dependent at first (it isn’t so bad, but the documentation does nearly zero handholding). But jsUnit, in that it can invoke each of many test scripts in a test page and reports the results, can bring us much closer to the holy grail of automated syntax checking. In any case I’m going to blaze along with minimal handholding as if you are able to follow along at home. Other things we’ll need: a local copy of jsLint, a way of loading JavaScript files as strings, a test page that can do that for us, and a test function that asserts that the script we just loaded passes jsLint.

Douglass Crockford freely provides a copy of his jsLint parser at http://www.crockford.com/javascript/fulljslint.js . The jslint object that his script creates takes some booleans for three important options, accepts a string when called as a method, generates a report, and slaps the report into jslint.report . We’ll need to add a little helper function to our test page that sets these options and returns the report) that he makes available, like so:

1
2
3
4
5
6
7
function go(str) {
    jslint.laxLineEnd=!lintOptions.requireStrictLineEndings;
    jslint.plusplus=lintOptions.dislikePreAndPostfixIncrements;
    jslint.jscript=lintOptions.tolerateJscript;
    jslint(str);
    return jslint.report();
}

Note that I’m planning to use the properties of a lintOptions object to carry the options.

Just a moment ago I wrote, “a way of loading JavaScript files as strings.” This is a problem because (as far as I know) client-side JavaScript doesn’t open files, and jsUnit runs in a browser. We can, however, use a server-side include (or a JSP include, or a PHP include, or other server-side means) to write the JavaScript file of our choice into a textarea and get it as a string that way. Our test page will need to load the file into the textarea, get the contents, pass them to the helper function, and assert that the jslint report is OK.

A jsUnit test page is a rudimentary HTML document with a few important pieces:

  1. a script tag including jsUnitCore.js
  2. a script tag including the script being tested
  3. test functions, which interact with the script being tested
  4. optional HTML elements that are convenient to the testing of the script being tested

A test page can also contain setUp() and tearDown() functions that are run before and after (respectively) each test function. In our case, we’ll need to use setUp() to set the lint options and get the contents of the textarea, like so:

1
2
3
4
5
6
7
8
function setUp() {
    lintOptions = {
        requireStrictLineEndings : true,
        dislikePreAndPostfixIncrements : false,
        tolerateJscript : false
    };
    lintSrc = lintSrcElement.value;
}

We’ll also need to write a test function that makes use of all of this:

1
2
3
4
5
6
7
8
9
10
11
function testRunJsLint() {
    var result = go(lintSrc);
    if (result.substr(0,2)!='ok') {
        result=result.entityify();
        lintPass = false;
    }
    else {
        lintPass = true;
    }
    assert(result, lintPass);
}

Ideally you can infer from the code snippets above what HTML is required in the test page. I recognize that this post is short on handholding; I plan to continue to polish it.

It’s time to learn about OO

I’ve talked a good game about wanting to transform this nascent weblog into an all-singing, all-dancing, object-oriented online application(tm), but have gained little traction in building what will eventually become punyMVc, a minimal Model-View-controller (little “c” on purpose) not-so-much-a-framework-as-a-technique. Why is that?

Yes, lack of time, and perhaps lack of prioritization. But more than that, I think, the various books I have been reading (1) and websites I have been browsing (2) all glance off of the topic of how to get started using OO techniques rather than really talking about how to get the job done. In general, a book or a site will explain (philosophically) the three pillars of OO (3) and display some elementary code that covers basic techniques of OO programming style (4) but does nothing that you’d actually want to bother doing, then going on to do some hand-waving about how great this can be if you are writing something really complicated. As is so often the case, the meaty and interesting middle of the learning curve is missing.

Frankly, I find this all rather unsatisfying. I’d like to learn about this on my own time, without soaking up the time of my more development-oriented coworkers. I’d like not to have to take a class. I’d like to build my own skill base, at home.

Lately I’ve had a little more success perusing the mother of all wikis , but it is slow going, as I have to do a lot of browsing to find the nuggets.

Ideas?

Notes:

  1. Books:
  2. Sites:
  3. Encapsulation, Inheritance, Polymorphism
  4. Grouping a data structure and the methods to act upon it into a single entity, controlling acess to the internal workings of that entity, passing references to objects around so that they are acted upon directly, etc.

Work continues apace

Work (in the immortal words of Scott ) continues apace, with me flying to Indianapolis on Monday to meet one of our biggest customers to pick her brain for enhancements to one of our online therapy management tools, all in the shadow of yet another looming round of layoffs. No discernible buzz this time about our group getting hacked at (and we’re as small as we can be), so it’s all of us or none of us, I wager. Actually, work is tougher but funner than it has been in the past, mostly because I (and Nils, my UE colleague) have abandoned fear! in favor of some organizing principles for our work.

  1. Do not act solely of fear
  2. Be skeptical of orders that are borne of fear
  3. Do not build anything that won’t get used
  4. Do not build anything that won’t carry the story forward in some meaningful way
  5. Apply good design AND good programming practices to everything that we do build (now including automated regression testing, syntax checking, minimal markup, hardcore modularization, and the like).

It is amazing what an effect 1 and 2 have had. That’s right, folks, abandon fear!, just step over to the tent and have some Kool-Aid… While it is impossible to abandon fear entirely, reminding ourselves to put it in its place has been quite useful. Certain competitive threats are shown to be meaningless inconveniences, while others loom only appropriately large. Bad ideas borne of fear are shown to be bad ideas, and abandoned. A greater honesty, resolve, and professionalism are apparent in every communication.

Of course, without 3 and 4, the first two might still have you building useless things, an egregious waste of your effort and the user’s time. Since it is far chaper to do the hard work ourselves than to multiply small bits of hard work over the entire user base, we must be willing to adhere to 3 and 4.

5 speaks for itself. A thing to be made should be made well, and one should not shy from using whatever tools or techniques are necessary to help one build a solid product, even (especially) if that includes asking others for help. Naturally, failing to ask others for help is usually borne of fear!, bringing us back to point 1.

How far should we push semantic markup?

Occasionally I run across a nifty semantic markup ponderable, such as those posed by Dan ” Simple Bits ” Cederholm in his quasiperiodic ” SimpleQuiz .” (If you haven’t checked it out already, do; it’s a fine example of good thought applied to meaningful, useful markup.)

I find these questions intriguing, and I strive to use the “right” markup wherever possible, selecting tags and structures that at least share a strong kinship with the meaning of their contents or the function of those contents within the document. But it is easy to overthink this.

In any case, I’m currently working on a web app that involves a fair number of forms. Most of the pages end with what we call “pagenav,” process-oriented navigational elements that take you forward or back in a process, or off to do another likely task once you are done. In general there’s a clickable thing of some kind to the right (to continue), often there is one or more to the left (go back, undo), etc.

Some time ago we vastly simplified the markup from a byzantine JavaScript and table structure to a simple div containing anchors, generally with class=”forward” or class=”back” as needed. But this is still a bit far from what is coming to be considered the right way to markup navigational links, an unordered list. And it isn’t terribly accessible, since some screen readers will read the collection of links as a single nonsensical sentence.

Here’s the rub: these various navigational elements are not of equal weight, as would be suggested by a UL. Most notably, we have an expected, encouraged action, “go forward.” Generally this is to submit the form, and generally it is what the user wants to do (and what we want the user to do). All of the other links are secondary to this, especially “go back” and “undo”-flavored links.

Furthermore, when we are congratulating the user after having completed a process we have one or a few possible convenience links leading them off to perform other tasks in the forward direction, and often a “go back and edit” retrograde link. These links are, more or less, of equal priority.

Ideally the markup would suggest a primary action, if we have one, and then segment the forward and back actions somehow. Ideally, the markup would be basically the same whether or not there was an expected forward action. And if, for some reason the page were shown to the user without benefit of CSS, the forward/back nature of the links should be somehow evident.

Perhaps this is too much for our beloved and simplistic (X)HTML to handle?

Nils ( DivineBlog ) pointed out that if we have a single, important submit action, it should be a honest-to-gosh submit button, outside of any secondary link structure. This is a great starting point; the remaining links could well live in one or more unordered lists with no ill-effects.

But how to handle forwardness/backness? Two ULs? More later.