May2008
15

ISBN Functions in C# (ISBN-10 to ISBN-13 Conversion)

by John Dyer

While working with some book data, I needed to merge the old ISBN-10 and ISBN-13 data. Unfortunately, I couldn't find any C# code to do the conversion. Search for "ISBN C#" in Google only return book stores with C# books.

So here is a little class that will convert and validate ISBNs. Here's how the code would look:

string isbn10 = "0830818030"; 
string isbn13 = ISBN.Convert10to13(isbn10); // returns "9780830818037"

string isbn = "083081803X"; // this one has a bad checksum (last digit) 
string correctIsbn = ""; 
if (!ISBN.IsValid(isbn, out correctIsbn)) { 
	isbn = corretnIsbn; // returns "0830818030"
}

An ISBN object can also be instantiated and used:

IBSN myBook = new ISBN("0-83081-803-0"); 
string isbn13 = myBook.ISBN13; // returns "9780830818037"
string isbn10 = myBook.ISBN10; // returns "0830818030" 

Here is the class:

public class ISBN
    {

        public ISBN(string isbn)
        {
            SetIsbn(isbn);
        }

        private string _isbn10 = "";
        private string _isbn13 = "";

        public string ISBN10
        {
            get
            {
                return _isbn10;
            }
            set
            {                
                string corrected = "";
               
                if (!IsValid(value, out corrected) && corrected == "")
                    throw new Exception("invalid ISBN");

                SetIsbn(corrected);
            }
        }

        public string ISBN13
        {
            get
            {
                return _isbn13;
            }
            set
            {
                string corrected = "";

                if (!IsValid(value, out corrected) && corrected == "")
                    throw new Exception("invalid ISBN");

                SetIsbn(corrected);
            }
        }

        private void SetIsbn(string isbn)
        {
            isbn = CleanIsbn(isbn);

            if (isbn.Length == 10)
            {
                _isbn10 = isbn;
                _isbn13 = Convert10to13(isbn);
            }
            else if (isbn.Length == 13)
            {
                _isbn13 = isbn;
                _isbn10 = Convert13to10(isbn);
            }
        }

        private static string CleanIsbn(string isbn)
        {
            return isbn.Replace("-", "").Replace(" ", "");
        }


        public static string Convert10to13(string isbn)
        {
            return Convert10to13(isbn, true);
        }
        public static string Convert10to13(string isbn, bool throwError) {
            
            // remove - and space
            string isbn10 = CleanIsbn(isbn);

            if (isbn10.Length != 10 && throwError)
                throw new Exception("ISBN must be 10 characters long");

            // 1) Drop the check digit (the last digit)
            isbn10 = isbn10.Substring(0, 9);

            // 2) Add the prefix '978' 
            string isbn13 = "978" + isbn10;

            // 3) Recalculate check digit 
            isbn13 = isbn13 + Isbn13Checksum(isbn13);

            return isbn13;
        }

        public static string Convert13to10(string isbn)
        {
            return Convert13to10(isbn, true);
        }

        public static string Convert13to10(string isbn, bool throwError)
        {

            // remove - and space
            string isbn13 = CleanIsbn(isbn);

            if (isbn13.Length != 13 && throwError)
                throw new Exception("ISBN must be 13 characters long");

            // 1) Drop the check digit (the last digit) and prefix '978'
            string isbn10 = isbn13.Substring(3, 9);
     
            // 2) Recalculate your check digit using the modules 10 check digit routine.
            isbn10 = isbn10 + Isbn10Checksum(isbn10);

            return isbn10;
        }

        public static bool IsValid(string isbn)
        {
            string correctIsbn = "";
            return IsValid(isbn, out correctIsbn);
        }

        public static bool IsValid(string isbn, out string correctISBN)
        {
            // remove - and space
            isbn = CleanIsbn(isbn);

            if (isbn.Length == 10) {
                return ValidateIsbn10(isbn, out correctISBN);
            }
            else if (isbn.Length == 13)
            {
                return ValidateIsbn13(isbn, out correctISBN);
            }
            else
            {
                correctISBN = "";
                return false;
            }

        }

        private static string Isbn10Checksum(string isbn)
        {
            int sum = 0;
            for (int i = 0; i < 9; i++)
                 sum += (10-i) * Int32.Parse(isbn[i].ToString());
   
            float div = sum / 11;
            float rem = sum % 11;

            if (rem == 0)
                return "0";
            else if (rem == 1)
                return "X";
            else
                return (11 - rem).ToString();
        }

        private static string Isbn13Checksum(string isbn)
        {
            float sum = 0;
            for (int i = 0; i < 12; i++)
                sum += ((i % 2 == 0) ? 1 : 3) * Int32.Parse(isbn[i].ToString());

            float div = sum / 10;
            float rem = sum % 10;

            if (rem == 0)
                return "0";
            else
                return (10 - rem).ToString();
        }


        private static bool ValidateIsbn10(string isbn, out string correctISBN)
        {
            correctISBN = isbn.Substring(0, 9) + Isbn10Checksum(isbn);

            return (correctISBN == isbn);
        }

        private static bool ValidateIsbn13(string isbn, out string correctISBN)
        {
            correctISBN = isbn.Substring(0, 12) + Isbn13Checksum(isbn);

            return (correctISBN == isbn);
        }
}

