Spinning SOA onto Google Languge Translator

Turning Google Language Translator (GLT) into a consumable service was a hobby project of mine from 2006.  I noticed that it had stopped working so I decided to fix it and at the same time put together some documentation on the cruddy code.  Knowing what I do today I would more than likely want to re-write the code base but for the moment will leave it as is for this documentation.

Here is a screen capture of the end results for the running samples.

Running Samples
The test page shows off the basic functionality and the only bug I notice is that the "Submit Data" button is not translating and I it should be so I probably lost some code along the way.

Tools for this project.

  1. Google Language Translator (GLT)
  2. Microsoft Fiddler
  3. AJAX.Net Professional
  4. C# in Microsoft .Net (but you could use any language here to do the HTTP POST using the details below)
  5. JSONP style data encapsulation for returning data to JavaScript
  6. jQuery the most awesome JavaScript client side library know to mankind IMHO
  7. Translator.js file I put together to wrap all this functionality up nice an neat.
  8. Language Translator Mashup Test Page with basic examples and usage tutorials.

So it seems that everyone out there puts out one integration or another for language translation services but I wanted a little more Web 2.0ish sort of approach.

Some of the limitations are that it can only handle limited size datasets due to QueryString length limitations in most browsers but if you host it on the same domain

Given the GLT one can easy use screen scraping to strip out translated text to create a nice mashup.  So first we need to understand how the basic GLT page works and for this I used Microsoft Fiddler to come up with the following details.

From this


Fiddler Raw shows this

GLT uses HTTP POST action to
    http://translate.google.com/translate_t page.

GLT POST Data from Microsoft Fiddler
    hl=en&ie=UTF8&text=Hello+World&langpair=en%7Cde

The drop down provides all the language code pairs so that data is easily extracted

en|en English
en|ar English to Arabic BETA
en|zh-CN English to Chinese (Simplified) BETA
en|fr English to French
en|de English to German
en|it English to Italian
en|ja English to Japanese BETA
en|ko English to Korean BETA
en|pt English to Portuguese
en|ru English to Russian BETA
en|es English to Spanish


The Service

Now I just need a wrapper for talking to this web page and putting in my own values and for this I have taken some C# code that I use here there and everywhere..  All this following code does is to read a web page using a HTTP POST Request and gets back the Raw HTML and parses it for returning only the contents of the <DIV> tags (<div id=result_box dir="ltr">).  Support for ltr left to right text and and rtl right to left language support is there so I wrapped that up as well.

Alos note that I wrap up the contents of the HTML result in that DIV tag into a JSON formatted data result using the AJAX.Net Professional toolkit and the AjaxPro.JavaScriptSerializer.Serialize method to wrap the data in a JSONP style return format for JavaScript use. The JavaScript callback method is always parseResults(data.)

Here is the C# code that I make the RPC call to.  You can easily wrap this code by just checking out the AJAX.Net Quick Start Guide. I used my own wrapper around an ASHX file to expose this functionality which is just as easy and was done before I understood the AJAX.Net API set.  I also left the PROXY code in place as a web.config setting in case your behind a firewall.

/// <summary>

///

/// </summary>

/// <returns></returns>

public string GetTranslatedText()

{

      current.Response.Expires = 60;

      current.Response.ExpiresAbsolute = DateTime.Now.AddMinutes(60);

      //current.Response.AddHeader("cache-control", "private"); // Cache-Control: no-cache

      //current.Response.CacheControl = "no-cache";             // Pragma: no-cache  same as Response.AddHeader("pragma", "no-cache");

     

      string postToURL = @"http://translate.google.com/translate_t";

      string _userAgent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322)";;

      string _accept = "image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, application/x-shockwave-flash, */*";

      string postData = "hl=en&ie=UTF8&text=" + System.Web.HttpUtility.UrlEncode(current.Request["text"].ToString()) + "&langpair=" + System.Web.HttpUtility.UrlEncode(current.Request["langpair"].ToString());

 

      string _responseText = "";

 

      HttpWebRequest loHttp;

      HttpWebResponse loWebResponse;

      StreamReader loResponseStream;

      Encoding enc;

      Stream loPostData;

 

      //--------------

      //- This code can be used if you need to go out over a PROXY

      string ISNProxy = "";

      try

      {

            string iSNProxy = ConfigurationSettings.AppSettings["ISNProxy"].ToString();

            ISNProxy = iSNProxy;

      }

      catch

      {

      }

     

      Uri proxyURI = null;

      System.Net.WebProxy myProxy = null;

 

      if (ISNProxy.Length > 0)

      {

            proxyURI = new Uri(ISNProxy);

            myProxy = new System.Net.WebProxy(proxyURI);

            myProxy.Address = proxyURI;

      }

      //HTTPRequest = ((HttpWebRequest) WebRequest.Create(strURL));

      //HTTPRequest.Proxy = myProxy;

      //--------------

 

      try

      {

            loHttp = (HttpWebRequest) WebRequest.Create(postToURL);

            if (ISNProxy.Length > 0)

                  loHttp.Proxy = myProxy;

 

            loHttp.Timeout = 15000;     // 5 secs

            loHttp.AllowAutoRedirect = false; // default is true

            loHttp.Method = "POST";

            loHttp.Accept = _accept;

            loHttp.Referer = postToURL;

            loHttp.ContentType = "application/x-www-form-urlencoded";

            loHttp.UserAgent = _userAgent;

            loHttp.KeepAlive = false;

            byte [] lbPostBuffer = System.Text.Encoding.GetEncoding(65001).GetBytes(postData);

 

            loPostData = loHttp.GetRequestStream();

            loPostData.Write(lbPostBuffer,0,lbPostBuffer.Length);

            loPostData.Close();

            loWebResponse = (HttpWebResponse) loHttp.GetResponse();

            enc = System.Text.Encoding.GetEncoding(65001);

            loResponseStream = new StreamReader(loWebResponse.GetResponseStream(),enc);

            _responseText = loResponseStream.ReadToEnd();

 

            loWebResponse.Close();

            loResponseStream.Close();

 

            string pos1 = "<div id=result_box dir=\"ltr\">";

            string pos2 = "</div>";

 

            // handle "right to left"/"left to right" languages....

            if ( _responseText.IndexOf(pos1) < 0)

                  pos1 = "<div id=result_box dir=\"rtl\">";

 

            if ( _responseText.IndexOf(pos1) > 0)

            {

                  _responseText = _responseText.Substring(_responseText.IndexOf(pos1));

                  _responseText = _responseText.Substring(0,_responseText.IndexOf(pos2) + pos2.Length);

            }

      }

      catch(WebException ex)

      {

            _responseText = ex.Message;

      }

 

      string [] packageData = { _responseText };

      string callBack = current.Request["callback"];

      string fieldID =  current.Request["id"];

      if ( fieldID != null)

      {

            ResponseDataTest rd = new ResponseDataTest();

            rd.TargetID = fieldID;

            rd.ResponseText = _responseText;

            _responseText = AjaxPro.JavaScriptSerializer.Serialize(rd);

      }

      else

            _responseText = AjaxPro.JavaScriptSerializer.Serialize(packageData);

 

      if ( callBack != null)

            _responseText = "parseResults(" + _responseText + ");";

 

      return _responseText;

}


