Thursday, February 10, 2011

JavaScript Meta blog

JavaScript is a critical component of PeopleSoft applications. With the web browser pretty much taking over as the rendering engine for enterprise applications, I see JavaScript as a critical language for computer professionals. Of course, technologies like PeopleTools and ADF contain abstraction layers so application developers do not have to write JavaScript, but, in the end, someone has to write the JavaScript generated by those abstraction layers. And, if something goes wrong, odds are very good you will have to dig through the generated JavaScript to see what went wrong (where, when, why, etc).

Anyway, I think JavaScript is one of the most important modern languages a programmer can learn, and I know I'm not alone in this opinion. Of late, I've found some very interesting online resources for people interested in learning JavaScript, so I wrote this post to pass those resources along to PeopleSoft developers.

I will maintain this as a "Meta-blog" post and update it as I find more resources. I will attempt to keep the list short, so you don't have to sift through thousands of irrelevant tutorials. Restated: this is not a complete list. It is just a short list of tutorials that I think stand above the rest.

38 comments:

Dan Kibler said...

Hi Jim

I'd be honored if you would include links to my JavaScript-related posts.

http://danielkibler.blogspot.com/2010/10/using-jquery-in-peoplesoft-introduction.html

http://danielkibler.blogspot.com/2010/11/using-jquery-in-peoplesoft-validating.html

http://danielkibler.blogspot.com/2010/11/using-javascript-in-peoplesoft-proxy.html

I have a new one drafted I hope to post soon.

Regards
Dan

Dan Kibler said...

I just added a new JavaScript-related post to my blog.

Using JavaScript in PeopleSoft: Creating your own dialog boxes

Dan

Manoj said...

Hi Jim,

I have been an ardent follower of your Journal/book. You have been my inspiration as far JS in PeopleSoft goes.

So i have been experimenting with JavaScript, jQuery & PeopleSoft. I thought of spicing up my PeopleSoft system by replacing static navigation options with some CSS based navigations.

But for the actual work to be done, I still used the PeopleCode. For instance, I defined my custom hyperlink as say: " %SubmitScriptName(document.%FormName,'UN_HRS_APPL_WRK_ADD_PB');".

So I can spice it up as if it was a custom link and still get the PeopleCode to fire.

But i've into problems. As soon as the PeopleCode fires, it clears all my injected JavaScript.

Say i have my script in a HTMLAREA. Before the code fires through my'%SubmitScript' the script is available in my Pagesource, but it disappears as soon as the code fires.

Do you happen to know why this would happen?

Paul said...

Hi Jim,
I've run into an interesting situation while implementing the custom UI scripts ideas in your book. It appears that the javascript gets loaded twice on delivered pages but only once on custom pages. I discovered this by trying to find a way to reliably get the page name across all (or the majority of) pages. As an example: if I add a new script (say, alert("test")) and attach it to any delivered menu/component i will get two alerts. If I attach it to a custom menu/component I only get one alert.

I noticed the help link in the pagebar uses the page name as the context id parameter. I was attempting to strip the page name from this URL. I still haven't tracked down why, yet, but the URL is populated on the first loading of the javascript but blank on the second loading of the javascript (which prevents me from using the help link to get the page name).

Have you seen this before? If so, do you know why it happens? (Also, I'd be happy to explain what we're using your fantastic utility for if you're interested.)

thanks for your time!
Paul

Jim Marion said...

@Manoj, I think I would have to see it to know what to do about it. I don't think I've attempted to do what you are trying to do.

Jim Marion said...

@Paul, that is very interesting. Whether the component is custom or delivered, it should behave the same. Do your custom components use the same CREF template? That is the only thing I can think of -- if your code is firing for both the header frame and for the TargetContent frame.

To find the menu, component, or page name, I use the strCurrUrl JavaScript variable.

Paul said...

Jim, Have you had a chance to test your javascript from the book on peopletools greater then 8.51.05? We're on 8.51.07 and it looks like oracle has locked down some of what you're allowed to do. Document.write is no longer allowed, for instance and appending scripts to the head is also working differently. They've even stopped you from inserting closing script tags in html definitions.

Jim Marion said...

@Paul, yes I have had a chance to implement some of the code on PeopleTools 8.50, and now 8.51. The key difference is $(document).ready. It doesn't work the same because PeopleTools no longer reloads the page. Instead, I use a technique called "Monkey patching" or "Duck punching." What I do is override the net.contentLoader (or something like that, can't remember) in a closure so I can do additional processing. I think my book's JavaScript for changing the search operator has some of this code in it.