I initially wanted to also make a method that would add back in the correct dashes, but I found out that the make up is based on country and publisher codes assigned by the International ISBN Agency which regularly updates the list of codes making it pretty much impossible to permanantly functionalize.

May2008
12

The Best Way to Demo Websites for Clients

by John Dyer

For the last few years, my web team has used a projector to collaborate on web projects. We use it for going through our weekly work items, demoing designs, doing training, and so on.

Unfortunately, it was an old projector with a weak bulb so we always had to turn out the lights to use it. Even then it was hard to see because the contrast and color depth were poor, and the resolution was only 1024x768. So last year I budgeted to get a new projector to solve these problems.

Instead of getting a new projector, I decided to get a LCD HDTV and use it as a monitor. I found a killer deal at Sam's Club on a 65" Olevia. The screen isn't quote as nice as something like a Sony, but for what we are doing, it is absolutely amazing. The 1920x1080 resolution and super bright LCD screen is perfect for looking at web designs. In this pic, We have our task app on the right and a full size web page on the left. Even though the text is tiny it's readable from about 8' away:

IMG_3278 

IMG_3279

I have an old PC running the display, and I'm using a few little utilities to make enhance presentations:

  • PowerStrip - Some PCs and Macs can't natively display 1920x1080 resolution. This little Windows app helps out and can usually enable even really old machines to do HD resolutions.
  • ZoomIt - this is a little app by the former SysInternals.com guys. It lets you zoom into a screen with the mouse scroll wheel and draw on the screen. This features are invaluable for mockups.
  • Zune Theme Modified - I also used a modified Zune theme, because I like how the dark colors make the UI fade into the background and focus attention on the design.

On the fun side, we can occasionally use it for things other than web design. Since we're in a basement, we don't have a good way of getting an antenna to pipe in some HD goodness, but we can watch trailers in full 1080p goodness!

IMG_3281

May2008
6

Web Marketing using Facebook Ads and Video for Dallas Seminary

by John Dyer

Note: This blog is usually fairly technical, dealing with ASP.NET, Flash, and JavaScript code samples, but this is a brief deviation into the broader world of the web as a technology platform for communicating an institution's message.

For Dallas Theological Seminary, we've been experimenting with "branding" and reaching different audiences by producing some new videos and experimenting with Facebook's relatively new targeted advertising.

Marketing for Seminaries?

Advertising and marketing for churches and seminaries can be a tricky thing, because it seems rather sacrilegious to "market" something spiritual. We've all seen late night and afternoon televangelists that make us cringe. But DTS isn't offering a "product" that someone buys; rather its mission is to train pastors, professors, and missionaries for a lifetime of service. If you call an admissions counselor and ask why you should go to DTS, you won't get a sales pitch. The admissions counselor will tell you that you should go to the school to which God is calling you. The admissions person will then mention some of DTS's strengths, but stay away from comparisons with other schools or "selling DTS."

Still, it is important for DTS to inform prospective students on what kind of school it is and what it's strengths are. Basic marketing ideas can help convey this, and naturally the web is a great place to start.

Context for the Advertisements

Any time you create an ad for a "company," it is important to know the background of the business and the historical context in which the ad is situated. This history of seminaries and churches in America is key to understanding what ads will communicate to the seminary audience. image 

In the early 1900s, was a debate in the Christian church between "fundamentalists" (those who believe certain truths are the most important thing about Christianity) and "liberals" (those who believe certain actions are the most important thing about Christianity). In this debate, it seemed that the "liberals" forgot that Jesus asked Christians to believe something (that he is the savior and the Son of God), while the the fundamentalists often forgot that Jesus asked Christians to do something (love others).

DTS was started in the 1920s during the fundamentalist movement. Unfortunately, many of the seminaries which started during this time period have a reputation of caring about teaching the Bible, but neglecting to meet real world needs. By the 60s and 70s, some Christians wanted to balance the emphases of both the liberals (loving) and fundamentalists (truth), and there they were called "evangelicals." Today, this term has become politically decisive and has less positive connotations in many places, so the word "evangelical" communicates something different than it used it.

