JavaScript functions to escape and unescape HTML

by timvasil 12/28/2008 9:10:00 PM

JavaScript doesn't have built-in functions to escape or unescape HTML, which may come in handy for certain DOM manipulations or AJAX scripting.  Here are the custom functions I wrote to handle these tasks.  I've tested the implementation with both IE and Firefox.

var g_oHtmlEncodeElement;

function htmlEscape(text)
{
    g_oHtmlEncodeElement = g_oHtmlEncodeElement || document.createElement("div");
    g_oHtmlEncodeElement.innerText = g_oHtmlEncodeElement.textContent = text;
    return g_oHtmlEncodeElement.innerHTML;
}

function htmlUnescape(html)
{
    g_oHtmlEncodeElement = g_oHtmlEncodeElement || document.createElement("div");
    g_oHtmlEncodeElement.textContent = g_oHtmlEncodeElement.innerText = "";
    g_oHtmlEncodeElement.innerHTML = html;
    return g_oHtmlEncodeElement.innerText || g_oHtmlEncodeElement.textContent;
}

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:

JavaScript | AJAX | IE | Firefox

Form.DefaultButton in Firefox

by timvasil 6/4/2008 11:53:00 AM

Setting the DefaultButton property on a form to an ImageButton, LinkButton, or Button with UseSubmitBehavior=false doesn't work on Firefox because the ASP.NET code hooking the Enter key on form submit looks for a "click" method on the button that Firefox does not support.

If you give your button a name attribute, say name='DefaultButton' for example, then throw the following JavaScript in a file that's included on all pages, it'll work.  The trick is to define the click method on Firefox's behalf.

setTimeout(function() { init(); }, 0);

// Fixup link buttons for FF
function init()
{
    if (document.getElementsByName)
    {
        var aoElements = document.getElementsByName("DefaultButton");
        if (aoElements)
        {
            for (var i = 0; i < aoElements.length; i++)
            {
                if (aoElements[i].click == undefined)
                {
                    aoElements[i].click = aoElements[i].onclick;
                }
            }
        }
    }
}


Update on 6/9/08:

That fix apparently doesn't work completely, as when you hit <Enter> in a textarea the form is submitted.  That's a critical flaw.  I didn't want to wholesale replace ASP.NET's WebForm_FireDefaultButton method, but, c'mon, there's only so much tolerance anyone can have for hunting down and working around other people's bugs. 

So, here's a tweaked version of the function that fixes default button handling and works across browsers.  In order to use this patched function, you need to define it *after* all other JavaScript files and also after <ajaxToolkit:ToolkitScriptManager /> if you happen to be using it.

var __defaultFired;
function WebForm_FireDefaultButton(event, target) {
    var element = event.target || event.srcElement;
    if (!__defaultFired && event.keyCode == 13 && !(element && (element.tagName.toLowerCase() == "textarea"))) {
        var defaultButton;
        if (__nonMSDOMBrowser)
            defaultButton = document.getElementById(target);
        else
            defaultButton = document.all[target];

        if (typeof(defaultButton.click) != "undefined") {
            __defaultFired = true;
            defaultButton.click();
            event.cancelBubble = true;
           
            if (event.stopPropagation) event.stopPropagation();
            return false;
        }
        if (typeof(defaultButton.onclick) != "undefined") {
            __defaultFired = true;
            defaultButton.onclick();
            event.cancelBubble = true;
           
            if (event.stopPropagation) event.stopPropagation();
            return false;
        }

    }
    return true;
}

 

Currently rated 5.0 by 1 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:

ASP.NET | JavaScript | Firefox

Fix for IE's 'Operation Aborted' error

by timvasil 5/28/2008 11:17:00 PM

If you've done any dynamic manipulation of web pages, especially with AJAX or any other kind of web magic in the mix, you've run into IE's dreaded "Operation Aborted" error.

Here are some official comments on the issue:

The blog post says that if you aren't meeting all 3 conditions, you're OK.  In my case, I believe I wasn't meeting the 3 conditions, and yet the problem persisted.  It was very hard to reproduce, too.  I had to have the offending website in the "Internet Zone" and experience some network lag to make it happen.

In the end, I found that if I replaced this line:

   document.body.appendChild(element);

With this one:

   document.body.insertAdjacentElement('afterBegin', element);

Everything worked fine.  Of course, this trick works only if you're inserting a node whose position isn't important, e.g. it's a div you're going to give an absolute position anyway.

Along the way I had tried wrapping the appendChild calls in a try/catch block, but unfortunatley the append would work fine, and then IE would complain with "Operation Aborted" only after the JavaScript finished.  I was hoping for a global exception handler to trap the problem, but fortunately insertAdjacentElement fixes the issue!

