Apr2008
23

Dynamic Text Replacement with JavaScript and C# ASP.NET

by John Dyer

What it Does

You write some basic markup and include a JavaScript file and some code which replaces normal text with a .NET generated image:

<style type="text/css">
h2 { color: #9E100F; size: 22px; }
</style>

<!-- This says "Frequently asked questions in zh-TW -->
<!-- If you just see boxes, you need to install Asian fonts on your machine --> <h2>關於網上教育常見的問題</h2> <script type="text/javascript" src="fontreplacer.js"></script> <script type="text/javascript"> FontReplacer.replace('h2','*', 'MyGreatFont'); </script>

This will take all h2 tags with any class (*) and replace them with and image rendered using 'MyGreatFont'. This font needs to be installed the c:\windows\fonts\ folder of your server or in the same folder as the .NET script which generates it. Initially the page will look like this (using the standard font for Chinese script on a PC which is 'MS Mincho'):

image

But after the replacement happens, it looks like this:

image

With another less formal font, it could look like this:

image

Of course, you can use any font installed on the server and it is not limited to Chinese text. Here is an example replacing the default Times New Roman text with Century Gothic:

image

image

This may be useful in other scenarios for viewing Unicode text on a machine without Unicode fonts installed (Greek, Hebrew, Arabic, etc.).

It can also produce anti-aliased PNGs for use on top of an image:

image image

Why I did it

Recently, I needed to have the titles for a series of webpages be rendered in a specific Chinese font (see http://www.dts.edu/chinese and click on "zh-TW" in the upper right) because the default doesn't look very official (according to my Chinese friends). I wanted to use sIFR which replaces the HTML text node with a Flash file that has the font embedded. The problem is that Chinese fonts are 3-8MB which means the user has to download a gigantic SWF. Also, there can be copyright problems with using sIFR.

So I turned to .NET to generate images using the font. The basic technique was first published back in a 2004 A List Apart article titled Dynamic Text Replacement. ALA used JavaScript and PHP and, since then, there have been many permutations of the basic idea, some using pure CSS instead of JavaScript. I haven't seen a good one for .NET, so I had to write my own. I don't like using CSS for some of the reasons mentioned here, so I went with JavaScript for the replacement. This way users without images or JavaScript can still see the text. Users with images and JavaScript get a slightly nicer view.

How it Works

The JavaScript method looks at the HTML element(s) you send it and pulls out the text and font color. It then sends a request to ASP.NET to return an image which renders the text using your assigned font and colors. There are three ways to call it:

// elements by tag and class
FontReplacer.replace(tag, class, fontName);
// elements by tag and class inside a given elmenent
FontReplacer.replaceIn(element, tag, class, fontName);
// replace array of elements
FontReplacer.replaceElements(elementArray, fontName);

This calls up a .NET script which writes the text to an image using the specified font. The C# code can generate a JPG or transparent PNG which happily sits on top of any background and looks very nice. (There is a flag in the JavaScript to to allow the PNG transparency). There are some tricks in .NET for generating a PNG with alpha channels and for saving a JPEG with a specified quality, so here's the full code for that part.

if (backColor == "transparent")
{
	MemoryStream io = new MemoryStream();
	bitmap.Save(io, ImageFormat.Png);

	context.Response.ContentType = "image/png";
	context.Response.BinaryWrite(io.GetBuffer());
}
else
{
	context.Response.ContentType = "image/jpg";
	SaveAsJpeg(bitmap, context.Response.OutputStream, (long)100);
}
void SaveAsJpeg(Image inputImage, Stream stream, long quality)
{
	 // generate JPEG stuff
	 ImageCodecInfo codecEncoder = GetEncoder("image/jpeg");
	 EncoderParameters encoderParams = new EncoderParameters(1);
	 EncoderParameter qualityParam = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, quality);
	 encoderParams.Param[0] = qualityParam;
	 inputImage.Save(stream, codecEncoder, encoderParams);
}
ImageCodecInfo GetEncoder(string mimeType)
{
	ImageCodecInfo[] codecs = ImageCodecInfo.GetImageEncoders();
	foreach (ImageCodecInfo codec in codecs)
	{
		if (codec.MimeType == mimeType)
		{
			return codec;
		}
	}
	return null;

}

There are also flags in the JavaScript to force the returned image to be the same height and width as the element you are replacing. By default, the height is forced so that the page doesn't jerk at all, but you should be aware that browsers often report the font size inaccurately. Also, .NET's measurement of the size a given string will take up can include some padding depending on the font, so you may have to play with it for your scenario.

Lastly, there are some important things to know about using fonts on the server.

  1. When you install a font on c:\windows\fonts\, IIS doesn't immediately know it's there. You'll need to run the "resetiis" command for IIS to pick up the new fonts.
  2. If you're in a hosted environment and can't install fonts, you're still in luck. There is some code to load a *.TTF file in the fontwriter.ashx script. In this case, you'll need to specify the font file name ('gothic.ttf') rather than the installed font name ('Century Gothic').
  3. You might want to adjust the TextRenderingHint hint property at the top if you don't want anti-aliased rendering.

Download & Example

Apr2008
10

Totally Unobtrusive (and totally awesome) Upside Down Text

by John Dyer

There is a huge need for upside down text on the web. With everyone "going mobile," developers cannot simply rely on users having their screens oriented the same way all the time. I went through all of the possible options for creating upside down text and finally came up with a totally unobtrusive, Unicode enabled, completely web-changing piece of code (which is mostly from someone else and using this and this).

Options for Upside Down Text

  1. Images - The most obvious option for upside down text is to open up Photoshop, enter some text, and use Rotate Canvas.
    Pros: easy to do.
    Cons: must be done for every piece of text. Not every OS/browser in the world can display images. This is just unacceptable.

  2. sIFR - I know it's a little 2005 of me to say it, but a great use of Flash is to replace text and rotate it around.
    Pros: works for any HTML text element.
    Cons: requires Flash which has even less support than images. Also, requires extra coding.

  3. Canvas - one day it might be possible to do more with text drawing on a canvas tag, but right now its not
    Pros: none, for now.
    Cons: doesn't exist.

  4. JavaScript - Everyone knows JavaScript can do anything. See my experiments in 3D and color and this guy's port of Mario. Using this great script its possible to simulate upside down characters using Unicode equivalents.
    Pros: is awesome.
    Cons: nothing at all.

Usage &Implementation

Just give your HTML element a class called "upsidedowntext" and add the script in the header and you have pure greatness:

<script type="text/javascript" src="upsidedown.js"></script>

<div class="upsidedowntext">I can be read in any orientation</div>
<div>I can be read in any orientation</div> <div class="upsidedowntext">This is Tom Cruise taking your picture</div> <div>This is a boring Russian Mig</div>

Result:

uoıʇɐʇuǝıɹo ʎuɐ uı pɐǝɹ ǝq uɐɔ ı
I can be read in any orientation

ǝɹnʇɔıd ɹnoʎ ƃuıʞɐʇ ǝsınɹɔ ɯoʇ sı sıɥʇ
This is a boring Russian Mig

Demo and Download


Note: this post was intended to be an light-hearted (late) April's fools poke at all the random stuff developers are doing with JavaScript, sometimes in the name of a standard or idea that really isn't really needed. Also, I think upside down text is funny.

Feb2008
21

Implementing Map Mashups with Mapstraction

by John Dyer

image A few years ago, I made my first map mashup with Google Maps for Dallas Theological Seminary. It allows visitors to search for churches around the world where DTS alumni are serving. Recently, we decided to update the mashup with several features in mind:

  1. Larger map - 2 years ago we were using a 800px wide layout and now we can use a 1024px layout
  2. Use updated API - Google has changed APIs since then
  3. Implement other maps - Yahoo and Microsoft each have good points
  4. Use custom icons - we are not just showing churches now, but also schools and counselors

To handle all of this, especially the implementation of other multiple map engines, I chose to use mapstraction, a JavaScript layer which allows developers to program against a common API for all mapping engines. It turns these two proprietary mapping code blocks:

// Google maps specific code
var gmap = new GMap2(document.getElementById("map"));
map.addControl(new GMapTypeControl());
gmap.setCenter(new GLatLng(37.4419, -122.1419), 13);
var gmarker = new GMarker( new GLatLng(37.443, -122.166) );
gmap.addOverlay(gmarker);
// Microsoft Live maps specific code
mmap = new VEMap('map');
mmap.SetDashboardSize(VEDashboardSize.Normal);
mmap.LoadMap();
mmap.SetCenterAndZoom(new VELatLong(37.4419, -122.1419), 10);
var shape = new VEShape(VEShapeType.Pushpin, new VELatLong(37.443, -122.166));
map.AddShape(shape);

into this single common set of code:

// universal map API, just change 'google' to 'yahoo', 'microsoft', etc.
var mapstraction = new Mapstraction('map','google');
mapstraction.addControls({ pan: true, zoom: 'large', overview: false, scale: true, map_type: true });
mapstraction.setCenterAndZoom(new LatLonPoint(37.4419, -122.1419), 12);
var marker = new Marker(new LatLonPoint(37.443, -122.166));
mapstraction.addMarker(marker);

In addition to Microsoft, Yahoo, and Google maps, you also get free access to a 3D map called Freeearth which is an amazing implementation of a 3D globe map (like Google Earth) using Papervision3D.

Examples

Here are examples using all four mapping engines. The only unique code is the shadow on the icons under Google maps. Also, prototype (which is used throughout the site) is handling the AJAX calls.

image image

image image

Try it out

Links

Jan2008
14

Simple Gravatar Image Tester

by John Dyer

For some testing, I needed a simple page where I could test out a gravatar. Unfortunately, I couldn't find anything on gravatar's site where you can see if your gravatar is working or just get the URL to the image. So I put together a super-simple page using this JavaScript MD5 hash function.

image

Dec2007
15

Stupid JavaScript Tricks: "3D" Panorama

by John Dyer

Update: Please note, this a Firefox-only, totally experimental "fun" project, not meant for real world use :)

