Troubleshooting

This section contains some common mistakes that developers make when building their own flakes.

Using the "id" element correctly

Markup elements fall into two categories: instance-specific elements and Page-Set specific elements. (This is somewhat analogous to class versus instance members, for those familiar with object orientation).

If a given flake exists many times in a Page-Set, an instance-specific element is created multiple times. The id attribute of such elements needs to be pre-pended with _PAGEFLAKES_, such that each element has a unique id attribute.

Page-Set specific elements are created once, regardless of how many times a flake exists on a Page-Set. The id attribute of such elements must be unique, such that it does not conflict with other Page-Set specific elements. For example, <script> elements containing FUA code, or <link> elements referencing style-sheets are Page-Set specific.

Using XMLHttpRequest

Most browsers, as a security precaution, allow the XMLHttpRequest object to only fetch data from the domain where the script itself was loaded. For flakes, this restriction effectively means that Flakes can only fetch data from *.pageflakes.com using XMLHttpRequest.

To work around this, Pageflakes provides the ContentProxy object.

Issues with Internet Explorer, Firefox and Opera

  1. Always use document.getElementById to get HTML elements. Internet Explorer supports accessing elements using the element ID directly, but Firefox and Opera do not. You can also use the shortcut function 'PF.$' instead of document.getElementById.
  2. There's no innerText in Firefox. So, use PF.T(element, text) to set text content inside HTML elements.
  3. CSS renders differently in Firefox, Opera and Internet Explorer, so make sure you test your flakes in all these browsers.

Use CDATA inside script blocks

Using < or > signs inside <script> tags is problematic, even if it is enclosed in double quotes. For example:

<script> body.innerHTML = "<a href='test.html'>Test</a>"; </script>

will fail because the < sign is invalid inside a script tag when it is parsed. So, use CDATA sections for such script blocks. For example:

<script><![CDATA[ body.innerHTML = "<a href='test.html'>Test</a>"; ]]></script>

If you see that your script blocks are not getting processed at all and the functions you are trying to access are completely unavailable, then this might be the reason.

This can also be a problem with for loops. If the script is not enclosed in CDATA, then the following example will fail:

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

We need some space before and after the "less than" (<) sign. Otherwise when we parse the flake file, "<10;" appears as a tag in the flake HTML file. Putting a space after the "<" characters prevents it from becoming a tag.

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

However, the preferred mechanism is to include the script in a CDATA element, and the whitespace is no longer an issue.

Avoid memory leaks and understand 'this'

Normally we know this refers to the current instance of the class. So, we write scripts like:

this.load = function()
{
 this.a = 10;
}

However, if the load function is called from an event or a timer, then this actually means the control from which the event is fired or the timer, not the instance of the class. The same applies to web service callbacks or any asynchronous JavaScript callbacks. In that case, this.a will no longer be a variable inside the class.

This code shows another problem:

function what_is_this()
{
  this.load = function(instance)
  {
    this.resultDIV = $('result');
    SomeWebService.DoSomething( 10, 20, 30, onComplete );
  }
  function onComplete(result)
  { 
    this.resultDIV.innerHTML = result; // FAILS
    this.done(); // FAILS
  }
  this.done = function()
  {
    alert("I never get called");
  }
}

This code will fail because this is no longer the instance of the class; instead it's some object from the Atlas framework which called the onComplete function.

One easy way to work around this problem is to save the value of this in a variable (self is commonly used):

function what_is_this()
{

var _resultDIV; var self = this; this.load = function(instance) { _resultDIV = $('result'); SomeWebService.DoSomething( 10, 20, 30, this.onComplete ); } this.onComplete = function(result) { _resultDIV.innerHTML = result; self.done(); } this.done = function() { alert("I do get called"); } }

While this works, it will leak memory because a closure is formed. Here’s how:

Once load() has been called and returned, onComplete cannot be removed because the global context has a reference to it (to call it back). In addition, the onComplete() function has a reference to the global context because it refers to the self variable, contained in the what_is_this object, which is created in the global context. Because of the circular reference, the memory is never freed, which quickly causes significant performance degradation.

To solve this issue, eliminate such closures:


function what_is_this()
{
  this._resultDIV = undefined; 
  this.load = function(instance) 
  { 
    this._resultDIV = $('result'); 
    this.SomeWebService.DoSomething( 10, 20, 30, PF.F(this, this.onComplete)); 
  }
  this.onComplete = function(result) 
  { 
    this._resultDIV.innerHTML = result;  
    this.done();  
  } 
  this.done = function()
  { 
    alert("I do get called"); 
  }
} 

In the example above, the self variable is eliminated, thus removing the closure. In addition, the PF.F function is used when setting the callback, to ensure that the value of this is correct when onComplete is invoked.

The code shown below improves the example even further by using prototypes. The advantage of prototypes is that if we have multiple instances of the what_is_this class created, the functions would be instantiated only once, resulting in performance savings.


function what_is_this()
{
  this._resultDIV =
  undefined; 
}
what_is_this.prototype.load = function(instance) 
{ 
  this._resultDIV = $('result'); 
  this.SomeWebService.DoSomething( 10, 20, 30, PF.F(this, this.onComplete)); 
} 
what_is_this.prototype.onComplete = function(result) 
{ 
  this._resultDIV.innerHTML = result;
  this.done();
} 
what_is_this.prototype.done = function()
{ 
  alert("I do get called"); 
}

Similarly, if an event handler needs to call one of the prototype functions, it must use the PF.F shortcut to ensure that the event handler is called with the correct value of this.

Minimize processing in load()

When a page is loaded, the load() function of all the embedded flakes must be called. This causes a large amount of operations, and thus to ensure that pages load in a timely fashion it is important to minimize the processing that occurs in the load() call.

More specifically it is recommended that:

  • The user interface of the flake is displayed as soon as possible in the load() call.
  • Access to the Profiles is kept to a minimum.

Do not use alert()

Javascript alerts are very intrusive and interfere with the the presentation of a Pageflakes page. As such, alerts should not be used. As an alternative, any messages should be displayed in a suitable <div> element.

Do not use <form> elements

Submitting a <form> using either POST or GET results in a refresh of the whole Pageflakes page. As this is inconsistent with the AJAX model that Pageflakes uses, any <form> elements are removed by the Pageflakes framework when the page is being loaded.

Developers should instead make usage of the Content Proxy object to communicate with server-side implementations.

Since the <form> tag is removed, it is not possible to use ASP.NET Controls such as DataGrid, Repeater, Calendar, Validators, etc. However, intrinsic HTML controls are allowed.