Currently rated 4.0 by 1 people

  • Currently 4/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:

JavaScript | IE

Debugging dynamically evaulated JavaScript

by timvasil 4/22/2008 2:10:00 AM

When using AJAX, an easy way to evaluate JavaScript or JSON returned from a web server is the good ol' eval function.  However, if an error is encountered during the evaluation Microsoft's Script Debugger doesn't let you see the offending line.  (I haven't checked what FireBug does yet.)

To get around this problem, you can insert the JavaScript as a <script> DOM node. 

In other words, instead of this:

    eval(js);

try this:

    var scriptTag = document.createElement("script");
    scriptTag.setAttribute('type', 'text/javascript');
    scriptTag.text = js;
    document.body.appendChild(scriptTag);

With the latter approach, the debugger works great.

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:

JavaScript | AJAX

Calendar utility class for manipulating dates in GWT or JavaScript

by timvasil 3/23/2008 11:56:00 PM

GWT doesn't yet emulate Java's Calendar class, so you're stuck writing your own code if you need to perform date manipulations, like rounding a date to the nearest month or truncating the time portion.  There are some gotchas here, like handling daylight savings time and leap years correctly, so the naive implementations of adding/subtracting milliseconds directly won't always work as expected.

Here's a class I wrote to help perform the date manipulations I need.  If you're using JavaScript directly instead of GWT, the port is straightforward. 

import java.util.Date;

/**
 * Built-in date range provider for various predefined date categories.
 * <p/>
 * Justification for use of deprecated methods on {@link Date}:
 * <br />
 * This class uses deprecated methods of the Date class since it must be able to run on the client side
 * and GWT hasn't emulated the {@link Calendar} class.
 */
public class CalendarUtils
{
    private static final long MS_PER_SEC = 1000;
    private static final long MS_PER_MIN = MS_PER_SEC * 60;
    private static final long MS_PER_HOUR = MS_PER_MIN * 60;
    private static final long MS_PER_DAY = MS_PER_HOUR * 24;
   
    private CalendarUtils()
    {
    }
   
    public static Date truncateToTime(Date date)
    {
        long time = date.getTime();
        return new Date(time - truncateToDay(date).getTime());
    }
   
    public static Date truncateToDay(Date date)
    {
        return new Date(date.getYear(), date.getMonth(), date.getDate());
    }
   
    public static Date truncateToMonth(Date date)
    {
        return addDays(truncateToDay(date), 1 - date.getDate());
    }
   
    public static Date truncateToYear(Date date)
    {
        date = truncateToMonth(date);
        date.setMonth(0);
        return date;
    }
   
    public static Date addDays(Date date, int days)
    {
        Date newDate = new Date(date.getTime());
        newDate.setDate(date.getDate() + days);
        return newDate;
    }
   
    public static Date addMonths(Date date, int months)
    {
        for (; months < 0; months++)
        {
            Date roundedPriorMonth = addDays(truncateToMonth(date), -1);
            date = addDays(date, -getDaysInMonth(roundedPriorMonth));
        }
        for (; months > 0; months--)
        {
            date = addDays(date, getDaysInMonth(date));
        }
        return date;
    }
   
    public static int getDaysInMonth(Date date)
    {
        return getDaysInMonth(date.getYear() + 1900, date.getMonth());
    }
   
    public static int getDaysInMonth(int year, int month)
    {
        switch (month)
        {
            case 1:
                return (((year % 4) == 0 && (year % 100) != 0) || (year % 400) == 0) ? 29 : 28;
   
            case 3:
            case 5:
            case 8:
            case 10:
                return 30;
               
            default:
                return 31;
        }
    }

    /**
     * Determines the number of days between two dates, always rounding up so a difference of 1 day 1 second
     * yield a return value of 2.
     */
    public static int dayDiff(Date endDate, Date startDate)
    {
         return (int)Math.ceil(((double)endDate.getTime() - startDate.getTime()) / MS_PER_DAY);
    }
}

 

 

Currently rated 4.2 by 5 people

  • Currently 4.2/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:

JavaScript | GWT

Updating Fusion Chart Data via setDataXML in IE

by timvasil 1/3/2008 2:42:00 AM

FusionCharts is using Adobe's ExternalInterface API to communicate between JavaScript and the SWF chart when you use FusionCharts.js to do things like setDataXML. For some reason, ExternalInterface isn't working correctly on IE under some conditions; the method simply doesn't exist on the OBJECT element.  I happen to be using the ExtJS library with GWT; either framework may be doing some DOM manipulations under the covers that are freaking out Adobe--but I haven't verified this.