The Client Side JavaScript

So the above code goes out and gets back a JSON formatted data set that can easily be embedded into a Web Page.  I wrote translator.js support file to wrap just this sort of functionality and to give a control style of helper but note that this .js file could be moved anywhere and will work X-Domain.  Sorry the header comments of the translator are a bit out of date and much has been changed or removed over time.  Also much of the code should be changed now that I have a much better idea of how jQuery works and all the much better changes that are now a part of the jQuery 1.2.3 release.

/*

File         : Translator.js 1.0.0.0 - Language Translation Mashup Tool

Description  : Web 2.0 Mashup Tool for adding Language Translation to HTML

Developed By : Kevin M Pirkl

Release Date : 12-24-2006

             :

Notes        : Translator.js supports HTTP GET for cross domain text translating

             :  via JSON Script style callback methods.  This script also has 

             :  Support for POST style calls will require that you write

             :  your own Web Proxy page for the callbacks XSS issue.

             :  This is only necessary if your data size  is greater than 2048

             :  characters.  Keep the text areas small and it should not be a problem

             :

             : This site can serve as Proxy point for your own GET style calls

             :  but you must take into account XSS security when doing so.

             :  It would be easy for another person to set this demo up and

             :  place in an attack vector.  References later...

             :

             : In order to assure some level of security my server has limted

             :  the callback method to always return "parseResults(...)" no

             :  matter what parameter you put into your code.

             :  Google Translator page also turns HTML into Encoded HTML Entities

             :  thus disabling JavaScript injection

                   :

             : GET support stylized after JSON Script callback's approach and the

             :  inherent security risk is related to Site Trust.  Ask the question

             :  do you trust the site that's doing the the translating work not to

             :  return harmful JavaScript code. There are many different XSS attack

             :  vectors out there http://www.isecpartners.com/files/iSEC-Attacking_AJAX_Applications.BH2006.pdf

             :  so the general rule of thumb it be careful who you trust.

             :

             : POST support for Translation text and URL lengths exceeding

             :  2048 characters will require you to wite a Proxy Web Page.

             :  Included sample Web Page Proxy Code for ASP.Net.

             :  More infomation about XHR and XSS is available at

             :  http://en.wikipedia.org/wiki/XHR

             :  http://en.wikipedia.org/wiki/Cross_site_scripting

             :

             : Other Concepts in this sample

             : 1> Hover concept for icons adopted from jQuery Cookbook

             :

             :

             : 4> Translator Page Scape from http://translate.google.com/translate_t

             : 5> XHR XSS Issue solution originally taken from JSONscriptRequest but updated from

             :  technotes found at http://www.bluishcoder.co.nz/2006/12/cross-domain-json-with-fjsc.html

             :  and I added UTF support

             : 6> $.cookie function support taken from a variety of places and recoded from scratch

            

*/

 

//Proxy Page for Translation requests that packages data to JSON/JSONP format

var basePageURL = " http://softwarecommunity.intel.com/isn/Home/";

var proxyWebPage = "CommonServices.ashx";

 

var scriptCounter = 1; // assists in XHR-XSS issue workaround

var fieldIndexer  = 1;

 

//$.ajaxTimeout(10000); // nothing should last longer than 10 seconds to translate

 

// render the StyleSheet

document.writeln("<style>");

document.writeln(".globalLocalizerToolBar { font-size: 10px; vertical-align: middle;}");

document.writeln(".globalLocalizerToolBar img { vertical-align: middle; }");

document.writeln(".globalLocalizerToolBar { width: 400px; }");

document.writeln(".globalLocalizerToolBar select { background-color: #E6E6E6; font-size: 10px; vertical-align: middle; width: 220px; color: #555555; font-family: verdana, sans-serif;}");

document.writeln(".xglobalLocalizerIcons { filter:alpha(opacity=50);opacity:0.5; }");

document.writeln(".xglobalLocalizerIconsHover { filter:alpha(opacity=100);opacity:1; }");

document.writeln("</style>");

 

// Applying a Dynamic ID to the DOM for these found items to add support for JSONscriptRequest style callbacks

$(document).ready(function() {

      $("*.translator").each(

            function( i ) {

                  if ( this.id == null || this.id == "")

                        this.id = "_DynamicName" + i.toString();

            });

});

 

// Show Content Parts - External HTML

$(document).ready(function() {

            $("a.contentpart").each( function( i ) { ContentParts(this);})

            $("a.contentpart").hide();

});

 

//-- Setup the Global Language Selector & cookies

