Jay Harris is Cpt. LoadTest

a .net developers blog on improving user experience of humans and coders
Home | About | Speaking | Contact | Archives | RSS
Filed under: Blogging | Flash | Programming

My earlier post on creating custom brushes in Google Syntax Highlighter (Extending Language Support in Google Syntax Highlighter) contains a rudimentary brush for ActionScript. The original is designed for Stone Soup; it is something to get an AS brush established, but is not meant to be exhaustive. I have revisited the brush and added some meat. The bush should now supply a more thorough coverage of the language. A download is provided below.

ActionScript Brush

dp.sh.Brushes.ActionScript = function() {
  var keywords = 'and arguments asfunction break call case catch clear ' +
    'continue default do else escape eval false finally for getProperty ' +
    'if ifFrameLoaded in instanceof loop NaN new newline not null or ' +
    'prototype return set super switch targetPath tellTarget this throw ' +
    'trace true try typeof undefined unescape var visible void while with';
  var builtin = '_currentframe _droptarget _framesloaded _global _height ' +
    '_level _name _root _rotation _target _totalframes _url _visible ' +
    '_width _x _xmouse _xscale _y _ymouse _yscale Array Boolean Button ' +
    'bytesLoaded bytesTotal Camera Color Date enabled Error focusEnabled ' +
    'Key LoadVars Math Mouse MovieClip nextFrame Number Object Selection ' +
    'Sound Stage String StyleSheet System TextFormat';
  var funcs = 'addProperty attachMovie attachVideo browse cancel ' +
    'clearInterval clone concat createEmptyMovieClip createTextField ' +
    'dispose draw duplicateMovieClip dynamic equals extends function ' +
    'getInstanceAtDepth gotoAndPlay gotoAndStop identity implements ' +
    'import interface isEmpty isFinite isNAN join length loadClip ' +
    'loadMovie loadMovieNum loadVariables loadVariablesNum merge moveTo ' +
    'on onClipEvent onDragOut onDragOver onEnterFrame onKeyDown onKeyUp ' +
    'onKillFocus onMouseDown onMouseMove onMouseUp onPress onRelease ' +
    'onReleaseOutside onRollOut onRollOver onUnload play pop prevFrame ' +
    'private public push registerClass removeMovieClip reverse rotate ' +
    'scale setEmpty setInterval setProperty shift slice sort sortOn ' +
    'splice startDrag static stopAllSounds stopDrag subtract swapDepths ' +
    'toString toString translate union unloadClip unloadMovie ' +
    'unloadMovieNum unshiftclass unwatch valueOf watch';
  var includes = '#include #initClip #endInitClip';

  this.regexList = [
    {regex: dp.sh.RegexLib.SingleLineCComments, css: 'comment' },
    {regex: dp.sh.RegexLib.MultiLineCComments, css: 'comment' },
    {regex: dp.sh.RegexLib.DoubleQuotedString, css: 'string' },
    {regex: dp.sh.RegexLib.SingleQuotedString, css: 'string' },
    {regex: new RegExp(this.GetKeywords(keywords), 'gm'), css: 'keyword' },
    {regex: new RegExp(this.GetKeywords(funcs), 'gm'), css: 'func' },
    {regex: new RegExp(this.GetKeywords(builtin), 'gm'), css: 'builtin' },
    {regex: new RegExp(this.GetKeywords(includes), 'gm'), css: 'preprocessor'}
  this.CssClass = 'dp-as';
  this.Style = '.dp-as .func { color: #000099; }' +
               '.dp-as .builtin { color: #990000; }';

dp.sh.Brushes.ActionScript.prototype = new dp.sh.Highlighter();
dp.sh.Brushes.ActionScript.Aliases = ['actionscript', 'as'];


Upload the Brush javascript file to your Google Syntax Highlighter Scripts directory, and load the file in unto your HTML with a <SCRIPT> tag with your other brushes.

<script language="javascript"

Display syntax-highlighted ActionScript using a traditional Google Syntax Highlighter <PRE> tag, using as or actionscript as the language alias.

<pre name="code" class="as">
  // Some ActionScript Code

Brush In Action

Sample ActionScript for Demo
ActionScript Brush for Google Syntax Highlighter
if (dteDate.getMonth() == intCurrMonth && intCurrMonth == intOldMonth
    && intOldYear == intCurrYear) {
  if (dteDate.getDay() == 0 and dteDate.getDate()>1) {
    intYPosition = intYPosition+20;
  duplicateMovieClip ("DayContainer", "DayContainer"+intDate, intDate);
  setProperty ("DayContainer"+intDate, _y, intYPosition);
  setProperty ("DayContainer"+intDate, _x, intXPosition[dteDate.getDay()]);

  } else if (intCurrMonth == 6) {
    if (intDate == 4) {
      clrFColor = new Color("DayContainer"+intDate+".foreground");
      clrBColor = new Color("DayContainer"+intDate+".background");
  } else if (intCurrMonth == 9) {
    if (intDate == 31) {
      clrFColor = new Color("DayContainer"+intDate+".foreground");
      clrBColor = new Color("DayContainer"+intDate+".background");
  } else if (intCurrMonth == 10) {
    if (intDate >= 22 && intDate <= 28 && dteDate.getDay() == 4) {
      clrFColor = new Color("DayContainer"+intDate+".foreground");
      clrBColor = new Color("DayContainer"+intDate+".background");
  set ("DayContainer"+intDate+":MyDate", new Date(dteDate.getFullYear(),
    dteDate.getMonth(), dteDate.getDate()));
  setProperty ("DayContainer"+intDate, _visible, true);


Download: shBrushAs.zip

  • Compressed shBrushAs.js for production. 
  • Uncompressed shBurshAs.js for debugging.

As always, this code is provided with no warranties or guarantees. Use at your own risk. Your mileage may vary.

Friday, December 12, 2008 8:25:38 AM (Eastern Standard Time, UTC-05:00)  #    Comments [1] - Trackback

Filed under: Events

Brian Genisio has organized a monthly lunch for the local development community around Ann Arbor, Michigan. The event, held on the third Thursday of every month, will be an opportunity for developers to get together, network with colleagues, talk about what is cool or what is in the way in day-to-day development efforts, and have a good time socializing at lunch. A Google Group has been set up for more information, and will serve as the primary method of communication.

The first Ann Arbor Nerd Lunch will be held next week, noon on Thursday, December 18th, at the Mahek Indian Cuisine restaurant in downtown Ann Arbor. The plan is to change the meeting place every month to accommodate different taste buds, but to keep the meeting time consistently on the third Thursday.

Ann Arbor Nerd Lunch - Google Group
Thursday, December 18th, Noon

Mahek Indian Cuisine - Map
212 E. Washington Street
Ann Arbor, Michigan 48104

Please RSVP on the Google Group, so that proper table sizes can be planned.

For every meeting, you are encouraged to bring a friend. For this first meeting, you are challenged with bringing someone who does not normally attend community functions, such as local conferences and user group meetings, yet is interested in getting involved. Help get Ann Arbor Nerd Lunch off the ground. Also, come with some ideas for the group. Should it stay casual? Should "special guests" be brought in to help start conversation? This is an event for the community, and the goal is to make a lunch that is beneficial for everyone. It should be a great time.

I'll see you there.

Technorati Tags: ,
Thursday, December 11, 2008 8:56:03 AM (Eastern Standard Time, UTC-05:00)  #    Comments [0] - Trackback

Filed under: Blogging | Flash | JavaScript | Programming | Tools

As I discussed in an earlier post (Blog your code using Google Syntax Highlighter), Google Syntax Highlighter is a simple tool that allows bloggers to easily display code in a format that is familiar end users. The tool renders the code in a very consumable fashion that includes colored syntax highlighting, line highlighting, and line numbers. Out of the box it supports most of the common languages of today, and a few from yesterday, but some common languages are unsupported. Perl, ColdFusion, and Flash's ActionScript all are unloved by Google Syntax Highlighter, as are many others that you may want to post to your blog. For these languages, the solution is a custom brush.

Syntax Highlighting Brushes

For Google Syntax Highlighter, brushes are JavaScript files that govern the syntax highlighting process, with names following the format of shBrushLanguage.js, such as shBrushXml.js. Brushes contain information about the keywords, functions, and operators of a language, as well as the syntax for comments, strings, and other syntax characteristics. Keyword-level syntax is applied to any specific word in the language, including keywords, functions, and any word operators, such as and, or, and not. Regular expressions apply character-level syntax to code, and identifies items such as character operators, the remainder of an inline comment, or the entire contents of a comment block. Finally, aliases are defined for the brush; these are the language aliases that are used within the class attribute of the Google Syntax Highlighter <PRE> tag. With this information, the brush applies the syntax highlighting styles according to the CSS defined for each component of the language.

Breaking Down Brushes

Decomposing the SQL Brush

In JavaScript, everything is an object that can be assigned to a variable, whether its a number, string, function, or class. Brushes are each a delegate function. The variable name of the brush must match dp.sh.Brushes.SomeLanguage.

dp.sh.Brushes.Sql = function() {

Next, define the list of keywords for applying syntax highlighting. Each list is not an array, but rather a single-space delimited string of keywords that will be highlighted. Also, multiple keyword lists can exist, such as one list for function names, another for keywords, and perhaps another for types, and unique styling can be applied to each grouping (we'll get to styling a little later).

  var funcs = 'abs avg case cast coalesce convert count current_timestamp ' +
    'current_user day isnull left lower month nullif replace right ' +
    'session_user space substring sum system_user upper user year';

  var keywords = 'absolute action add after alter as asc at authorization ' +
    'begin bigint binary bit by cascade char character check checkpoint ' +
    'close collate column commit committed connect connection constraint ' +
    'contains continue create cube current current_date current_time ' +
    'cursor database date deallocate dec decimal declare default delete ' +
    'desc distinct double drop dynamic else end end-exec escape except ' +
    'exec execute false fetch first float for force foreign forward free ' +
    'from full function global goto grant group grouping having hour ' +
    'ignore index inner insensitive insert instead int integer intersect ' +
    'into is isolation key last level load local max min minute modify ' +
    'move name national nchar next no numeric of off on only open option ' +
    'order out output partial password precision prepare primary prior ' +
    'privileges procedure public read real references relative repeatable ' +
    'restrict return returns revoke rollback rollup rows rule schema ' +
    'scroll second section select sequence serializable set size smallint ' +
    'static statistics table temp temporary then time timestamp to top ' +
    'transaction translation trigger true truncate uncommitted union ' +
    'unique update values varchar varying view when where with work';

  var operators = 'all and any between cross in join like not null or ' +
    'outer some';

Following the keyword definitions is the Regular Expression pattern and Style definition object list. The list, this.regexList, is an array of pattern/style objects: {regex: regexPattern, css: classString}. The regexPattern is a JavaScript RegExp object, and defines the pattern to match in the source code; this pattern can be created using one of three options within Google Syntax Highlighter.

Predefined Patterns
Within Google Syntax Highlighter, dp.sh.RegexLib contains five predefined regular expression patterns. MultiLineCComments is used for any language that uses C-style multi-line comment blocks: /* my comment block */. SingleLineCComments is used for any language that uses C-style single line or inline comments: // my comment. SingleLinePerlComments applies for Perl-style single line comments: # my comment. DoubleQuotedString identifies any string wrapped in double-quotes and SingleQuotedString identifies strings wrapped in single-quotes. These options are used in place of creating a new instance of the RegExp object.
Keyword Patterns
Google Syntax Highlighter has a GetKeywords(string) function which will build a pattern string based on one of the brush's defined keyword strings. However, this is only the pattern string, and not the RegExp object. Pass this value into the RegExp constructor: new RegExp(this.GetKeyword(keywords), 'gmi')
Custom Pattern Definition
Create a new RegExp object using a custom pattern. For example, use new RegExp('--(.*)$', 'gm') to match all Sql comments, such as --my comment.

For these pattern/style objects, the regular expression pattern is followed by the name of the CSS class to apply to any regular expression matches. The style sheet packaged with Google Syntax Highlighter, SyntaxHighlighter.css, already defines the many CSS classes used by GSH; place the additional styles for your custom brushes within this file, in a new file, in your HTML, or defined them within the brush using JavaScript.

  this.regexList = [
    {regex: new RegExp('--(.*)$', 'gm'), css: 'comment'},
    {regex: dp.sh.RegexLib.DoubleQuotedString, css: 'string'},
    {regex: dp.sh.RegexLib.SingleQuotedString, css: 'string'},
    {regex: new RegExp(this.GetKeywords(funcs), 'gmi'), css: 'func'},
    {regex: new RegExp(this.GetKeywords(operators), 'gmi'), css: 'op'},
    {regex: new RegExp(this.GetKeywords(keywords), 'gmi'), css: 'keyword'}

The delegate definition ends with any style specifications. Apply a style sheet to the entire code block using this.CssClass. Also, as mentioned above, the brush can define custom CSS using this.Style as an alternative to placing the CSS in HTML or a CSS file. When finished, close the delegate.

  this.CssClass = 'dp-sql';
  this.Style = '.dp-sql .func { color: #ff1493; }' +
    '.dp-sql .op { color: #808080; }'; }

The final component of a Brush, set outside of your delegate, contains the prototype declaration and any aliases to apply to the Brush. Aliases consist of a string array (a real array this time, not a space-delimited string) of language aliases to use, such as ['c#','c-sharp','csharp']. Alias values must be unique across all defined brushes that you have included into your site.

dp.sh.Brushes.Sql.prototype = new dp.sh.Highlighter();
dp.sh.Brushes.Sql.Aliases = ['sql'];

Making a Custom Brush (for ActionScript)

I like rich media applications, such as those developed in Flash or Silverlight. I was surprised when I found that Google Syntax Highlighter does not ship with an ActionScript brush, and more surprised when I found out that no one has written one, yet. So, using the methods from above, I created one. This isn't meant to be an exhaustive brush, but more like Stone Soup. It's a start. Please feel free to add to it.

dp.sh.Brushes.ActionScript = function() {

  var keywords = 'and break case catch class continue default do dynamic else ' +
    'extends false finally for if implements import in interface NaN new not ' +
    'null or private public return static super switch this throw true try ' +
    'undefined var void while with';

  this.regexList = [{regex: dp.sh.RegexLib.SingleLineCComments, css: 'comment'},
    {regex: dp.sh.RegexLib.MultiLineCComments, css: 'comment'},
    {regex: dp.sh.RegexLib.DoubleQuotedString, css: 'string'},
    {regex: dp.sh.RegexLib.SingleQuotedString, css: 'string'},
    {regex: new RegExp(this.GetKeywords(keywords), 'gm'), css: 'keyword'}];

    this.CssClass = 'dp-as';

dp.sh.Brushes.ActionScript.prototype = new dp.sh.Highlighter();
dp.sh.Brushes.ActionScript.Aliases = ['actionscript', 'as'];
Wednesday, December 10, 2008 4:47:27 PM (Eastern Standard Time, UTC-05:00)  #    Comments [1] - Trackback

Filed under: Mush

Photo of Jay Harris After seeing yet one more link on the internet attributed to Captain LoadTest, I figure that it is time to put a face to the nickname, plus associate it with any other mindless drivel that might spew out of my head in the next 10 minutes.

I am Jay Harris, the software developer. Not to be confused with Jay Harris the sportscaster on ESPN or Kay-Jay Harris the football player of the New York Giants. I am a senior software consultant, blogger, community advocate, and Microsoft Certified Professional in southeast Michigan, where I specialize in ASP.NET development and automated developer testing. Prior gigs, at least since moving to Michigan in 2003, include SRT Solutions in Ann Arbor working as a senior software consultant, a few years at Latitude Consulting Group in Howell as a Senior Developer and Performance Specialist, and time at Novations Learning Technologies in East Lansing as a Presentation Developer and Performance Specialist. Before moving to Michigan, my path includes several developer and developer-in-test positions at various companies throughout Western New York, and four years as student at Clarkson University, where I hold a Bachelor of Science in Technical Communications and a minor in Computer Science.

Throughout my career, I have focused on the full user experience. I am not only driven towards providing usable interfaces but I am also a strong advocate of practices and processes that improve quality through code, such as automated testing, continuous integration, and performance analysis. I am also active in the developer community, speaking at area user groups and conferences, serving as President of Ann Arbor .Net Developers user group, and organizing local study groups to help area developers pursue Microsoft .NET certifications.

Random Trivia

  • I enjoy restoring wooden furniture. I like bringing old, discarded, unloved furniture back to life. It fits well with taking existing code and making it better, taking a poor performing application and making it faster, or even when I was a kid rebuilding Legoland after yet another natural Lego-disaster.
  • I am an endorphin junkie. Combine that with a high tolerance for g-forces, and I have an addiction for fast vehicles, extreme roller coasters, and crazy amusement park rides. I hope that I can soon add bungee jumping and sky diving to the list.
  • I am not a Michigan Native; I am originally from Western New York. I was born and raised near Rochester, New York, and I moved from Syracuse, New York to Lansing, Michigan in 2003 with my wife so that she could attend law school at Michigan State. We like Michigan, but still consider ourselves tourists, and probably always will.
Wednesday, December 10, 2008 3:31:26 PM (Eastern Standard Time, UTC-05:00)  #    Comments [1] - Trackback

Filed under: ASP.Net | Blogging | Programming | SEO

Did you know that yourdomain.com and www.yourdomain.com are actually different sites? Are they both serving the same content? If so, it may be negatively impacting your search engine rankings.

Subdomains and the Synonymous 'WWW'

Sub-domains are the prefix to a domain (http://subdomain.yourdomain.com), and are treated by browsers, computers, domain name systems (DNS), search engines, and the general internet as separate, individual web sites. Google's primary web presence, http://www.google.com, is very different than Google Mail, http://mail.google.com, or Google Documents, http://docs.google.com, all because of subdomains. However, what many do not realize is that www is, itself, a subdomain.

A domain, on its own, requires no www prefix; a subdomain-less http://yourdomain.com should be sufficient for serving up a web site. And since www is a subdomain, dropping the prefix could potentially return a different response. There are some sites that will fail to return without the prefix, and some sites that fail with it, but the most common practice is that the www subdomain is synonymous for no subdomain at all.

The Synonymous WWW and SEO

The issue with having two synonymous URLs (http://yourdomain.com and http://www.yourdomain.com) is that search engines may interpret them as separate sites, even if they are serving the same content. The two addresses are technically independent and are potentially serving unique content; to a cautious search engine, even if pages appear to contain the same content, there may be something different under the covers. This means your audience's search results returns two entries for the same content. Some users will happen to click on yourdomain.com while others navigate to www.yourdomain.com, splitting your traffic, your page hits, your search ranking between two sites, unnecessarily.

HTTP Redirects will cure the issue. If you access http://google.com, your browser is instantly redirected to http://www.google.com. This is done through a HTTP 301 permanent redirect. Search Spiders recognize HTTP response codes, and understand the 301 as a "use this other URL instead" command. Many search engines, such as Google, will then update all page entries for the original URI (http://yourdomain.com) and replace it with the 301's destination URL (http://www.yourdomain.com). If there is already an entry for the destination URL, the two entries will be merged together. The search entries for yourdomain.com and www.yourdomain.com will now share traffic, share page hits, and share search ranking. Instead of having two entries on the second and third pages of search results, combining these entries may be just enough to place you on the first page of results.

In addition to combining search entries for subdomains, you can also combine root-level domains through HTTP 301. On this site, in addition to adding the www prefix if no subdomain is specified, captainloadtest.com will HTTP 301 redirect to www.cptloadtest.com.

Combining the Synonyms

We need a way to implement an HTTP 301 redirect at the domain level for all requests to a site; however, often we are using applications that may not grant us access to the source, or we don't have the access into IIS through our host to set up redirects for ourselves. URL Rewrite, Part 2 covers a great drop-in redirect module by Fritz Onion that uses a stand-alone assembly with a few additions in web.config to HTTP 301 redirect paths in your domain (it also supports HTTP 302 redirects). This module is perfect for converting a WordPress blog post URL, such as cptloadtest.com/?p=56, to a DasBlog blog post URL like cptloadtest.com/2006/05/31/VSNetMacroCollapseAll.aspx. However, to redirect domains and subdomains, the module must go a step further and redirect based on matches against the entire URL, such as directing http:// to https:// or captainloadtest.com to cptloadtest.com, which it does not support. It's time for some modifications.

private void OnBeginRequest(object src, EventArgs e) {
  HttpApplication app = src as HttpApplication;
  string reqUrl = app.Request.Url.AbsoluteUri;
  redirections redirs
    = (redirections) ConfigurationManager.GetSection("redirections");

  foreach (Add a in redirs.Adds) {
    Regex regex = new Regex(a.targetUrl, RegexOptions.IgnoreCase);
    if (regex.IsMatch(reqUrl)) {
      string targetUrl = regex.Replace(reqUrl, a.destinationUrl, 1);

      if (a.permanent) {
        app.Response.StatusCode = 301; // make a permanent redirect
        app.Response.AddHeader("Location", targetUrl);


By converting app.Request.RawURL to app.Request.AbsoluteUri, the regular expression will now match against the entire URL, rather than just the requested path. There is one downside to this change: the value is the actual path processed, not necessarily what was in the originally requested URL. To this effect, the value of AbsoluteUri for requesting http://www.cptloadtest.com?p=56 is actually http://www.cptloadtest.com/default.aspx?p=56; by requesting the root directory, the default page is being processed, not the directory itself, so default.aspx is added to the URL. Keep this in mind when setting up your redirection rules. Also, the original code converted the URL to lower case; with my modifications, I chose to maintain the case of the URL, since sometimes case matters, and instead ignore case in the regular expression match using RegexOptions.IgnoreCase. Finally, I made some other minor enhancements, like using the ConfigurationManager, since ConfigurationSettings is now obsolete, and reusing the matching Regex instance for replacements.

Download: RedirectModule.zip


  • Source code for the drop-in Redirect Module
  • Sample web.config that uses the module
  • Compiled version of redirectmodule.dll

The code is based on the original Redirect Module by Fritz Onion and the Xml Serializer Section Handler by Craig Andera. As always, this code is provided with no warranties or guarantees. Use at your own risk. Your mileage may vary. Thanks to Fritz Onion for the original work, and allowing me extend his code further.

The usage is the same as Fritz Onion's original module. Drop the assembly into your site's bin, and place a few lines into the web.config. The example below contains the rules as they would apply to this site, 301 redirecting http://www.captainloadtest.com to http://www.cptloadtest.com, and adding the www subdomain to any domain requests that have no subdomain.

<?xml version="1.0"?>
    <section name="redirections"
      type="Pluralsight.Website.XmlSerializerSectionHandler, redirectmodule" />
  <!-- Redirect Rules -->
  <redirections type="Pluralsight.Website.redirections, redirectmodule">
    <!-- Domain Redirects //-->
    <add targetUrl="captainloadtest\.com/Default\.aspx"
      destinationUrl="cptloadtest.com/" permanent="true" />
    <add targetUrl="captainloadtest\.com"
      destinationUrl="cptloadtest.com" permanent="true" />

    <!-- Add 'WWW' to the domain request //-->
    <add targetUrl="://cptloadtest\.com/Default\.aspx"
      destinationUrl="://www.$1.com/" permanent="true" />
    <add targetUrl="://cptloadtest\.com"
      destinationUrl="://www.$1.com" permanent="true" />

    <!-- ...More Redirects -->
      <add name="RedirectModule"
        type="Pluralsight.Website.RedirectModule, redirectmodule" />

The component is easy to use, and can redirect your site traffic to any URL you choose. Neither code changes to the application nor configuration changes to IIS are needed. By using this module to combine synonymous versions of your URLs, such as alternate domains or subdomains, you will improve your page ranking through combining duplicate search result entries. One more step towards your own search engine optimization goals.

URL Rewrite

Thursday, December 4, 2008 4:43:10 PM (Eastern Standard Time, UTC-05:00)  #    Comments [4] - Trackback