Today's students who are considering seminary are more concerned than ever about balancing these two things and so DTS needs new ways to communicate that it wants to care about Christian beliefs and Christian practices without making one too dominate. These students also don't read traditional Christian publications, so DTS needs to shift from print publications to Internet and from top down communication to allowing prospects to connect to real students.

A New Tagline

The older taglines for DTS have been things that primarily emphasize Biblical teaching such as "Standing Strong for the Truth" or "Training You Can Trust. Leaders You Can Follow."

We wanted something shorter and somewhat pithy that emphasized both aspects of truth and love. Here's what we came up with.

  • Teach Truth. Love Well.

Not too revolutionary, but hopefully it's clear and it communicates. Here's how Google sees it :)

image

Facebook Page and Ads Summary

DTS has traditionally run ads on large Christian website like ChristianityToday.com and OnePlace.com. But to reach younger students, we moved over to Facebook.

Also, in the past, a traditional DTS ad might have emphasized something like "Want to be a Pastor?", but many of today's seminarians are going into non-traditional forms of ministry. DTS grads still have a very strong emphasis on teaching the Bible, but more students are doing this outside the traditional church pastor role. The first few ads we ran emphasize these other forms of ministry. We ran each one for around a week and paid per click with click-through rates of around 0.10%.

image image

Which one do you like more?

The second ad turned out to be more successful (higher click-through rates). This is probably because the image is more appealing and "Dallas Seminary" doesn't show up until later in the text of the ad.

In addition to advertisements, we also setup a Facebook Page for DTS. Rather than aggressively market to students through physical mail or email blasts, this allows interested students to check out DTS and ask questions of current students and alumni before talking to an admissions counselor. On the GoingToSeminary blog, there is a great post about connecting to a current student to get real answers. Below is an example of a prospect asking a question that a current student answered on Facebook. That person got a real answer from a real student without ever needing to visit the main seminary website.

image

Connecting with Video

DTS's excellent AV team has also produced several videos that communicate the "Teach truth. Love Well." message. These are sent to alumni and supporters via email every few months.

Ben Stuart
Ben leads a college ministry called BreakAway which is attended by about 5,000 Texas A&M (my alma mater) students. His story is classic DTS and emphasizes "Teach Truth".

Christ in North Africa
Where Ben highlights "Teach Truth," Christy highlights "Love Well." She is an English teacher North Africa, but never "teaches" Christianity verbally; she does it by loving the girls in her classes.

William "Duce" Branch
"Duce" Branch is a pastor who ministers to the hip hop generation through his music.This is a guy who took 120 hours of course work and still managed to keep his love for his culture. He really embraces both elements of "Teach Truth. Love Well."

This last video also has a great metaphor for communicating to different audiences where the president of the school is shown on an upper-middle-class HDTV while Duce is shown on various older TVs. They both are working for the same goal with the same message, but the context and transmission system changes to be audience appropriate. Good stuff.

I hope this is a helpful summary of how DTS works and how the newer media forms can be used to communicate to changing audiences.

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
16

var baby = john.Meiosis() + amber.Meiosis();

by John Dyer

Just a little genetics (my college major) humor written in C# 3.0 syntax (with overloaded operators and an implicitely typed local var) to announce that we're going to be parents later this fall! Check out the sweet spin moves below. I'm calling all-star running back...

offspring01_sonogram
This video requires Adobe Flash player

Behold, children are a gift of the LORD; The fruit of the womb is a reward.” Psalm 127:3

 

Apr2008
14

Dallas Seminary Mobile Site

by John Dyer

Since Microsoft and Apple finally decided to allow/license versions of Flash Lite 3.0 for their mobile devices (Windows Mobile and iPhone), I created an initial build of a mobile site for Dallas Seminary which will eventually use Flash Lite 3.0 for all the audio and video content.

The goal was to make it look like an iPhone application using the list format, but have it work properly on other mobile devices by not specifically targeting the iPhone's screen size such as Windows Mobile and Blackberry. It's a bit more cramped on the smaller WM screen, but it works well.

Here are a few screen shots:

iPhone Windows Mobile
image dts_wm_home
image dts_wm_media

 

There is one gotcha on the iPhone that doesn't show up using Safari. The iPhone may try to startup zoomed out like it would need for a normal website. If your <div> tags are set to expand, the iPhone will stretch everything out and then zoom out.

 image

To fix this add the following:

<meta name="viewport" content="user-scalable=no,width=320,scale=1.0" />
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.

Mar2008
16

QTVR like Panoramas of Chichen Itza made in Flash with Papervision3D

by John Dyer

