Tuesday, August 30, 2005

Javascript function overriding, Part 4

In this final part of the Javascript function override series, I will give you a coding scenario when the 2 method of function overriding will make a difference to your code and a scenario in which I have use method 2 to extend feature functionality.

If you want to refer back to my previous post, follow these links :
Part 1
Part 2
Part 3

The answer to the first question is very obvious. From the 2 example I have given, if your new override function need to call the orginal function, then you have to use method 2. Otherwise you will end up with a recursive function call if you use method 1.

Here come the second part which is more interesting. Recently I use the second method to extend the functionality and overcome some limitation in Windows Sharepoint Services (WSS).

I am not going to go in great length what is WSS. You can find out more on this product from the Microsoft Sharepoint website. In a glance, WSS provide a website for team and departmental collaboration. In each WSS site, you can create picture library, document library, link list to other website, survey poll and custom list to store some custom information and many more.

For the sake of this discussion, I will only focus on custom list. But the method I describe here is equally
applicable to other lists as well.

In the list view, when you hover over each list item, a context menu will be popup. The context menu consist of menu items which offer you actions that you can perform on the item. Behind the scene, there is a series of Javascript function calls which make all these happen. At one point in the call chain, the client side script framework will call a function named AddListMenuItems(m, ctx) (you can find this function is ows.js). This function is responsible to construct the menu item that is relevant to the list item.

If you look into this function, one of the first thing it does is call another function named Custom_AddListMenuItems(m, ctx). According to the Microsoft documentation, if you want to add your own menu item to the context menu, you should create the function Custom_AddListMenuItems(m, ctx) and add your own code to create the extra menu items in this function. This provide some form of extensibility point.

So far so good, except for 2 thing :
1) Your custom menu items always appear before the default menu items provided by WSS.
2) You always have to work on the Custom_AddListMenuItems(m, ctx) function to add your own menu items. Imagine a situation where multiple developers each is dealing with a different set of requirements and they each need to add their own custom menu item. Then it become difficult and messy to maintain the function because it is work by multiple developers and the single function might contain many complex logic to construct the menu item.

Using the javascript capability I demonstrated in method 2 of my example, I come out with a way to insert menu item at the back of the default menu items and allow the creation of each menu item to be encapsulate into different functions.

The idea and steps is pretty simple:
1) I create an array which is use to store the custom function.
2) When the page is loaded, I add the original AddListMenuItems() to the array.
3) Each time a new menu item need to be added, the menu item creation code can be put into a different function, and this function is added into the array.
4) I override the AddListMenuItems function to do something different. My version of the function will loop through the array and call each function in the array to contruct the menu item they are responsible for.


Here are the javascript code that does the logic:
<script language="javascript">

var AddListMenuItemsFuncList;
AddListMenuItemsFuncList = new Array();

AddListMenuItemsFuncList[AddListMenuItemsFuncList.length] = AddListMenuItems;

AddListMenuItems = function(m, ctx)
{
for(ctr=0; ctr < AddListMenuItemsFuncList.length; ctr++)
{
AddListMenuItemsFuncList[ctr](m, ctx);
}
}


function MyMenuItemFunction1(m, ctx)
{

var strDisplayText = "Custom Action 1";
var strAction;
var strImagePath = "";

strAction = "alert('Custom Action 1 " + itemTable.ItemId + "')";

// Add menu item
CAMOpt(m, strDisplayText, strAction, strImagePath);

// add a separator to the menu
CAMSep(m);
}

function MyMenuItemFunction2(m, ctx)
{

var strDisplayText = "Custom Action 2";
var strAction;
var strImagePath = "";

strAction = "alert('Custom Action 2 " + itemTable.ItemId + "')";

// Add menu item
CAMOpt(m, strDisplayText, strAction, strImagePath);

// add a separator to the menu
CAMSep(m);
}


function insertFunctionAt(arr, idx, newFunc)
{
for(ctr=arr.length-1; ctr >=idx; ctr--)
{
arr[ctr+1] = arr[ctr];
}

arr[idx] = newFunc;
return arr;
}

function addFunctionToMenu(funcPointer)
{
AddListMenuItemsFuncList[AddListMenuItemsFuncList.length] = funcPointer;
}

// Add this custom menu item to the back of the default menu items.
addFunctionToMenu(MyMenuItemFunction1);


// Add this custom menu item before the default menu items.
insertFunctionAt(AddListMenuItemsFuncList, 0, MyMenuItemFunction2);

</script>


To see this code in action, add a Content Editor Web Part(CEWP) to your custom list's view page. You can show the Web Part Tool Pane by adding ?ToolPaneView=2 to the end of the URL(This is an unsupported method). Then open the Source Editor, paste the above code into the Source Editor and click OK to save. Now refresh the view page. If you hover over the list item, you will see the new custom menu item in the context menu.

One Last Word