I'm putting together a tutorial for making 3D panoramas using Papervision3D (example) and I though I would release some pretty pointless JavaScript code I wrote a while back that attempts to make a panorama using just JavaScript. Here's how it works: it creates a series of div columns, and then puts copies of the image in each column. As you move your mouse across the image, it stretches the columns vertically along a sin curve to fake the 3D look. And then it destroys your processor.

Here's part of a photo before the effect is applied:

image

Here's the photo split into 1px wide columns. Notice how the roof is straightened out. 

image

Unfortunately, its too slow to be usable. So here's the photo split into 5px wide columns, which makes it pretty usable and doesn't look too bad.

 image

Here are links to try it out:

Oct2007
12

Real World Object Size Comparison in Papervision3D

by John Dyer

While doing development in Papervision 3D again for a campus map, I thought it would be fun to mock up a dynamic size comparison tool like the one at sizeasy. The demo has the obligatory iPhone along with a BlackBerry 8700 and a deck of cards. The measurements are in millimeters, but you could change it to whatever you want, as long as all of the objects matched up. You can also add a URL to whatever image you want to use for each object:

Sizer in PV3D

TV, coffee maker, mountain dew 6-pack

XBOX, PS3, Wii

Sep2007
26

PhotoShop-like JavaScript Color Picker