Last week, I visited Cancun with my in-laws and had a chance to see the ruins at Chichen Itza. I took a series of photos and made them into a panorama using Papervision3D (as done here by BlitzAgency for NBC's The Office and here by a company selling a product called Flash Panoramas). I made a little script that can handle both spherical and cubic images to see how both would look. Here's some previews of the two versions (The lines are there to show how the panorama is being constructed):

Spherical  
CropperCapture[45]
Cubic
CropperCapture[42]

A cubsic panorama is composed of six images (top, bottom, left, right, front, back) while a spherical panorama is made using a single image which is stretched at the top and bottom (like a map of the world where Greenland and Antarctica are distorted). In general, it looks like the spherical version can generate the same level of detail with fewer polygons and thus more speed, but the cubic images can be a little smaller and are more easily edited.

Each of these is composed from approximately 45-50 images. I took around 15 in a circle at the horizon, 15 looking up about 45 degrees, and 15 looking down 45 degrees. Unfortunately, I didn't take enough pictures to complete the sky or the ground below, so you'll see some black spots if you "look" up or down. I used Hugin to stitch the images together, and it did a pretty good job considering I didn't have a tripod or wide angle lens to accurately take the pictures. (link: the best tutorial I have seen on image stitching)

3D Panoramas

Original Images

The view page also has a list of commands (f=fullscreen) and links to the original files.

Update: I changed the navigation to be more like QTVR where you have to click to move, and there is an arrow cursor. Also, smoothing and precision are automatically applied when you stop moving.

Mar2008
12

Data Portability Pack now in BlogEngine.NET Core

by John Dyer

The Data Portability Pack that I posted about a few weeks ago has now made it into the core of BlogEngine.NET for version 1.4. Mads Kristensen added the SIOC and APML handlers, and Roman Clarkson is working on a FOAF implementation that includes adding some Profile data (first and last name, etc.) to users of a BE.NET instance.

FOAF (friend of a friend) currently doesn't have any way of (1) automatting "Add as Friend" style interactions (like Facebook or Myspace) or (2) verifying connections between people. Roman and I would like to find a secure way of implementing this functionality which would in effect create a decentralized social network. There are however, several problems to work through regarding security (public vs. private friends lists), identification (unique IDs: email or OpenID?), verification (preventing friend spam), and so on. These issues have been talked about for some time in the FOAF community, but so far there hasn't been an implementation. Here are some older (2003) and newer (2007) discussions on the subject to get caught up:

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

Feb2008
14

Data Portability Pack for BlogEngine.NET

by John Dyer

What it Does

This is an initial release of 3 extensions which will add the follow protocols to your BlogEngine.NET blog:

  • SIOC - Somewhat like RSS, but utilizes RDF to describe connections to  other sites and between users and posts (example).
  • APML - Creates an "attention profile" of your interests based on tags and categories. Each is given a value based on its frequency (example).
  • FOAF - A list of your friends, their websites, and a hashed version of their email for a unique key (example).

You can see examples of each of these by clicking on the links on the upper right of this blog. To learn more about data portability, see this post. The following links show this data in a browsable format:

How it Works

imageEach of these has a Handler, a Generator, and an Extension file. The extension files automatically add <meta> discovery files to your <head> tag.

Note: Currently, the FOAF information is stored in two different Extension files due to a limitation in BE.NET 1.3 (it cannot store both scalar and table data). One extension is for your personal information and the other is for your friends' data. BE 1.4 will allow these to be merged into one file.

Warning: BE.NET currently deletes extension data when you change *.cs files, so please backup your extension file when adding or changing extensions.

Installation and Usage

  1. Download Data Portability Pack for BlogEngine.NET
  2. Copy the *.cs files into your /App_Code/Extensions folder (you can also create a sub-directory within that folder)
  3. Add the following to your web.config file
    <add verb="*" path="sioc.axd" type="BlogEngine.DataPortability.SiocHandler, App_Code" validate="false"/>
    <add verb="*" path="foaf.axd" type="BlogEngine.DataPortability.FoafHandler, App_Code" validate="false"/>
    <add verb="*" path="apml.axd" type="BlogEngine.DataPortability.ApmlHandler, App_Code" validate="false"/>	
  4. Go to your Admin->Extensions page and you should see 4 extensions prefixed with "DP_". The FoafFriends and MyProfile extensions need data entered, the other ones you can just leave as their defaults.
    image
  5. Optionally, you can add links to each of these files (as I have on the upper right)

Future Work

  • Merge FOAF extensions for BE 1.4
  • Possibly store FOAF data in a separate file rather than as Extension data
  • Add additional settings to APML such as parsing blog roll
  • If you also add OpenID support to your blog, then you will have everything that is currently on the data portability standards list.

kick it on DotNetKicks.com

Feb2008
8

SIOC implementation for BlogEngine.NET

by John Dyer

Update: This is now part of the Data Portability Pack for BlogEngine.NET

A major feature of BlogEngine.NET is that it supports a lot of open standards. Keeping with this, I've setup an initial implementation of SIOC for BlogEngine.NET. SIOC (Semantically-Interconnected Online Communities) is an extension of RDF which goes beyond RSS in that it describes not only the content of a website, but the connections between site content, users on the site, and another websites. While RSS has one big document for recent content, SIOC splits up its data into (1) a site profile, (2) profiles for authors, (3) profiles for posts, and (4) profiles for comments. Each of these not only lists the author's name, but also includes FOAF (Friend of a Friend) data to help identify users across the Internet via a hashed email address. If all sites were SIOC/FOAF enabled, you could theoretically search an aggregator site (like Technorati if it supported SIOC) for all posts and comments by a given user within a set of blogs, forums, or other websites. For more information on this, see my post on Data Portability standards.

Site Profile

To get a feel for the data, here's what the site profile looks like (note: I've tried to match the Word Press SIOC plugin URL conventions as much as possible)
http://johndyer.name/sioc.axd?sioc_type=site