$(document).ready(function() {

 

      //Create the global user settings toolbar control if the Global Span Class class="globalLocalizerToolBar" is present

      if ($("span.globalLocalizerToolBar").size() > 0)

            CreateToolBar();

 

      // set toggle hide/show on the toolbar

      $("#gLTBExpander").toggle (

            function() {

                  toggleGlobalLanguageToolbar();

            },

            function() {

                  toggleGlobalLanguageToolbar();

            }

      )

 

      //-- Default User Settings

      if ($.cookie("enableAutoTranslate") == null)   //enable automatic translation on page load

            $.cookie("enableAutoTranslate", "false");

 

      if ($.cookie("gLTBExpanderSetting") == null)   //show/hide setting of the toolbar on page load

            $.cookie("gLTBExpanderSetting", "shown");

 

      if ($.cookie("translationLanguage") == null)   //User selected language to translate to.

            $.cookie("translationLanguage", "en|en", 30);

      else

            if( $("#langpair").size() > 0)

                  $("#langpair").val($.cookie("translationLanguage")); // Toolbar loaded so set the users last language selection

 

      // Set the AutoTranslate Icon's text to display On or Off message

      $("#menuAutoTranslateIcon").attr("title",($.cookie("enableAutoTranslate") == "true" ? "On":"Off")  + " - enable auto-translate on page open");

 

      //Cross Browser compatiable hover function

      $("img.globalLocalizerIcons").hover(

            function(){ $(this).addClass("globalLocalizerIconsHover"); },

            function(){ $(this).removeClass("globalLocalizerIconsHover"); }

      );

 

      // Hide the Global User Settings Toolbar's DropDown and AutoTranslate features on load

      if ( $.cookie('gLTBExpanderSetting') == 'hidden')

            $("#gLTBExpanderSpan").hide();  // contains the language selector and icon toolbar

 

      $("#gLTBLoading").hide();  // spinning globe loading animation

 

      // Seems to still be a bug in this so I hide the spinning globe manually

      //$("#gLTBLoading").ajaxStart(function(){

      //    $(this).show();

      //});

 

      // When Translator calls are done hite the spinning globe

      //$("#gLTBLoading").ajaxStop(function(){

      //    $(this).hide();

      //});

 

      // auto translate when page loads if it is enabled

      if (($.cookie("enableAutoTranslate") == "true"))     

            AutoTranslate();

 

});

 

 

// Show or hide support for the Translator Global Toolbar

function toggleGlobalLanguageToolbar()

{

      if ( $.cookie("gLTBExpanderSetting") == "hidden")

      {

            $.cookie("gLTBExpanderSetting", "shown");

            $("#gLTBExpanderSpan").show();

      } else

      {

            $.cookie("gLTBExpanderSetting", "hidden");

            $("#gLTBExpanderSpan").hide();

      }

}

 

//Enable or disable the AutoTranslate feature on page load.

function toggleAutoTranslate()

{

      // only session based cookie as to not waste space

      if ($.cookie("enableAutoTranslate") == "true") 

            $.cookie("enableAutoTranslate", "false");

      else

            $.cookie("enableAutoTranslate", "true");

 

      $("#menuAutoTranslateIcon").attr("title", ($.cookie("enableAutoTranslate") == "true" ? "On":"Off") + " - enable auto-translate on page open")

}

 

// Translate the text found in the Field by ID

// but creates a backup of it so it can be reverted

function Translate(textToTranslateFieldID)

{

      $("#gLTBLoading").show();

 

      var valOrHTML = "html";

      if ( $(textToTranslateFieldID).html()=="" && $(textToTranslateFieldID).val()!="" )

            valOrHTML = "val";

      if( !textToTranslateFieldID.old )

      {

            textToTranslateFieldID.old = valOrHTML == "html" ? $(textToTranslateFieldID).html() : $(textToTranslateFieldID).val();

            if ( $(textToTranslateFieldID).attr("dir") != null)

                  textToTranslateFieldID.oldDirection = $(textToTranslateFieldID).attr("dir");

            else

                  textToTranslateFieldID.oldDirection = "ltr";

      }

     

      //Implemented below are both GET & POST approaches.  GET implementation 

      // approach supports XSS calls while POST does not.  I wanted

      // this tool to be cross site script (XSS) capable so anyone could

      // make use of this sample code without the need for a Proxy Web Page

      // to route the Web Requests.  There are some limitations to GET and XSS

      // support.  GET URL Limits for the average browser is 2048 characters so

      // the entire URL cant be greater than that many characters without this

      // code failing over to a POST approach.

      //

      // I would propose the API call to the Google Service like the following URL to support

      //   JSON script callback style

      //

      // http://translate.google.com/translate_t?callback=parseResults&hl=en&ie=UTF8&text=" + escape(textToTranslateFieldID.old) + "&langpair=" + $.cookie("translationLanguage") + "&id=" + escape(textToTranslateFieldID.id)

      //

      //    callback=Name of a JavaScript callback function [optional] I have a provided method below parseResults(...)

      //    hl=en [required]

      //    ie=UTF8 [required]

      //    langpair=Value from the drop drop selection list found at http://translate.google.com/translate_t [required]

      //    id=ID of the DOM object where the data came from and result go into. [required]

      //

      // Definition of the JSON Callback Object

      //     jsonData.TargetID

      //     jsonData.ResponseText

      //

      var request = basePageURL + proxyWebPage + "?F=GetTranslatedText&callback=parseResults&hl=en&ie=UTF8&text=" + escape(textToTranslateFieldID.old) + "&langpair=" + $.cookie("translationLanguage") + "&id=" + textToTranslateFieldID.id;

      // Do a GET request via Cross Site Scripting for smaller data requests and POST if the request is larger

      // Small requests and be run through this server and not cause an error message

      if ( request.length < 2048)

      {

            //$.getScript(request);  // wow jQuery is much easier to do this with but the XSS issue still exists

           

            // Thanks to http://www.bluishcoder.co.nz/2006/12/cross-domain-json-with-fjsc.html for

            //   the following few lines of code to get around XHR cross domain issues

            //$("head/script#jsonrequest*").remove();

            var script = document.createElement("script");

            script.setAttribute("type", "text/javascript");

            //script.setAttribute("src", request + '&noCacheIE=' + (new Date()).getTime());

            script.setAttribute("src", request);

            script.setAttribute("charset", "UTF-8"); 

            script.setAttribute("id", "jsonrequest" + scriptCounter);

            $("head").append(script);

            scriptCounter++;

      }

      else

            alert("Translation data length > 2000 characters not supported in this version.");

//          $.post(basePageURL + proxyWebPage,{

//                F:"GetTranslatedText",

//                hl:"en",

//                ie:"UTF8",

//                id:textToTranslateFieldID.id,

//                text:textToTranslateFieldID.old,

//                langpair:$.cookie("translationLanguage")

//          },function(xml, status){

//             

//                var returnData;

//                try

//                {

//                      returnData = eval('(' + xml + ')');

//                      xml = returnData.ResponseText;

//                }

//                catch(err)

//                {

//                      //alert(err);

//                      xml = "Invalid in return results"

//                }

//         

//                // Error condition (POST Failed) - to handle later

//                if ( !(status == "success" || status == "notmodified") )

//                {

//                      $(textToTranslateFieldID).append("..timeout");

//                      //return;

//                }

//

//                setResults(textToTranslateFieldID, xml, valOrHTML);

//               

//          });

}

 

 