citizen6in said...

@jim: we have a requirement where in we dont want the 'Add an Attachment's pop up on the main page. The pop up contains Textbox, browse, upload and cancel button. when user clicks the 'Add an Attachment' button, user shuld be shown the File browse dialog, if a file is selected then the file should be uploaded. Our customisation for tools less than 8.50 did not work in 8.50, as we have pop up. Is there a way to do this. atleast with jQuery.?

Jim Marion said...

@citizen6in, I have not tried that before. I see your point though. Why show that prompt? Why not just open the file dialog? Since I haven't looked into the JavaScript for the Add Attachment button, I can't give you much more than just a place to start. Add Attachment buttons usually execute FieldChange PeopleCode. In 8.50 this will cause an Ajax request/response. I suggest you use Fiddler/Firebug to inspect the Ajax request and response. In there you should see the JavaScript required to display that dialog. Once you discover the JavaScript behind the buttons, you can start Monkey Patching the methods/functions so that they behave the way you require.

Marc said...

I have used jQuery to manipulate grids so that the "Click column heading to sort ascending" title is changed to "Click column heading to sort ascending by fieldname". This was to help with some 508 Compliance issues with PeopleSoft.

/* Set the hover text for the column headings */
$('a.PSLEVEL1GRIDCOLUMNHDR').each(function (index) {
var hdgObj = $(this);
var hdgTitle = hdgObj.attr('title');
var hdgText = hdgObj.text();
if (hdgTitle.indexOf("by") !== -1) {
}
else
{
var newTitle = hdgTitle + " by " + hdgText;
hdgObj.attr('title', newTitle);
}
})

Anwway, I need to do the same for the search pages and I have yet to figure out where the code is for building the search results so that I can manipulate things. I have noticed that my code does not get fired when the search button is pressed, so I am assuming that I cannot use the $(document).ready(function (), since PeopleSoft is no longer reloading the page. Any suggestions that you can provide?

Regards,
Marc

Jim Marion said...

@Marc, First, use Firebug to see what scripts are loaded into the search page (PT_PAGESCRIPT, etc). These are HTML Definitions in App Designer. You can use one of them as your delivery vehicle. Second, $(document).ready doesn't fire on PT 8.50 and greater after search button click because the request/response is all Ajax (you already figured this out). In my book (chapter 7) I have an example of Monkeypatching the search page's net.ContentLoader, which is the Ajax service for PT 8.50. My example shows how execute a function after the advanced search button click returns a response. Your scenario would be very similar.

If you use something like PT_PAGESCRIPT as your delivery mechanism, then you need to have a way to determine if you are on a search page. Here is what I use:

if($("#\\#ICSearch").length > 0) {
// search page
} else {
// do nothing
}

krunal mehta said...

Hi, recently we have upgraded our PeopleTools from 8.49.23 to 8.52.09.



After this we are facing the issue like the processing image is keep on running. For ex: If I navigate to Structure and Content --> Portal Objects --> Home Page-->Tabs -->Home



In that if I am clicking any tab it will load the page but the processing image keeps running. I see an error in the status bar like below



Webpage error details



User Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; .NET CLR 2.0.50727; .NET CLR 1.1.4322; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)

Timestamp: Thu, 11 Oct 2012 05:19:41 UTC

Message: Permission denied

Line: 1602

Char: 5

Code: 0

URI: http://askhrtest.hk.standardchartered.com:8522/cs/DGEPUPG1/cache/PT_PAGESCRIPT_win0_291.js

Message: Permission denied

Line: 1602

Char: 5

Code: 0

URI: http://askhrtest.hk.standardchartered.com:8522/cs/DGEPUPG1/cache/PT_PAGESCRIPT_win0_291.js.