<rdf:RDF>
<!-- users -->
<sioc:Usergroup rdf:about="http://localhost/johndyer/sioc.axd?sioc_type=site#authors"> <dc:title>Authors at "John Dyer"</dc:title> <sioc:has_member> <sioc:User rdf:about="http://localhost/johndyer/author/John+Dyer.aspx" rdfs:label="John Dyer"> <sioc:see_also rdf:resource="http://localhost/johndyer/sioc.axd?sioc_type=user&amp;sioc_id=John+Dyer" /> </sioc:User> </sioc:has_member> </sioc:Usergroup>
<!-- site info -->
<sioc:Site rdf:about="http://johndyer.name/"> <dc:title>John Dyer</dc:title> <dc:description>ASP.NET, Flash, and JavaScript from FreeTextBox and Dallas Seminary</dc:description> <sioc:link>http://johndyer.name/</sioc:link> <sioc:host_of>http://johndyer.name/sioc.axd?sioc_type=site#webblog</sioc:host_of> <sioc:has_group>http://johndyer.name/sioc.axd?sioc_type=site#authors</sioc:has_group>
</sioc:Site>
<!-- blog info, posts--> <sioc:Forum rdf:about="http://johndyer.name/sioc.axd?sioc_type=site#webblog"> <sioc:name>John Dyer</sioc:name> <sioc:link rdf:resource="http://johndyer.name/sioc.axd?sioc_type=site#webblog" /> <sioc:container_of> <sioc:Post rdf:about="http://johndyer.name/post/2008/02/BlogEngineNET-Extension-Flash-Video-Player.aspx" dc:title="BlogEngine.NET Extension: Flash Video Player"> <rdfs:seeAlso rdf:resource="http://johndyer.name/sioc.axd?sioc_type=post&amp;sioc_id=06af08e0-c4eb-421c-9e18-b618358d33ca" /> </sioc:Post> </sioc:container_of> </sioc:Forum> </rdf:RDF>

This describes (1) the users on the site (sioc:Usergroup) which would be blog authors, (2) the site in general (sioc:Site), and (3) and displays the content (sioc:Forum). Each of these has links to other SIOC documents for the user and then for the posts.

Post Profile

Here's what a post looks like:
http://johndyer.name/sioc.axd?sioc_type=post&sioc_id=612564cd-3cfa-42b5-92e9-44beb52f7cd5

<?xml version="1.0" encoding="utf-8"?>
<rdf:RDF xmlns:foaf="http://xmlns.com/foaf/0.1/" xmlns:rss="http://purl.org/rss/1.0/" xmlns:admin="http://webns.net/mvcb/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:content="http://purl.org/rss/1.0/modules/content" xmlns:sioc="http://rdfs.org/sioc/ns#">
  <foaf:Document rdf:about="http://johndyer.name/">
    <dc:title>SIOC post profile for "John Dyer"</dc:title>
    <dc:description>A SIOC profile describes the structure and contents of a weblog in a machine readable form. For more information please refer to http://sioc-project.org/.</dc:description>
    <foaf:primaryTopic>http://johndyer.name/post/2008/02/Website-Break-Coke-vs-Diet-Coke-vs-Coke-Zero.aspx</foaf:primaryTopic>
    <admin:generatorAgent>BlogEngine.NET SIOC Generator (1.0) by John Dyer</admin:generatorAgent>
  </foaf:Document>