// [Required] by my Proxy Web Page when using callback this method is always returned    

function parseResults(jData)

{

      try

      {

            var valOrHTML = "html";

            if ( $("#"+jData.TargetID).html()=="" && $("#"+jData.TargetID).val()!="" && $("#"+jData.TargetID).val() != null)

                  valOrHTML = "val";

 

            setResults($("#"+jData.TargetID), jData.ResponseText, valOrHTML)

      }

      catch(err)

      {

            alert(err);

      }

}

 

 

//Parse and Place the GET/POST return results into the correct DOM element by ID

function setResults(textToTranslateFieldID, xml, valOrHTML)

{

      // -- Cant use this with Arabic language... RegExp does not work right with

      // --   characters out of the normal ascii range...  (it's a JavaScript issue)

            //var parsedResults = xml.match("<div.*?result_box dir=ltr>(.*?)</div>")[1];

            // alert(parsedResults);

            //$("#fieldID").html(parsedResults);

      // ---------

      var pos1 = "<div id=result_box dir=\"ltr\">";

      var pos2 = "</div>";

      var textAlign = "";

 

      // handle "right to left"/"left to right" languages....

      if ( xml.indexOf(pos1) < 0)

      {

            // not sure that this or the next line is the correct way to go but..

            pos1 = "<div id=result_box dir=\"rtl\">"; //$(textToTranslateFieldID).attr("dir","ltr"); //$(textToTranslateFieldID).attr("dir","rtl");

            textAlign = "right";

      }

           

      if ( xml.indexOf(pos1) > -1)

      {

            xml = xml.substring(xml.indexOf(pos1));

            xml = xml.substring(pos1.length,xml.indexOf(pos2));

            $(textToTranslateFieldID).css( "text-align", textAlign);

            xml = $.trim(xml);

            if ( xml.length > 0)  // fixes zero length translation bug from Google Beta Translators

            {

                  xml = HtmlDecode(xml);

                  if ( valOrHTML == "val")

                        $(textToTranslateFieldID).val(xml);

                  else

                        $(textToTranslateFieldID).html(xml);

            }

      }

      $("#gLTBLoading").hide();

}

 

 

//Global Settings Toolbar Creator

function CreateToolBar()

{

      // This is much more efficient than working out each item as a DOM element

      //  and adding it together piecemeal and this looks cleaner

      var textToRender = "";

      textToRender += "<img align=\"top\" class=\"globalLocalizerIcons\" id=\"gLTBExpander\" title=\"Click to show/hide the Global User Settings Toolbar\" src=\"" + basePageURL + "images/earth.gif\"><span align=\"right\" id=\"gLTBExpanderSpan\">&nbsp;";

      textToRender += "<span align=\"right\" id=\"gLTBExpanderSpan\">&nbsp;";

      textToRender += "<select id=\"langpair\" name=\"langpair\" onChange=\"setUserSelections();\">";

      textToRender += "<option value=\"en|en\">English</option>";

      textToRender += "<option value=\"en|ar\">English to Arabic BETA</option>";

      textToRender += "<option value=\"en|zh-CN\">English to Chinese&nbsp;(Simplified) BETA</option>";

      textToRender += "<option value=\"en|fr\">English to French</option>";

      textToRender += "<option value=\"en|de\">English to German</option>";

      textToRender += "<option value=\"en|it\">English to Italian</option>";

      textToRender += "<option value=\"en|ja\">English to Japanese BETA</option>";

      textToRender += "<option value=\"en|ko\">English to Korean BETA</option>";

      textToRender += "<option value=\"en|pt\">English to Portuguese</option>";

      textToRender += "<option value=\"en|ru\">English to Russian BETA</option>";

      textToRender += "<option value=\"en|es\">English to Spanish</option>";

      textToRender += "</select>&nbsp;";

      textToRender += "<img class=\"globalLocalizerIcons\" align=\"top\" id=\"menuAutoTranslateIcon\" onClick=\"toggleAutoTranslate();\" src=\"" + basePageURL + "images/enable.gif\">&nbsp;";

      textToRender += "<img class=\"globalLocalizerIcons\" align=\"top\" title=\"click to translate\" onClick=\"AutoTranslate();\" src=\"" + basePageURL + "images/cr16-action-translate.png\" >&nbsp;";

      textToRender += "<img align=\"top\" id=\"gLTBLoading\" title=\"Loading\" src=\"" + basePageURL + "images/globe.gif\" >";

     

      textToRender += "<br></span>";

 

      $("span.globalLocalizerToolBar").append(textToRender);

 

}

 