Sometimes it is showing permission denied error on PT_COMMON script.



We are not facing this issue when we do the same thing in Chrome Version 22.0.1229.79. But our organization standard browser is Internet Explorer .8.0.6001.18702



Please help us to find the issue.
Thanks for your time.

Jim Marion said...

@krunal, did you set the Auth Token Domain in your web profile?

You might get better results if you post this question in the PeopleSoft OTN Forum.

krunal mehta said...

Auth token is set up at the web server level and also at the web profile. Not sure how do we need to analyze/fix this issue. Please guide us to resolve the same

Jim Marion said...

@Krunal, the brightest PeopleSoft minds in the world monitor the OTN and ITToolbox forums. Here you will only get my assistance. Post your question in either the PeopleSoft OTN general forum or the ITToolbox PeopleTools forums and I'm sure someone that has experienced that issue will respond.

Hari said...

Dear Jim,

We have finally found the below error is not due to the permission issue

Message: Permission denied

Line: 1602

Char: 5

Code: 0

URI: http://askhrtest.hk.standardchartered.com:8522/cs/DGEPUPG1/cache/PT_PAGESCRIPT_win0_291.js

It looks like there is some problem with the Java Script execution. While loading the page JS getting error and stops further processing due to this we are getting the processing image as its waiting for the execution to complete.

When I try to debug the JS error it stops at the below line (which is given as bold text)