<!-- post data -->
<sioc:Post rdf:about="http://johndyer.name/post/2008/02/Website-Break-Coke-vs-Diet-Coke-vs-Coke-Zero.aspx"> <sioc:link rdf:resource="http://johndyer.name/post/2008/02/Website-Break-Coke-vs-Diet-Coke-vs-Coke-Zero.aspx" /> <sioc:has_container rdf:resource="http://johndyer.name/sioc.axd?sioc_type=site#webblog" /> <dc:title>Website Break: Coke vs. Diet Coke vs. Coke Zero</dc:title> <!-- post author -->
<sioc:has_creator> <sioc:User rdf:about="http://johndyer.name/author/John+Dyer.aspx"> <rdfs:seeAlso rdf:resource="http://johndyer.name/sioc.axd?sioc_type=user&amp;sioc_id=John+Dyer" /> </sioc:User>
</sioc:has_creator> <foaf:maker> <foaf:Person rdf:about="http://johndyer.name/author/John+Dyer.aspx" foaf:name="John Dyer"> <foaf:mbox_sha1sum>3eb435778fafc3efde35fba2ca731c6d3234178f</foaf:mbox_sha1sum> <foaf:homepage rdf:resource="http://johndyer.name/" /> <rdfs:seeAlso rdf:resource="http://johndyer.name/sioc.axd?sioc_type=user&amp;sioc_id=John+Dyer" /> </foaf:Person> </foaf:maker>
<dcterms:created>2/1/2008 4:40:53 PM</dcterms:created> <sioc:content> Today, we decided to take a break from web development and do something truly important: a taste test. Tim, Adam, Brian, and I wanted to see if we could identify Coke, Diet Coke, and Coke Zero in a blind test. We each had three cups that another team member had setup. We tasted each drink and tried to match the drink with the cup and also mark our favorite. Tim is the narrator and then the participants from left to right are: John (me), Adam, Tim, and Brian. </sioc:content> <content:encoded>&lt;p&gt;Today, we decided to take a break from web development and do something truly important: a taste test. Tim, Adam, Brian, and I wanted to see if we could identify Coke, Diet Coke, and Coke Zero in a blind test. We each had three cups that another team member had setup. We tasted each drink and tried to match the drink with the cup and also mark our favorite. Tim is the narrator and then the participants from left to right are: John (me), Adam, Tim, and Brian.&lt;/p&gt;</content:encoded> <!-- categories and tags -->
<sioc:topic rdfs:label="Off Topic" rdf:resource="http://johndyer.name//category/Off Topic.aspx" /> <sioc:topic rdfs:label="challenge" rdf:resource="http://johndyer.name//?tag=/challenge" /> <sioc:topic rdfs:label="soda" rdf:resource="http://johndyer.name//?tag=/soda" />
<!-- comments --> <sioc:has_reply> <sioc:Post rdf:about="http://johndyer.name/post/2008/02/Website-Break-Coke-vs-Diet-Coke-vs-Coke-Zero.aspx#id_a82699f9-77e2-460d-b3b1-d1d470180df9"> <rdfs:seeAlso rdf:resource="http://johndyer.name/sioc.axd?sioc_type=comment&amp;sioc_id=a82699f9-77e2-460d-b3b1-d1d470180df9" /> </sioc:Post> </sioc:has_reply>
<!-- outbound links --> <sioc:links_to rdf:resource="http://www.cokezero.com/" rdfs:label="Coke Zero" /> </sioc:Post> </rdf:RDF>

This one also has a "document" description (which I left out on the site profile) and then has information about the post including its author (both sioc:has_creator and foaf:maker which has my email as a SHA1 hash), content (sioc:content and content:encoded), categories (sioc:topic), outbound links (sioc:links_to), and then links to the comments for the post (sioc:has_reply)

Download and Installation

  1. Download BlogEngine.NET SIOC extension
  2. Copy the three files into the \App_Code\Sioc\ directory
  3. Add the following line to your web.config
    <add verb="*" path="sioc.axd" type="SIOC.SiocHandler, App_Code" validate="false"/>

Update: This is now part of the Data Portability Pack

What's Next?

  1. This extension will add a <link> tag to your header, but you view it directly at: http://yoursite.com/sioc.axd
  2. Explore the SIOC file at:  http://sparql.captsolo.net/browser/browser.py?url=http%3A%2F%2Fjohndyer.name%2Fsioc.axd
  3. An SIOC explorer is also available here which allows you to search and sort the data from among several sites: http://www.activerdf.org/sioc/
Feb2008
5

Introduction to DataPortability and the state of its standards

by John Dyer

imageSince November 2007, the DataPortability discussion has been big news. The following post is an overview of the underlying ideas of "data portability" as well as the status of the proposed technologies which will enable it.