by John Dyer

Update: Many people have noted "it looks like [picker X]" and they are right: there are tons of pickers out there (I listed a few in the original post). The main difference in mine is that it has all 6 picking options (H,S,V,R,G,B) not just Hue. I haven't seen one that does that and this is a fun way of showing what JavaScript can do.
-----

There are a lot of nice JavaScript color pickers out there (colorjack, colourmod, yahoo, dojo, nogray, mootools), but none of them has a full HSB and RGB options of Photoshop's picker. Some pickers try to generate the entire color map in JavaScript by drawing a 256x256 grid made of divs. This is very slow, which is why color pickers that go the JavaScript route often don't draw the entire map, but instead only 4x4 or 8x8 blocks (ColorZilla).

The other options is to use transparent PNGs and opacity to fake the maps. A few years back, I did this, but it didn't have RGB and was Firefox and IE only (excellent tutorial here). To create the color mixing, the larger map is made of two layers and the vertical slider has four layers (two are used for H, S, or B; four layers for R, G, or B). Some others pickers use this method for a hue map, but don't include the other maps (S,V,R,G,B). The JavaScript is made up of some color methods, a slider control, a handler for input, and the ColorPicker object to put it all together. Click the image below to try it out:

CropperCapture[11]

 

Tested on IE5.5, IE6, IE7, FF2, Opera 9, Safari 2. It would probably need some modification depending on use, but you can download it here: Color Picker

Sep2007
10

FreeTextBox 4.0 Update

by John Dyer

For the last year, I've been working and reworking FreeTextBox 4 for various internal projects, trying to come up with a good API that will work well for both ASP.NET developers and generic JavaScript developers. The major updates that I wanted to do were

  • new more object oriented JavaScript API based on Prototype (and including some features like one toolbar for multiple editors)
  • support for other environments than ASP.NET
  • support for Opera and Safari
  • new styling and theming (including the Office 2007 style Floatie)
  • live CSS editing in another window
  • full document editing with doctypes

All of these are features built into the content manager of DTS website (www.dts.edu). Right now I have an early build at: http://www.freetextbox.com/ftb4/

 
 