function doModalOnLoad_win0(bStartUp) {
mTop = MTop(); // top window
if (!mTop || typeof mTop.oParentWin == 'undefined' || !mTop.oParentWin) return;
Not sure how this Mtop() is working and what it is expecting to complete the execution? Could you please help us?

Thanks a lot for your time.



Jim Marion said...

@Hari, When your web browser throws a permission denied error, it means the JavaScript attempted to perform an operation that is considered insecure. Usually this is caused by trying to access a frame from a different domain. Make sure the top level page and your transaction page have the same "document.domain" value. You can check this from the JavaScript console in any browser. Also take each fragment of the if statement and execute it in the JavaScript console to see which part is throwing the access violation. Don't be surprised if the error is really somewhere else in the JavaScript.

Another thing you can test is adding your PeopleSoft application domain to your list of trusted sites.

Peter Nguyen said...

Jim,

Love your Blog! I'm trying to bind a JQUERY click event to a PeopleTools Button/HyperLink field (TYPE: Hyperlink, Destination: PeopleCode Command), so that when the user clicks the hyperlink a loader/spinner icon appears and makes an ajax call to an iscript,
but whenever I click the hyperlink it hits PT_PAGESCRIPT:submitAction and it strips the field element of all the attributes I set using my jquery callback function and returns it to the state it was in before I clicked it. I can get this to work using an html area or any other field for that matter, but just wondered if there was a work around for the PeopleTool button.

Jim Marion said...

@Peter, thank you for the compliments.

When creating buttons that look like PeopleSoft buttons, but behave differently, I usually use an HTML Area and copy the HTML from a PeopleSoft button into the HTML Area. I change the ID, name, and onclick handler.

If you want to change the way a delivered button behaves, you will need to reset the onclick attribute $element.prop("onclick", null). For hyperlinks, you can have your $.click event handler return false.

Pawan M said...

Hi Jim,

I have posted a query related to the browser permission denied error related to the Mtop function in oracle forums:

https://forums.oracle.com/forums/message.jspa?messageID=10905546#10905546

Could you take a look and offer some guidance?

sri anjana Vadevoo said...

%SUBMITSCRIPTNAME resets all my other javascript code.How do I prevent this.

Jim Marion said...

@Sri, can you elaborate? How are you using this MetaHTML? Where are you using it?

sri anjana Vadevoo said...

Hi,

I have grid column that needs to be sorted.This column is a HTML area and cannot use the delivered peoplesoft sorting ie(%submitscriptname(form,sortcolumn)).Hence I have written my own javascript code
-> to make the coulmn heading a link
->to sort the column ascending/descending when the link is clicked.

It works fine but once I try to sort any other peoplesoft column or click download or find or in other words when %submitscriptname is called anywhere else all my JS code is reset.My column heading is no more a link and everything is back to initial stage.Is there any way to bypass the reset?

Thanks,
Sri

Jim Marion said...

@Sri, that makes sense. When you click any link or perform any action, it submits an Ajax request. The Ajax response will overwrite the current page contents. Any JavaScript on your page will still exist, but the element bindings will not exist because the former elements will have been replaced by the Ajax response. I am not sure I like this, but it is how it works. One way I work around this is to use jQuery's live method for event binding. Another approach is to "monkey patch" net.ContentLoader so you can rebind your event handler after the Ajax response.

Kevin Weaver said...

Hey Jim,

We have had multiple issues with ePerformance and our users wanted to autosave the document if the manager click's on the back button. So I was looking into doing a simple like this:

window.onbeforeunload = function () {
// stuff do do before the window is unloaded here.

submitAction_%Formname(document.%Formname,"EP_BTN_LINK_WRK_EP_STORE_PB");
// alert('Auto Save');
}


But the save only works if the alert is fired after the submitAction function? Is there a way to autosave the page if the window is unloading from either the back button or even regular navigation within the component without the alert? And if you could tell me why this only works with the alert, that would be super as well!

Thanks!

Jim Marion said...

@Kevin, I am suspecting it has something to do with timing and the request/response. With an alert, the full request/response can happen for the save. Without it, the browser is just changing pages and killing the save request. You can probably see this in Firebug. The save request would be highlighted in red in the console, and then disappear as the new page loads.

Kevin Weaver said...

I really only want the onbeforeunload to happen if the user clicks the back button. So I attempted to change the onclick event of the hyperlinks like this...


function adaptLinks() {
var links = document.getElementsByTagName('a');
for (i = 0; i != links.length; i++) {
links[i].onclick = (function () {
var origOnClick = links[i].onclick;
return function (e) {
window.onbeforeunload = function(){ };
if (origOnClick != null && !origOnClick()) {
return false;
}
}
})();
}
}
adaptLinks();



This prevents the onbeforeunload and fires the onclick method associated with the link. I wonder if I could use jQuery or something cool like that to select all the input buttons and hyperlinks to override the onclick to blank out the onbeforeunload and call the default onclick event. Then change the onbeforeunload to ask the user if they want to navigate away from the page without saving.

Thanks for you input!

Kevin

Jim Marion said...

@Kevin, I like your approach of capturing the link click before it happens. Yes, you could use jQuery to identify and handle click events. Another alternative is to monkeypatch a method like saveWarning so it calls your save if the user chooses cancel. You will also want to monkeypatch the timeout so that it saves before timing out.

Ramee said...

Hi Jim,

I am on HRMS 9.1 and Tools 8.51. On the compensation page we have 3 grids at level 1 with almost 100 columns in total.
1) There are 2 issues one if the number of rows in the grid reaches more than 80 the page fails to load.
2) Out of the 100 fields in all the grids at most 5 to 6 fields are editable. If I load around 75 rows in each grid the page loads but on a grid field change there is minimal time taken up by appserver in contrast webserver rendering the page is having a processing time of 6 to 8 sec each for a field.

The business user wants more than 100 rows to be displayed on the grid and also have the field change work within 3-4 sec.

I am not sure how to approach the issue to show more than 100 rows in the grid without crashing the appserver, I am more interested in getting the field change work lot quicker.

We have tried many options in the setting and cache and nothing helped. I have seen your post on Jquery, can I make the component differed and use a Jquery to mimic the calculation in the field change on the grid to avoid the appserver and webserver overhead?

Please suggest,
Regards Ricky.

Jim Marion said...

@Ramee/Ricky, yes you can use Ajax (jQuery) to perform the calculation if the data exists in the database. What you would do is add an HTML Area to the grid with a button and some JavaScript to execute on click. This replaces your FieldChange. The JavaScript will make an Ajax request to an iScript to perform the calculation.

If all of the data already exists on the page, the you can perform the calculation with JavaScript and skip ajax.

Robert said...

Hi Jim,

I have successfully added an HTML Area to a page that uses jsTree to display a list of checkboes with multiple levels for a user to select.