What is Data Portability?

The idea behind "data portability" is that website visitors should be able to control the data that a site has about them. It's a philosophical ideal that harkens back to the early days of the Internet before big businesses got into the game, and the Internet was thought to be a place of independence and freedom. Today, Gmail has all my email, Facebook has my friends, flickr my photos, Amazon my book and music purchases, and so on. DataPortability is a work group which is trying to urge these web companies to allow its users to export all of their data and be able to import into another site at will. Over the past month, representatives from big players like Microsoft, Google, and Facebook have joined DataPortability to help create standards for data transmission.

Here's a video explanation:


DataPortability - Connect, Control, Share, Remix from Smashcut Media on Vimeo.

What will I hopefully be able to do with Data Portability?

Though you can't do anything yet since "data portability" is just an idea right now, here are two possible scenarios:

Transferring friends and interests between sites. Facebook has data on who my "friends" are and information about things they like; Amazon has information on book and music I like based on what I've purchased and clicked on over the years; Pandora has information on what music I like, and so on. If one of my musician friends joined another social network like Bebo and I wanted to follow him, "data portability" would allow me to compile my interests and friends from Facebook, Amazon, and Pandora together and import that data into Bebo so I didn't have to start over there. It would already know what I like and who my friends are.

Collating my online activities. Much of my life has either "happened" on the web (blog posts, facebook messages, flickr photos, etc.) or I have left a record of real world events on the Internet. It would be good for me to be able to get all of this data and store it for my own use. Today I can't do this, but if all those sites were enabled with "data portability," I could theoretically pull out my photos from flickr, tickets from American Airlines, blog posts, emails, reviews at Trip Advisor, etc. and put together a timeline of my last vacation.

What possible problems are there with Data Portability?

"Data portabilty" sounds great in theory, but there are some problems that have to be overcome before it can really happen. These "problems" are not deal-breakers, just issues that the DataPortability workgroup is attempting to solve, and things users ought to think through before they immediately adopt such technology.

Identity verification. Probably the biggest technical hurdle is in the area of "identity management." If your list of friends has more than one person named "John Smith" or a friend with just a screen name like "JohnMayerLover556," then it will be difficult to import these into another site, especially if that site has has 10 more users named "John Smith." Websites usually assign a number to keep track of each user (a unique ID), but these cannot be shared among sites since they often overlap. Some standards propose using an email address as a unique ID, but people generally don't want their email address openly shared because of SPAM. One proposed standard (FOAF, see below) proposes using hashing the email address, but currently many sites don't allow email to be exported (such as Facebook). Also, since this information is being exported from a site, it is then made more public. This will require that "friends" can be verified in both directions since someone could merely copy the unique ID into their friends list.

Business value. There is some question as to why big websites would want to do "data portability" even if they've already joined the DataPortability workgroup. Most introductions to "data portability" usually use Amazon as an example, but interestingly Amazon has not yet joined. They have worked hard to build a great store, and they have collected a lot of data over a long period of time that they are not likely to want to give up to every new site that pops up. Similarly, Facebook's targeted advertisement platform is built on the interests that users enter into the site. This is an important part of their business model, and not something they are likely to give up unless there is a good business reason to do so. I don't think that Google, Microsoft, and Facebook joined DataPortability merely of good will and adherence to philosophical ideals of freedom, but because they believe they can make money doing so.

Security. As with "identity management" the overall security of publicly posting a lot of data about oneself could pose problems. For example, if I post all my interests, all my website associations, all my personal relationships in accessible XML files, then "identity" theft could be a genuine possibility. If someone can do damage with just a name, address, and social security number, imagine what they could do with data that allows them to mimic your lifestyle.

What technologies will hopefully enable Data Portability?