//Provide Automatic Translation of the page elements on page load

function AutoTranslate()

{

      if ($.cookie("translationLanguage") != "en|en")

            $("*.translator").each( function(i) {

                  Translate(this);

            });

      else

            revertTranslatedSections(); //reset all items to original form

}

 

 

//Remember user dropdown selections so Translator Toolbar displays correct language in the dropdown list.

function setUserSelections()

{

      $.cookie("translationLanguage", $("#langpair").val(), 30);

      AutoTranslate();

}

 

 

//Put field text back to the proper non-translated value

function revertTranslatedSections()

{

      //reset all items to original form

      $("*.translator").each (

            function( i ) {

                  if ( this.old)

                  {

                        if ( $(this).html() != "")

                              $(this).html(this.old);

                        else

                              $(this).val(this.old);

                        //$(this).attr("dir",this.oldDirection);

                        if ( this.oldDirection == "rtl")

                              $(this).css("text-align","right");

                        else

                        {

                              $(this).css("text-align","");

                        }

                  }

            }

      )

}

 

// Load external HTML into Content Parts on this page

function ContentParts(destination)

{

      $.get ( destination.href,

            function( contentPart){

                $(destination).after(contentPart);

                // I think there is a bug here that can be got around if you do something like so..

                //$(destination).html($(destination).html());

                // I have noticed that content parts CSS class's do not seem to be

                // placed but I need to investigat more later on.

            }

      );   

}

 

//

// Requires jQuery to be inluded before this extension in compilation order

// Ex..

//   $.cookie("test","value");   // Set a session cookie

//   $.cookie("test",false);   // Delete a session cookie

//   $.cookie("test");   // Read a Cookie value

//

//   etc..

$.cookie = function(name, value, expires, path, domain, secure) {

      if (typeof value == "undefined")

      {

            var nameEQ = name + "=";

            var ca = document.cookie.split(';');

            for(var i=0;i < ca.length;i++)

            {

                  var c = ca[i];

                  while (c.charAt(0)==' ')

                        c = c.substring(1,c.length);

                  if (c.indexOf(nameEQ) == 0)

                        return c.substring(nameEQ.length,c.length);

            }

            return null;

      } else if (value === false)

      {

            arguments.callee(name,"",-1);

      } else

      {

            if (typeof expires == 'number')

            {

                  var date = new Date();

                  date.setTime(date.getTime() + (expires * 24 * 60 * 60 * 1000));

                  expires = '; expires=' + date.toGMTString();

            } else

            {

                  expires = '';

            }

            path = (path) ? '; path=' + path : '';

            domain = (domain) ? '; domain=' + domain : '';

            secure = (secure) ? '; secure' : '';

            document.cookie = name + '=' + value + expires + path + domain + secure;

      }

};

 

 

// HtmlDecode http://lab.msdn.microsoft.com/annotations/htmldecode.js

//   client side version of the useful Server.HtmlDecode method

//   takes one string (encoded) and returns another (decoded)

function HtmlDecode(s)

