Search code examples
javascripthtmlspecial-characterstapestry

Tapestry Grid with inPlace zone update fails with single quote in data for grid


I have following grid in a component.

<t:grid t:id="resultsGrid" id="resultsGrid" inPlace="true" source="results" row="result" rowIndex="rowIndex" rowsPerPage="50" pagerPosition="top" model="model">

InPlace=true renders grid inside a zone and then each new page request simply updates that zone. But in following case, zone update does not take place.

Let's say for page 3, the grid data(rows) contains single quote. Page 3 is rendered fine but after that if you click on any other page number then the zone is not updated. Browser does receive data from server for new page but the zone update does not take place.

This happens only when data/text of grid contains a single quote e.g. when there is "Adam's" in some row of grid. The zone is updated fine if grid data does not contain single quote.

On more debugging I found that the issue starts in following function of tapestry.js file.

In the process of updating zone, following function calls purgeChildren(element) function.

/**
     * Updates the zone's content, and invokes either the update function (to
     * highlight the change) or the show function (to reveal a hidden element).
     * Lastly, fires the Tapestry.ZONE_UPDATED_EVENT to let listeners know that
     * the zone was updated.
     * 
     * @param content
     */
    show : function(content) {

        Tapestry.purgeChildren(this.updateElement);

        this.updateElement.update(content);

        var func = this.element.visible() ? this.updateFunc : this.showFunc;

        func.call(this, this.element, this.endcolor);

        this.element.fire(Tapestry.ZONE_UPDATED_EVENT);
    },

Following is purgeChildren(element) function which calls purge(element) function.

/**
     * Invokes purge() on all the children of the element.
     */
    purgeChildren : function(element) {

        var children = element.childNodes;

        if (children) {
            var l = children.length, i, child;

            for (i = 0; i < l; i++) {
                var child = children[i];

                /* Just purge element nodes, not text, etc. */
                if (child.nodeType == 1)
                    Tapestry.purge(children[i]);
            }
        }
    }

And finally, following is purge(element) function which is where the zone update process breaks. In this function 'Unexpected identifier' exception is thrown when grid data contains single quote.

//this is where the problem is.
/**
     * Purges the element of any event handlers (necessary in IE to ensure that
     * memory leaks do not occur, and harmless in other browsers). The element
     * is purged, then any children of the element are purged.
     */
    purge : function(element) {

        // removes all functions/handlers attached to this element
        /* Adapted from http://javascript.crockford.com/memory/leak.html */
        var attrs = element.attributes;
        if (attrs) {
            var i, name;
            for (i = attrs.length - 1; i >= 0; i--) {
                if (attrs[i]) {
                    name = attrs[i].name;
                    /* Looking for onclick, etc. */
                    if (typeof element[name] == 'function') {
                        element[name] = null;
                    }
                }
            }
        }

        /* Get rid of any Prototype event handlers as well. */
        Event.stopObserving(element);

        Tapestry.purgeChildren(element);
    },

As mentioned in comment purge(element) function removes all handler functions of the element and is perhaps necessary in IE for memory leaks.

I could fix the issue by changing purge(element) function in following way. Comment out the code which removes handlers of the element. Upon making this change the zone is updated fine, even if the data contains single quote.

purge : function(element) {

    // removes all functions/handlers attached to this element
    /* Adapted from http://javascript.crockford.com/memory/leak.html */
//  var attrs = element.attributes;
//  if (attrs) {
//      var i, name;
//      for (i = attrs.length - 1; i >= 0; i--) {
//          if (attrs[i]) {
//              name = attrs[i].name;
//              // Looking for onclick, etc
//              if (typeof element[name] == 'function') {
//                  element[name] = null;
//              }
//          }
//      }
//  }

    /* Get rid of any Prototype event handlers as well. */
    Event.stopObserving(element);

//  Tapestry.purgeChildren(element);
},

But this is not a good fix. I have following questions, any help with them would be great.

1: Why does the code in purge function break with single quote in data while removing handlers of the passed element?

2: The data is fed to grid from a source class which extends GridDataSource interface. Thus, the grid data is generated by tapestry from that source class and hence handling of special characters is also supposed to be done by tapestry. Will it help if I add toString() method in data container class for grid?

3: Is there any other way of implementing purge(element) function so that it does not break with single quotes in data?

4: I am using tapestry version 5.2.4 , can this issue be fixed if I upgrade to latest version?

5: What can be potential problems in changing purge(element) function the way I have changed it to avoid the exception? How unsafe/safe that change is?


Solution

  • Had forgotten to post an answer.

    The issue was that each grid row's content contained an image. For that image, onClick handler was passed name of the page which was returned from server without escaping special characters. Hence the problem occurred while updating zone.

    Escaping the name of page before sending it to browser fixed the issue.