So "data portability" has awesome potential and some problems to work through. Another issue is what "standards" should be used to transfer this data. Long before the DataPortability workgroup was formed, several formats were proposed to handle this kind of information. Here is a summary of four of the most important ones:

  • image OpenID - Instead of having separate usernames and passwords for each site you visit, OpenID allows you to have one login across the internet that you control through a URL. For example, I could have openid.johndyer.name be my OpenID. If I go to a site that supports OpenID, I would not enter a username and password at that site, but just my OpenID (openid.johndyer.name). The site would send me to openid.johndyer.name where login and then verify that I wanted to return to the sending site. This way, I control the username and password and I can use it anywhere.
  • image SIOC (Semantically-Interlinked Online Communities) - SIOC's goal is to connect websites and content on those sites together. For example, it should be able to help identify blog posts, Amazon book reviews, blog comments, and forum posts made by the same person. Also, it should be able to semantically connect websites and blogs based on their tags and content. Right now, plugins exist for popular web applications (such as WordPress and phpBB), but there are very few applications which can actually do much with the data.
  • imageFOAF (Friend of a Friend) - While SIOC is about websites and content, FOAF is more about people and relationships. The idea is to be able to represent "friend" lists in a standard way. Obviously this introduces security questions (do I really want my friends on Facebook importing my data into xanga?), but some have already been addressed. FOAF proposes using either OpenID or a hashed email as a unique ID. In addition to relationships, FOAF can also describes activities people do through calendars, photos, blogs, etc. (working example: Facebook FOAF exporter app)
  • image APML (Attention Profile Markup Language) - APML is the proposed standard for cataloging "interest." The idea is to take all of the things you do on the web (uploading photos, blog posting, music and book purchases, etc.) and organize the related subjects and topics into one standardized document of your interests. If you found a new music store or website, and you wanted to see if it had things you like, you could upload your APML document, and it would filter its data to only show things of interest to you. (see: http://www.engagd.com/ for a working example)

There are also additional proposals for relationships (XFN), personal details (hCard), and identity management (Yarid) which have some overlap with the standards above. Also, OMPL is a standard for collecting lists such as the RSS feeds. Here is the full list of proposed standards.

What does it all mean and what can I do today?

Since there are very few real world uses of these technologies, "data portability" is only an idea at this point. Right now, there are no sites listed as "DP Enabled" This is mostly because "DP" hasn't been fully defined yet which shows us how early in the game this all is. OpenID perhaps has the greatest number of sites which support it, though some sites are merely offering their own login as an OpenID (for example, Yahoo offers openid.yahoo.com/youryahooid as an OpenID URL). AMPL, FOAF, and SIOC are currently not supported by any major sites, and they may not for some time unless a unique ID can be reliably used. Also, it seems that the current standards can't really handle all that the entire "connect, control, share, remix" vision, so a lot of work will probably need to be done. Hopefully it will go more smoothly than HTML5 and ES4 development.

Interestingly, the idea behind "data portability" is a lot like the early 90s before the Internet was widely available and people were on closed networks like AOL and Compuserve. They could only send email to people on the same network, but when Netscape came on the scene, access became universal, email and HTML became standard, and it changed everything. Today users are on different networks (MySpace, Facebook, xanga, etc.) with no way to connect. Hopefully, "data portability" will be able to change all that.

Links:

Feb2008
4

BlogEngine.NET Extension: Flash Video Player

by John Dyer

Last week, I posted a flash video using flvplayer extension from betaparticle for BlogEngine.NET. I wanted to make some adjustments to the extension and I ended up completely rewriting it.

Updates/improvements:

  • Plays FLV or MP4.
  • All properties are available in the BlogEngine.NET extension manager so you don't have to touch the code to change settings.
  • The size of the player will automatically make room for the controls at the bottom (20px)
  • Uses SWFObject for flash replacement. This allow graceful degradation for those without Flash including mobile users. (I'll upgrade it to SWFObject 2.0 when it's released)
  • Enables Fullscreen playback.
  • Shows an initial image at startup named the same as the flv (myvideo.flv and myvideo.jpg). This way the video is not black at startup.
  • Optionally, instead of SWFObject, full <object><embed> formatting can be used.

Credits

Uses

Installation & Usage

  1. Download the file and unzip: Flash Video Player extension for BlogEngine.NET
  2. Copy FlashVideoPlayer.cs to /App_Code/Extensions/
  3. Copy the flashvideo folder to the root of your application (this can be customized in the settings)
  4. Add videos (myvideo.flv or myvideo.mp4) and images (myvideo.jpg) to the flashvideo folder.
  5. To use, just add [flv:myvideo.flv] anywhere in your post.

Example

oregon
This video requires Adobe Flash player

Download:

Feb2008
1

Website Break: Coke vs. Diet Coke vs. Coke Zero

by John Dyer

Today, we decided to take a break from web development and do something truly important: a taste test. Tim, Adam, Brian, and I wanted to see if we could identify Coke, Diet Coke, and Coke Zero in a blind test. We each had three cups that another team member had setup. We tasted each drink and tried to match the drink with the cup and also mark our favorite. Tim is the narrator and then the participants from left to right are: John (me), Adam, Tim, and Brian.

coke_challenge
This video requires Adobe Flash player

Results: Adam, Brian, and I all correctly identified the three flavors of Coke. Adam and I choose Coke Zero as the best, while Brian favored Coke Classic. The most interesting result was that Tim thought Coke Zero was Coke Classic and he also thought it was the best tasting. So, Coke Zero is our big winner.

coke_challenge

Please post your workplace challenge!