{

      var out = "";

      if (s==null) return;

 

      var l = s.length;

      for (var i=0; i<l; i++)

      {

            var ch = s.charAt(i);

           

            if (ch == '&')

            {

                  var semicolonIndex = s.indexOf(';', i+1);

                 

            if (semicolonIndex > 0)

            {

                        var entity = s.substring(i + 1, semicolonIndex);

                        if (entity.length > 1 && entity.charAt(0) == '#')

                        {

                              if (entity.charAt(1) == 'x' || entity.charAt(1) == 'X')

                                    ch = String.fromCharCode(eval('0'+entity.substring(1)));

                              else

                                    ch = String.fromCharCode(eval(entity.substring(1)));

                        }

                    else

                      {

                              switch (entity)

                              {

                                    case 'quot': ch = String.fromCharCode(0x0022); break;

                                    case 'amp': ch = String.fromCharCode(0x0026); break;

                                    case 'lt': ch = String.fromCharCode(0x003c); break;

                                    case 'gt': ch = String.fromCharCode(0x003e); break;

                                    case 'nbsp': ch = String.fromCharCode(0x00a0); break;

                                    case 'iexcl': ch = String.fromCharCode(0x00a1); break;

                                    case 'cent': ch = String.fromCharCode(0x00a2); break;

                                    case 'pound': ch = String.fromCharCode(0x00a3); break;

                                    case 'curren': ch = String.fromCharCode(0x00a4); break;

                                    case 'yen': ch = String.fromCharCode(0x00a5); break;

                                    case 'brvbar': ch = String.fromCharCode(0x00a6); break;

                                    case 'sect': ch = String.fromCharCode(0x00a7); break;

                                    case 'uml': ch = String.fromCharCode(0x00a8); break;

                                    case 'copy': ch = String.fromCharCode(0x00a9); break;

                                    case 'ordf': ch = String.fromCharCode(0x00aa); break;

                                    case 'laquo': ch = String.fromCharCode(0x00ab); break;

                                    case 'not': ch = String.fromCharCode(0x00ac); break;

                                    case 'shy': ch = String.fromCharCode(0x00ad); break;

                                    case 'reg': ch = String.fromCharCode(0x00ae); break;

                                    case 'macr': ch = String.fromCharCode(0x00af); break;

                                    case 'deg': ch = String.fromCharCode(0x00b0); break;

                                    case 'plusmn': ch = String.fromCharCode(0x00b1); break;

                                    case 'sup2': ch = String.fromCharCode(0x00b2); break;

                                    case 'sup3': ch = String.fromCharCode(0x00b3); break;

                                    case 'acute': ch = String.fromCharCode(0x00b4); break;

                                    case 'micro': ch = String.fromCharCode(0x00b5); break;

                                    case 'para': ch = String.fromCharCode(0x00b6); break;

                                    case 'middot': ch = String.fromCharCode(0x00b7); break;

                                    case 'cedil': ch = String.fromCharCode(0x00b8); break;

                                    case 'sup1': ch = String.fromCharCode(0x00b9); break;

                                    case 'ordm': ch = String.fromCharCode(0x00ba); break;

                                    case 'raquo': ch = String.fromCharCode(0x00bb); break;

                                    case 'frac14': ch = String.fromCharCode(0x00bc); break;

                                    case 'frac12': ch = String.fromCharCode(0x00bd); break;

                                    case 'frac34': ch = String.fromCharCode(0x00be); break;

                                    case 'iquest': ch = String.fromCharCode(0x00bf); break;

                                    case 'Agrave': ch = String.fromCharCode(0x00c0); break;

                                    case 'Aacute': ch = String.fromCharCode(0x00c1); break;

                                    case 'Acirc': ch = String.fromCharCode(0x00c2); break;

                                    case 'Atilde': ch = String.fromCharCode(0x00c3); break;

                                    case 'Auml': ch = String.fromCharCode(0x00c4); break;

                                    case 'Aring': ch = String.fromCharCode(0x00c5); break;

                                    case 'AElig': ch = String.fromCharCode(0x00c6); break;

                                    case 'Ccedil': ch = String.fromCharCode(0x00c7); break;

                                    case 'Egrave': ch = String.fromCharCode(0x00c8); break;

                                    case 'Eacute': ch = String.fromCharCode(0x00c9); break;

                                    case 'Ecirc': ch = String.fromCharCode(0x00ca); break;

                                    case 'Euml': ch = String.fromCharCode(0x00cb); break;

                                    case 'Igrave': ch = String.fromCharCode(0x00cc); break;

                                    case 'Iacute': ch = String.fromCharCode(0x00cd); break;

                                    case 'Icirc': ch = String.fromCharCode(0x00ce ); break;

                                    case 'Iuml': ch = String.fromCharCode(0x00cf); break;

                                    case 'ETH': ch = String.fromCharCode(0x00d0); break;

                                    case 'Ntilde': ch = String.fromCharCode(0x00d1); break;

                                    case 'Ograve': ch = String.fromCharCode(0x00d2); break;

                                    case 'Oacute': ch = String.fromCharCode(0x00d3); break;

                                    case 'Ocirc': ch = String.fromCharCode(0x00d4); break;

                                    case 'Otilde': ch = String.fromCharCode(0x00d5); break;

                                    case 'Ouml': ch = String.fromCharCode(0x00d6); break;

                                    case 'times': ch = String.fromCharCode(0x00d7); break;

                                    case 'Oslash': ch = String.fromCharCode(0x00d8); break;

                                    case 'Ugrave': ch = String.fromCharCode(0x00d9); break;

                                    case 'Uacute': ch = String.fromCharCode(0x00da); break;

                                    case 'Ucirc': ch = String.fromCharCode(0x00db); break;

                                    case 'Uuml': ch = String.fromCharCode(0x00dc); break;

                                    case 'Yacute': ch = String.fromCharCode(0x00dd); break;

                                    case 'THORN': ch = String.fromCharCode(0x00de); break;

                                    case 'szlig': ch = String.fromCharCode(0x00df); break;

                                    case 'agrave': ch = String.fromCharCode(0x00e0); break;

                                    case 'aacute': ch = String.fromCharCode(0x00e1); break;

                                    case 'acirc': ch = String.fromCharCode(0x00e2); break;

                                    case 'atilde': ch = String.fromCharCode(0x00e3); break;

                                    case 'auml': ch = String.fromCharCode(0x00e4); break;

                                    case 'aring': ch = String.fromCharCode(0x00e5); break;

                                    case 'aelig': ch = String.fromCharCode(0x00e6); break;

                                    case 'ccedil': ch = String.fromCharCode(0x00e7); break;

                                    case 'egrave': ch = String.fromCharCode(0x00e8); break;

                                    case 'eacute': ch = String.fromCharCode(0x00e9); break;

                                    case 'ecirc': ch = String.fromCharCode(0x00ea); break;

                                    case 'euml': ch = String.fromCharCode(0x00eb); break;

                                    case 'igrave': ch = String.fromCharCode(0x00ec); break;

                                    case 'iacute': ch = String.fromCharCode(0x00ed); break;

                                    case 'icirc': ch = String.fromCharCode(0x00ee); break;

                                    case 'iuml': ch = String.fromCharCode(0x00ef); break;

                                    case 'eth': ch = String.fromCharCode(0x00f0); break;

                                    case 'ntilde': ch = String.fromCharCode(0x00f1); break;

                                    case 'ograve': ch = String.fromCharCode(0x00f2); break;

                                    case 'oacute': ch = String.fromCharCode(0x00f3); break;

                                    case 'ocirc': ch = String.fromCharCode(0x00f4); break;

                                    case 'otilde': ch = String.fromCharCode(0x00f5); break;

                                    case 'ouml': ch = String.fromCharCode(0x00f6); break;

                                    case 'divide': ch = String.fromCharCode(0x00f7); break;

                                    case 'oslash': ch = String.fromCharCode(0x00f8); break;

                                    case 'ugrave': ch = String.fromCharCode(0x00f9); break;

                                    case 'uacute': ch = String.fromCharCode(0x00fa); break;

                                    case 'ucirc': ch = String.fromCharCode(0x00fb); break;

                                    case 'uuml': ch = String.fromCharCode(0x00fc); break;

                                    case 'yacute': ch = String.fromCharCode(0x00fd); break;

                                    case 'thorn': ch = String.fromCharCode(0x00fe); break;

                                    case 'yuml': ch = String.fromCharCode(0x00ff); break;

                                    case 'OElig': ch = String.fromCharCode(0x0152); break;

                                    case 'oelig': ch = String.fromCharCode(0x0153); break;

                                    case 'Scaron': ch = String.fromCharCode(0x0160); break;

                                    case 'scaron': ch = String.fromCharCode(0x0161); break;

                                    case 'Yuml': ch = String.fromCharCode(0x0178); break;

                                    case 'fnof': ch = String.fromCharCode(0x0192); break;

                                    case 'circ': ch = String.fromCharCode(0x02c6); break;

                                    case 'tilde': ch = String.fromCharCode(0x02dc); break;

                                    case 'Alpha': ch = String.fromCharCode(0x0391); break;

                                    case 'Beta': ch = String.fromCharCode(0x0392); break;

                                    case 'Gamma': ch = String.fromCharCode(0x0393); break;

                                    case 'Delta': ch = String.fromCharCode(0x0394); break;

                                    case 'Epsilon': ch = String.fromCharCode(0x0395); break;

                                    case 'Zeta': ch = String.fromCharCode(0x0396); break;

                                    case 'Eta': ch = String.fromCharCode(0x0397); break;

                                    case 'Theta': ch = String.fromCharCode(0x0398); break;

                                    case 'Iota': ch = String.fromCharCode(0x0399); break;

                                    case 'Kappa': ch = String.fromCharCode(0x039a); break;

                                    case 'Lambda': ch = String.fromCharCode(0x039b); break;

                                    case 'Mu': ch = String.fromCharCode(0x039c); break;

                                    case 'Nu': ch = String.fromCharCode(0x039d); break;

                                    case 'Xi': ch = String.fromCharCode(0x039e); break;

                                    case 'Omicron': ch = String.fromCharCode(0x039f); break;

                                    case 'Pi': ch = String.fromCharCode(0x03a0); break;

                                    case ' Rho ': ch = String.fromCharCode(0x03a1); break;

                                    case 'Sigma': ch = String.fromCharCode(0x03a3); break;

                                    case 'Tau': ch = String.fromCharCode(0x03a4); break;

                                    case 'Upsilon': ch = String.fromCharCode(0x03a5); break;

                                    case 'Phi': ch = String.fromCharCode(0x03a6); break;

                                    case 'Chi': ch = String.fromCharCode(0x03a7); break;

                                    case 'Psi': ch = String.fromCharCode(0x03a8); break;

                                    case 'Omega': ch = String.fromCharCode(0x03a9); break;

                                    case 'alpha': ch = String.fromCharCode(0x03b1); break;

                                    case 'beta': ch = String.fromCharCode(0x03b2); break;

                                    case 'gamma': ch = String.fromCharCode(0x03b3); break;

                                    case 'delta': ch = String.fromCharCode(0x03b4); break;

                                    case 'epsilon': ch = String.fromCharCode(0x03b5); break;

                                    case 'zeta': ch = String.fromCharCode(0x03b6); break;

                                    case 'eta': ch = String.fromCharCode(0x03b7); break;

                                    case 'theta': ch = String.fromCharCode(0x03b8); break;

                                    case 'iota': ch = String.fromCharCode(0x03b9); break;

                                    case 'kappa': ch = String.fromCharCode(0x03ba); break;

                                    case 'lambda': ch = String.fromCharCode(0x03bb); break;

                                    case 'mu': ch = String.fromCharCode(0x03bc); break;

                                    case 'nu': ch = String.fromCharCode(0x03bd); break;

                                    case 'xi': ch = String.fromCharCode(0x03be); break;

                                    case 'omicron': ch = String.fromCharCode(0x03bf); break;

                                    case 'pi': ch = String.fromCharCode(0x03c0); break;

                                    case 'rho': ch = String.fromCharCode(0x03c1); break;

                                    case 'sigmaf': ch = String.fromCharCode(0x03c2); break;

                                    case 'sigma': ch = String.fromCharCode(0x03c3); break;

                                    case 'tau': ch = String.fromCharCode(0x03c4); break;

                                    case 'upsilon': ch = String.fromCharCode(0x03c5); break;

                                    case 'phi': ch = String.fromCharCode(0x03c6); break;

                                    case 'chi': ch = String.fromCharCode(0x03c7); break;

                                    case 'psi': ch = String.fromCharCode(0x03c8); break;

                                    case 'omega': ch = String.fromCharCode(0x03c9); break;

                                    case 'thetasym': ch = String.fromCharCode(0x03d1); break;

                                    case 'upsih': ch = String.fromCharCode(0x03d2); break;

                                    case 'piv': ch = String.fromCharCode(0x03d6); break;

                                    case 'ensp': ch = String.fromCharCode(0x2002); break;

                                    case 'emsp': ch = String.fromCharCode(0x2003); break;

                                    case 'thinsp': ch = String.fromCharCode(0x2009); break;

                                    case 'zwnj': ch = String.fromCharCode(0x200c); break;

                                    case 'zwj': ch = String.fromCharCode(0x200d); break;

                                    case 'lrm': ch = String.fromCharCode(0x200e); break;

                                    case 'rlm': ch = String.fromCharCode(0x200f); break;

                                    case 'ndash': ch = String.fromCharCode(0x2013); break;

                                    case 'mdash': ch = String.fromCharCode(0x2014); break;

                                    case 'lsquo': ch = String.fromCharCode(0x2018); break;

                                    case 'rsquo': ch = String.fromCharCode(0x2019); break;

                                    case 'sbquo': ch = String.fromCharCode(0x201a); break;

                                    case 'ldquo': ch = String.fromCharCode(0x201c); break;

                                    case 'rdquo': ch = String.fromCharCode(0x201d); break;

                                    case 'bdquo': ch = String.fromCharCode(0x201e); break;

                                    case 'dagger': ch = String.fromCharCode(0x2020); break;

                                    case 'Dagger': ch = String.fromCharCode(0x2021); break;

                                    case 'bull': ch = String.fromCharCode(0x2022); break;

                                    case 'hellip': ch = String.fromCharCode(0x2026); break;

                                    case 'permil': ch = String.fromCharCode(0x2030); break;

                                    case 'prime': ch = String.fromCharCode(0x2032); break;

                                    case 'Prime': ch = String.fromCharCode(0x2033); break;

                                    case 'lsaquo': ch = String.fromCharCode(0x2039); break;

                                    case 'rsaquo': ch = String.fromCharCode(0x203a); break;

                                    case 'oline': ch = String.fromCharCode(0x203e); break;

                                    case 'frasl': ch = String.fromCharCode(0x2044); break;

                                    case 'euro': ch = String.fromCharCode(0x20ac); break;

                                    case 'image': ch = String.fromCharCode(0x2111); break;

                                    case 'weierp': ch = String.fromCharCode(0x2118); break;

                                    case 'real': ch = String.fromCharCode(0x211c); break;

                                    case 'trade': ch = String.fromCharCode(0x2122); break;

                                    case 'alefsym': ch = String.fromCharCode(0x2135); break;

                                    case 'larr': ch = String.fromCharCode(0x2190); break;

                                    case 'uarr': ch = String.fromCharCode(0x2191); break;

                                    case 'rarr': ch = String.fromCharCode(0x2192); break;

                                    case 'darr': ch = String.fromCharCode(0x2193); break;

                                    case 'harr': ch = String.fromCharCode(0x2194); break;

                                    case 'crarr': ch = String.fromCharCode(0x21b5); break;

                                    case 'lArr': ch = String.fromCharCode(0x21d0); break;

                                    case 'uArr': ch = String.fromCharCode(0x21d1); break;

                                    case 'rArr': ch = String.fromCharCode(0x21d2); break;

                                    case 'dArr': ch = String.fromCharCode(0x21d3); break;

                                    case 'hArr': ch = String.fromCharCode(0x21d4); break;

                                    case 'forall': ch = String.fromCharCode(0x2200); break;

                                    case 'part': ch = String.fromCharCode(0x2202); break;

                                    case 'exist': ch = String.fromCharCode(0x2203); break;

                                    case 'empty': ch = String.fromCharCode(0x2205); break;

                                    case 'nabla': ch = String.fromCharCode(0x2207); break;

                                    case 'isin': ch = String.fromCharCode(0x2208); break;

                                    case 'notin': ch = String.fromCharCode(0x2209); break;

                                    case 'ni': ch = String.fromCharCode(0x220b); break;

                                    case 'prod': ch = String.fromCharCode(0x220f); break;

                                    case 'sum': ch = String.fromCharCode(0x2211); break;

                                    case 'minus': ch = String.fromCharCode(0x2212); break;

                                    case 'lowast': ch = String.fromCharCode(0x2217); break;

                                    case 'radic': ch = String.fromCharCode(0x221a); break;

                                    case 'prop': ch = String.fromCharCode(0x221d); break;

                                    case 'infin': ch = String.fromCharCode(0x221e); break;

                                    case 'ang': ch = String.fromCharCode(0x2220); break;

                                    case 'and': ch = String.fromCharCode(0x2227); break;

                                    case 'or': ch = String.fromCharCode(0x2228); break;

                                    case 'cap': ch = String.fromCharCode(0x2229); break;

                                    case 'cup': ch = String.fromCharCode(0x222a); break;

                                    case 'int': ch = String.fromCharCode(0x222b); break;

                                    case 'there4': ch = String.fromCharCode(0x2234); break;

                                    case 'sim': ch = String.fromCharCode(0x223c); break;

                                    case 'cong': ch = String.fromCharCode(0x2245); break;

                                    case 'asymp': ch = String.fromCharCode(0x2248); break;

                                    case 'ne': ch = String.fromCharCode(0x2260); break;

                                    case 'equiv': ch = String.fromCharCode(0x2261); break;

                                    case 'le': ch = String.fromCharCode(0x2264); break;

                                    case 'ge': ch = String.fromCharCode(0x2265); break;

                                    case 'sub': ch = String.fromCharCode(0x2282); break;

                                    case 'sup': ch = String.fromCharCode(0x2283); break;

                                    case 'nsub': ch = String.fromCharCode(0x2284); break;

                                    case 'sube': ch = String.fromCharCode(0x2286); break;

                                    case 'supe': ch = String.fromCharCode(0x2287); break;

                                    case 'oplus': ch = String.fromCharCode(0x2295); break;

                                    case 'otimes': ch = String.fromCharCode(0x2297); break;

                                    case 'perp': ch = String.fromCharCode(0x22a5); break;

                                    case 'sdot': ch = String.fromCharCode(0x22c5); break;

                                    case 'lceil': ch = String.fromCharCode(0x2308); break;

                                    case 'rceil': ch = String.fromCharCode(0x2309); break;

                                    case 'lfloor': ch = String.fromCharCode(0x230a); break;

                                    case 'rfloor': ch = String.fromCharCode(0x230b); break;

                                    case 'lang': ch = String.fromCharCode(0x2329); break;

                                    case 'rang': ch = String.fromCharCode(0x232a); break;

                                    case 'loz': ch = String.fromCharCode(0x25ca); break;

                                    case 'spades': ch = String.fromCharCode(0x2660); break;

                                    case 'clubs': ch = String.fromCharCode(0x2663); break;

                                    case 'hearts': ch = String.fromCharCode(0x2665); break;

                                    case 'diams': ch = String.fromCharCode(0x2666); break;

                                    default: ch = ''; break;

                              }

                        }

                        i = semicolonIndex;

                  }

            }

           

            out += ch;

      }

 

      return out;

     

}

Well that is about all I have to say on this subject.  Hope this is of some help to you.

Kevin Pirkl