Once a user selects checkboxes from the jsTree, I am able to execute an iScript via a javascript button that sets the querystring with the users selected. Once the iScript has been executed, I get the list from the querystring and process accordingly.

However, I am unable to add a PeopleSoft button to the page that calls a javascript to pull which checkboxes are selected. When the button is clicked, the jsTree is re-loaded and I lose the checkboxes that the user selected (even with using jsTree state property, I can't pull the selected checkboxes). I haven't been able to figure out how to stop the automatic refresh of the jsTree from loading when a PS button is clicked. This is the preferred method over iScript as I don't want the page to reload, nor do I want to add to the querystring.

Any help is greatly appreciated.

As always, thanks for your help and your blog is a fantastic help and resource!

Jim Marion said...

@Robert, I think what you are experiencing is PeopleSoft replacing the page's contents on FieldChange. I believe the "hack" would be to Monkeypatch net.ContentLoader to restore the state of the jTree. You can see examples of Monkeypatching net.ContentLoader in my posts Mnkeypatching PeopleSoft and Changing the Search Page Operator

jase said...

Hi Jim,
Love your work!
I have a page and on it I put some html, javascript and use a JQuery library. It all works, but when I click a button on the page which uses standard FieldChange, it repaints the whole page so all my HTML and javascript disappears. Do I need to do something special so that PeopleCode and HTML/Javascript can co exist ?
Thanks.

Jim Marion said...

@Jase, that is true, and is an unfortunate side effect of the way PeopleTools implemented "partial" page refresh (I put partial in quotes because I wonder how they define partial). Since the page does not reload, just the content through Ajax, any JavaScript objects attached to the window object will exist after a FieldChange event. For example, window.jQuery will exist both before and after.

If you have an HTML Area that has jQuery, then jQuery will reload after every FieldChange. The trick I use is to wrap the jQuery library in this JavaScript: if(!window.jQuery) { jQuery lib content here }. I actually modify the jQuery JavaScript library file. I do the same with any jQuery plugins, but test for the existence of the plugin. I actually wrap ALL of my JavaScripts in this manner. It is sort of like C-style include protection, but for JavaScript.

Another alternative is to place all of your code in a common HTML definition such as PT_PAGESCRIPT and then evaluate the URL to determine if PT_PAGESCRIPT is executing on your component. My PeopleTools Tips book has a chapter on creating a configurable version of this.

The best alternative comes with PeopleTools 8.54. It is an online configuration page that allows you to inject any JavaScript into a component. You can do this globally or specify a component.

jase said...

ok, great. Thanks.

Robert said...

Hey Jim,

I tried to implement your recommendation for @Jase and am running into issues.

Before the fix you recommended, I had the following:






I pass in the libraries.

I then commented out that code and replaced with the following:



if(!window.jQuery)
{
var s = document.createElement("script");
s.src = "%bind(:1)";
document.head.appendChild(s);

var t = document.createElement("script");
t.src = "%bind(:2)";
document.head.appendChild(t);
} else {
}


When I test, the initial load does not display the tree. When I click a JS button that initiates a fieldchange event, then the tree does display. Each time the JS button is clicked, the tree is refreshed.

I thought the code above would initially load the tree and then when fieldchange is executed, it would not refresh.

Any help is greatly appreciated.

Thanks.

Jim Marion said...

@Robert, I can't confirm, but I'm suspecting that what you are experiencing is the asynchronous nature of scripts loaded AFTER the the DOM is ready. script tags are loaded synchronously while a document is loading, but asynchronously if appended as DOM elements in the manner you demonstrated. My recommendation was NOT to add "if" tests to the place where you want to include the library but rather to open up the library and modify it. Put that wrapper right in the library itself. Then you can put all the script tags you want into your code, but the script will only parse once. That is the way C header files work as well (ifdef, etc). You put the criteria in the header, not the place where you use the code.

If you prefer to put the test in the location where you import the library, then I recommend using a script loader like $script. I like this one because it is light weight. If you minify it, you can embed it in one line. Another alternative is to create your own loader using either Ajax or a script tag event handler. The key is to make sure that your dependencies resolve in the correct order.