I will have a beta release this month without all the features to help developers migrate (if they so choose) and then finish out the remaining features over the coming months. Having the entire control work in JavaScript apart from any dependancies on ASP.NET allows an ASP.NET developer to use a normal <asp:TextBox> control and "upgrade" with FreeTextBox. In plain HTML and JavaScript, this looks like:

<textarea id="MyHtmlCode" cols="14" rows="8"></textarea>
<script type="text/javascript">
var FreeTextBox1 = new FreeTextBox('MyHtmlCode');
</script>	

And in ASP.NET, it would look like

<asp:TextBox id="MyHtmlCode" TextMode="MultiLine" runat="server" />
<script type="text/javascript">
var FreeTextBox1 = new FreeTextBox('<%= MyHtmlCode.ClientID %>');
</script>	

Then in your AJAX code you can reference the editor object and its method directly:

<script type="text/javascript">
var html = FreeTextBox1.getHtml(); 
FreeTextBox1.setHtml('<b>new html</b>');
</script>
Sep2007
6

Table JavaScript: Alternating columns

by John Dyer

Here's a simple script for enabling alternating CSS classes on a table.

// ALT script
function createAlternatingRows(table, className) {
var tbodys = table.getElementsByTagName('tbody');
if (tbodys.length > 0) 
table = tbodys[0];
var rows = table.getElementsByTagName('tr');
for (var i=0; i<rows.length; i++) {
if (i%2==0) rows[i].className = className;
}
}
createAlternatingRows(document.getElementById('myTable'), 'alt'); 

Just send it a table and a classname it and it handle the rest. It's setup to ignore <thead> and <tfoot> tags and it will overwrite other classes.

Aug2007
29

Greek and Hebrew Unicode Keyboards in HTML

by John Dyer

For classes I've needed a simple way to enter in Greek and Hebrew text. There are some nice programs out there that install new keyboards on your system and allow you to type from the QWERTY keyboard. But this requires you to learn a whole new key mapping system which isn't worth it if you just need a few words. I usually just revered to MS Word's Insert Symbol.

So I've created some little tools in HTML to help in typing out a few quick words.

Hebrew Keyboard

Designed to be used with Ezra SIL font. Has all the consonants, vowels, and accents of which I know.

 



Greek Keyboard

This one is designed for use with Gentium font. It has functions for capital letters as well as all diacritical versions of all consonants. When you like on an alpha, a secondary keyboard will show up to allow you to select from various diacritical combinations.

 

Aug2006
29

Textarea text counter / limiter

by John Dyer

I wrote a little prototype class for limiting the number of characters in a <textarea>. It also includes a counter if you want. You set the ID of the <textarea>, the ID of the <input> where the number of characters will be reported, and the number of characters. There is also an internal flag for whether or not to include carriage returns (\n or \r) in the count. Here's how to instantiate it:

<textarea id="textarea1" rows="5" cols="57"></textarea>
<br />
<input type="text" id="remaining" size="9" /> characters remaining
<script type="text/javascript">
var t1 = new TextCounter('textarea1', 'remaining', 150);
</script>

And here's the full script:

var TextCounter = Class.create();
TextCounter.prototype = {
    initialize: function(textareaid, inputid, maxLength) {
        this.maxLength = maxLength;
        this.textarea = $(textareaid);
        this.input = $(inputid);
        this.input.value = maxLength;
        this.input.readonly = true;
        this.input.disabled = true;
        Event.observe(this.textarea, 'keyup', this.checkChars.bindAsEventListener(this));
        Event.observe(this.textarea, 'keydown', this.checkChars.bindAsEventListener(this));
        this.checkChars();
    }, 
    checkChars: function(e) {
        var includeBreaksInCount = false; // false = don't count a return (\r or \n) in the count.
        var charCount = this.textarea.value.length;
        var breaks = 0;
        if (!includeBreaksInCount) {
            var lines = this.textarea.value.split('\n');
            breaks = lines.length;
            // check for /r at the end of the lines (IE)
            for (var i=0; i<lines.length; i++) {
                var line = lines[ i ];                
                if (line.charCodeAt(line.length-1) == 13)
                    breaks++;
            }
            
        }
        
        // check if over limit
        if ((charCount-breaks) > this.maxLength) {            
            this.textarea.value = this.textarea.value.substring(0, (this.maxLength + breaks) );
        }
        
        // update counter
        if (this.input) {
            if ((charCount-breaks) > this.maxLength) {
                this.input.value = 0;
            } else {
                this.input.value = (this.maxLength + breaks) - charCount;
            }        
        }
    }
}
Aug2006
22