Although the function override capability in Javascript is powerful, it work like a double sided sword. If you don't use it with careful thought, you can be killed for the same reason.

Using it to extend the custom menu items in WSS provide you with greater flexibility, but we are also making an assumption on the behavior of the AddListMenuItems function. Microsoft does not guarantee that this function will not change in future release or Service Pack. If it does, then your code might just break.

To put it in simple words, I think this technique work fine, except that it is an unsupported method, and use it with care.

Labels:

Sunday, August 28, 2005

Podcast Link

.NET
.NET Rocks

SQL Server
SQL Down Under Portal

ASP.NET
Wallace B. McClure

Office
Office Zealot

Agile Development
Team Agile

Javascript function overriding, Part 3

In this installment, I will give you the code of how to prove what I have said.

To prove my point for method 1, create the following HTML page :

<html>
<body>

<script language="javascript">
var base_foo;

function foo()
{
alert('foo is called');
}

base_foo = foo;

function foo()
{
base_foo();
alert('override foo is called');
}

function clickme()
{
foo();
}
</script>

<input type="button" value="click me" onclick="clickme()"/>
</body>
</html>


If you click on the button, clickme() will be called which in turn call foo(), you will end up with a stack overflow exception.

This is because base_foo and foo point to the same memory address, when foo() call base_foo(), it is essentially a recursive function call.


To prove my point for method 2, create the following HTML page :

<html>
<body>

<script language="javascript">
var base_foo;

function foo(yourname)
{
alert('Hello ' + yourname);
}

base_foo = foo;

foo = function(yourname)
{
base_foo(yourname);
alert('(overide) Hello ' + yourname);
}


function clickme()
{
foo('Jonathan');
}
</script>

<input type="button" value="click me" onclick="clickme()"/>
</body>
</html>


If you click on the button, clickme() will be called which in turn will call foo(), what you will see now is 'Hello Jonathan' will be display, and then follow by '(override) Hello Jonathan'.

This show that base_foo and foo point to different memory address.

In the next final part of this series, I will give you a scenario when the 2 method will make a difference and a case study where I have use method 2 to override function.

Labels:

Friday, August 26, 2005

Javascript function overriding, Part 2

Continue from the part 1, I will give a more detail explaination to what I say in Part 1.

Using the same example :
Method 1
<script language="javascript">
var base_foo;

function foo()
{
alert('foo is called');
}

base_foo = foo;

function foo()
{
alert('override foo is called');
}
</script>

The first time you create function foo(), your code will be loaded into a arbitary memory location, say for example 0x001a.

Your function pointer foo will be set to point to address 0x001a.

Next you assign base_foo to point to the same memory address pointed to by foo

Now, your override function foo() to do different thing. The script engine will load your new code into the same memory location (0x001a).

And both your function pointer foo and base_foo will remain point to the same address.


Moving to method 2:
Method 2
<script language="javascript">
var base_foo;

function foo(yourname)
{
alert('Hello ' + yourname);
}

base_foo = foo;

foo = function(yourname)
{
alert('(overide) Hello ' + yourname);
}
</script>


The first time you create function foo(), your code will be loaded into a arbitary memory location, say for example 0x001a.

Your function pointer foo will be set to point to address 0x001a.

Next you assign base_foo to point to the same memory address pointed to by foo.

Now you override function foo() to do different thing:

foo = function(yourname)
{
// your new code
}

What happen now is your new code is loaded into a new memory location, say (0x002a).

Your function pointer foo will now be changed to point to address 0x002a. But base_foo remain point to 0x001a.

In the next part, I will give an example of how to prove this.

Labels:

Wednesday, August 24, 2005

Javascript function overriding, Part 1

Disclaimer : All the javascript code, explaination and description in this blog series is particular to Internet Explorer only.

In Javascript, you can overide a function using the following method :
Method 1
<script language="javascript">
function foo()
{
alert('foo is called');
}

function foo()
{
alert('override foo is called');
}
</script>

If you call foo(), it will display 'override foo is called'.

Recently, I discover another way of overriding function :
Method 2
<script language="javascript">
function foo(yourname)
{
alert('Hello ' + yourname);
}

foo = function(yourname)
{
alert('(overide) Hello ' + yourname);
}
</script>

Both method will accomplish the same thing. However, they are semantically different.

Using method 1, what happen is the new code will over-write the existing code at the same memory address.

In method 2, the new code will be loaded into a different memory address and the function pointer foo will be changed to point to this new memory address.

What does this mean ? I will give an example and show you how to prove what I say in my next post.

Labels:

Wednesday, August 03, 2005

Shortcut to Lock Your Workstation

Here is how you can quickly lock your workstation without doing the CTRL+ALT+DEL thing :

create a shortcut to rundll32.exe user32.dll,LockWorkStation.
Then, launch the shortcut to lock the workstation.