A couple things to try:

  1. Ensure that the ID and name attributes of the OBJECT and/or EMBED tags do not have characters such as . (period), -, +, *, /, and \.  Amusingly, this is because Adobe's ExternalInterface API is using a bunch of eval() JavaScript methods and these special characters get interpreted as JavaScript operators.  Yeah, great work, Adobe.
     
  2. Hook up the ExternalInterface functions you need manually.  For example, here's the modified setDataXML function in FusionCharts.js to do the trick (code in red has been added):

setDataXML: function(strDataXML){
    //If being set initially
    if (this.initialDataSet==false){
        //This method sets the data XML for the chart INITIALLY.
        this.addVariable('dataXML',strDataXML);
        //Update flag
        this.initialDataSet = true;
    }else{
        //Else, we update the chart data using External Interface
        //Get reference to chart object
        var chartObj = infosoftglobal.FusionChartsUtil.getChartObject(this.getAttribute('id'));
        if (!chartObj.setDataXML)
        {
            __flash__addCallback(chartObj, "setDataXML");
        }
        chartObj.setDataXML(strDataXML);
    }
},

Currently rated 4.7 by 3 people

  • Currently 4.666667/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:

JavaScript | FusionCharts

Integrating Applications with RoboHelp for the web

by timvasil 12/21/2007 7:59:00 PM

Documentation on integrating with RoboHelp is sparse.  Here are some things I discovered by reading the JavaScript source (which looks like Win32 UI code out of the 1990s):

To link directly to a topic for context-sensitive help:

index.html#page.name.htm

To show the Index pane:

index.html#>>pdb=ndx

To show the Search pane:

index.html#>>pdb=nls

To show the Glossary pane:

index.html#>>pdb=gls

With these links, you can create a Help menu in your application with links to all of the appropriate pages:

If you have the patience, you can tweak the RoboHelp source to provide some pretty cool value-adds.  For example, I was able to add keyword search highlighting:

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:

JavaScript | RoboHelp

Spot the bug: JavaScript positioning

by timvasil 11/17/2007 6:20:00 PM

Can you spot the bug in the following function--designed to get the absolute position of an element on a webpage?

function getLocation(obj)
{
 var curleft = curtop = 0;
 for (; obj; obj = obj.offsetParent)
 {
  curleft += obj.offsetLeft;
  curtop += obj.offsetTop;
 }
 return [curleft, curtop];
}

Here's a hint:  under normal operation it works fine. 

Here's another hint:  it involves IE "expressions."

So, what's the problem?  It comes down to this line of code:

 var curleft = curtop = 0;

Here curleft is defined as a local variable, but curtop is not!  curtop is actually synonymous with this.curtop, so it's a property of this, not a local variable.  Imagine what would happen if getLocation() were called recursively; the nested call would essentially change the value of curtop before returning, the outer function would return a bogus "top" value.  But there's no recursive call so there's no problem, right?  In my case, wrong!  Apparently as you access offsetLeft and offsetTop properties, IE may decide to evaluate expressions.  And if you've so happened to define an expression that calls getLocation(), you're suddenly making a non-obvious recursive call.  For example:  <div style='top:expression:getLocation(document.all.element)'></div> will trigger the bug.

The fix is to declare the variables this way instead:

 var curleft = 0; 
 var curtop = 0;

 

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:

JavaScript

onmousemove continually fires, indefinitely

by timvasil 11/12/2007 12:14:00 AM

I recently decided to hook document.onmousemove and document.onkeypress for idle detection on a web page.  Kind of the right idea, but surprising results. 

Did you know that, at least with IE 7, the onmousemove event continues to fire a couple times per second even when the mouse is not moving?  and even when the browser window doesn't have the focus, so long as the mouse is over the document?  Bizarre. 

There's an easy enough way around this issue:

document.onmousemove = observeMoveActivity;
var g_iOldX;
var g_iOldY;
function observeMoveActivity()
{
    if (event && (event.screenX != g_iOldX || event.screenY != g_iOldY))
    {
        g_iOldX = event.screenX;
        g_iOldY = event.screenY;
        observeActivity();  // do actual work here
    }
}

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:

JavaScript

 

About the author

Tim Vasil Tim Vasil
I'm a software engineer living in Cambridge, MA.

E-mail me Send mail

Search

Calendar

<<  September 2010  >>
MoTuWeThFrSaSu
303112345
6789101112
13141516171819
20212223242526
27282930123
45678910

View posts in large calendar

Recent comments