Get the value of a radio button in JavaScript

by John Dyer
I regularly use the prototype javascript library in JavaScript development (in fact, FreeTextBox4 will use it). But I've found that the $F() function doesn't work like I expect it to with radio buttons. $F() will return null if a radio is not checked and the value if it is checked. What I want it to do is figure out the value of the currently selected radio in the radio group (all with the same name). Since prototype doesn't do it, I decided to write one to do it. It will return null if none of the radios are checked and it works using either the ID of one of the radios or the name of the group. Here it is

function getRadioValue(idOrName) {
	var value = null;
	var element = document.getElementById(idOrName);
	var radioGroupName = null;  
	
	// if null, then the id must be the radio group name
	if (element == null) {
		radioGroupName = idOrName;
	} else {
		radioGroupName = element.name;     
	}
	if (radioGroupName == null) {
		return null;
	}
	var radios = document.getElementsByTagName('input');
	for (var i=0; i<radios.length; i++) {
		var input = radios[ i ];    
		if (input.type == 'radio' && input.name == radioGroupName && input.checked) {                          
			value = input.value;
			break;
		}
	}
	return value;
}
Aug2006
1

Mouse Tracking: Beyond Analytics

by John Dyer
The next big thing after website traffic reports appears to be mouse tracking. There are several services popping up that use JavaScript to track a user's mouse movements and clicks. When you login to the service you can watch movies of exactly how a user explored a webpage using their mouse. This is a much cheaper alternative to a full-blown eyetracking system. Here are some of the major players so far (note all of them have nice real-word names instead of "web 2.0" meaningless madeup names):

"Heatmaps"
  • MapSurface.com - Based on his orginal AJAX Link Tracker, Glenn Jones' service overlays graphical click data on top of your site. You simply press "Alt-X" and up pops your data. MapSurface.com is the hosted service, but there is also a free download [no commercial plans yet announced]
  • CrazyEgg.com - does essentially the same thing as MapSurface, but the overlays look a little more polished. [currently accepting beta inquiries]
"Mouse Movements"
  • OpenCube.com - OpenCube has been known for it's nice DHTML/JavaScript/CSS widgets and menus, but it has also developed a product that goes a heatmap and actually tracks the mouse movements of users. My worry here is that it seems to require IE to view the data. [$119 per site. Requires ASP on server]
  • ClickTale.com - Right now, this looks to be the most powerful tool for mouse tracking. This services is also in Beta, so it will take some time to see how good they really are, but their demo videos look solid. [accepting beta applications]
"Free"
  • MIT Labs - MIT's Media Lab has released a PHP-based tool that does some pretty powerful mouse tracking. It creates an overlay with arrows of differnt sizes and colors indicating the speed and direction of the mouse. They have a sort of hosted service as well as a download package. (usability study PDF)
It will be interesting to see how this arena shapes up over the next year and how useful this proves to be. I would be willing to be several open source tools will crop up as well.
Apr2006
25

Lightbox usage for calendar

by John Dyer

I just made my first implimentation of Lightbox ("Gone Wild" edition). Lightbox (original, gone wild) is a small JavaScript tool for making accessible modal windows. I've implimented a slighlty tweaked version it in a calendar page (see pics below) and on a page that shows faculty member's publications (example). I've also used some IE hacks to get IE6 to properly use alpha PNGs for a nicer overlay. Rather than use the full-fledged ASP.NET calendar component, I wrote a custom ASP.NET control that renders a CSS-only table. I would have liked to make the calendar URL-hackable (have the ability to move through months via URLs like /calendar/2006/09/) but for now I'm using postbacks.

Before clicking the link
Before pressing the link
After clicking a lightbox link
After clicking on a calendar entry
Jan2006
20

Hotmail Beta is Great - Sign Up HTML is Horrible!

by John Dyer
The hotmail team sent me a beta to their awesome new hotmail beta. The problem is they coded the invite with the worst HTML I've ever seen. It's pretty normal to make HTML mistakes, but these are not actually mistakes. Interestingly, they took advantage of IE's ability to figure out horrible HTML.

Basically, all the links are nested like so,
<table>
    <a>
        <tr>
            <td>Click Me</td>
        </tr>
    </a>

</table>

These links appear to work in IE, but not in Firefox or Opera (IE is actually broken on my machine. It just doesn't work anymore...). Funny tactic huh?