Welcome to the OPA Hub!


Tag Archives: JavaScript Extension

JavaScript Extensions for Oracle Policy Modeling

JavaScript Extensions for Oracle Policy Modeling

It’s almost ready. The perfect Christmas gift (yeah, right, in bizarro-world) or New Year workout kit. The new book is due out in a few weeks, and you have the chance to win a free copy ahead of everyone else by entering our prize Survey (30 seconds) right now.

The book comes with 50 examples. You get them as Zip Files as well when you buy the book. The book covers all the different types of JavaScript Extensions that are currently available, with examples of each of them (sometimes many different examples) using easily-understandable business scenarios as the starting point, and using example projects from Oracle Policy Modeling – so you don’t need to install a Hub, or do anything special – which let’s you get started straight away.

  • Label Extensions
  • Input Extensions
  • Search Extensions
  • Options Extensions
  • Entity Collect Extensions
  • Entity Container Extensions
  • Button Extensions
  • Event Extensions
  • Navigation, Header and Footer Extensions

The OPA Hub Website Community has also stepped up and I am delighted to say that 8 examples (with credits) came from readers of this website. I am very grateful to them and encourage all of you to submit your ideas and code snippets for the next version.

Hopefully this book will appeal to non-technical people too – I wrote it in the same style as the others (Getting Started with OPA, Getting Started with OSvC) and tried to make it as accessible as possible to everyone.

Here, in avant-premiere, is the cover. And no, I’m not sure what those things are either. But they sure look exotic and tasty. I wanted a parsnip on the cover but these were available and they look good. These are important questions that you worry about late at night. The publisher’s website has been updated, watch this space for the launch date!

 

JavaScript Extensions

JavaScript Extension : Geomapping with Leaflet JS

JavaScript Extension : Geomapping with Leaflet JS

Ever since the dawn of time (or at least since the arrival of JavaScript Extensions in Oracle Policy Automation), for whatever reason, the subject of Google Maps (geocoding, reverse geocoding, etc) has been one of the most read and downloaded on this site. But now, given that Google Maps (like Google APIs in general) requires registrations, license keys, and is subject to all sorts of restrictions  and throttling, developers have needed something else to impress their customers and colleagues with eye candy.

Geomapping with Leaflet JS and MapBox

So it’s appropriate therefore that this little post is all about Leaflet JS, “an open-source JavaScript library
for mobile-friendly interactive maps”. The Leaflet JS website is stuffed full of interesting examples and tutorials, so here goes for a short example of using this library in Oracle Policy Automation.

The setup is slightly different than working with Google Maps, but everything starts with the obvious step : download a library! In my case, I downloaded the library and CSS styles and dropped them into the resources folder. The files are tiny.

For the demonstration, my Oracle Policy Automation project contains only a handful of attributes, specifically enough to be able to create an address (street, town, zipcode, country) and a customer name (for a marker on the map).

The second element to handle in your code, is the image tile provider for your map. Working with Leaflet requires two things : Leaflet and a tile provider – in my case mapbox proved to be a good choice. It looks lovely. It requires a registration and access token, but it is very easy to obtain and you can try for free. Leaflet is entirely agnostic however, so if you want to use another tiling source, you can.

Apart from these steps, most of the code looks familiar : get the attribute values, pass them to the geocoding engine and then extract the latitude and longitude from the returned JSON object. Once you have that information,we use onreadystatechange to check that the page is loaded, and then draw the map, using latitude and longitude, and drop a custom marker at the location with a shadow and a popup window associated with the marker.

Because this is based on OpenStreetMap, we need to be good citizens and ensure we give credit where credit is due. Let’s look at a simple example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
/**
 * Richard Napier The OPA Hub Website June 2019
 * Educational Example of Custom Label with Leaflet JS and MapBox
 * I will remember this is for demonstration purposes only
 */
OraclePolicyAutomation.AddExtension({
	customLabel: function (control, interview) {
 
		if (control.getProperty("name") == "xMap") {
			return {
				mount: function (el) {
 
					// Add CSS First
					var head = document.getElementsByTagName('head')[0];
					var linkcss = document.createElement('link');
					linkcss.id = "leafletcss";
					linkcss.rel = 'stylesheet';
					linkcss.type = 'text/css';
					linkcss.href = '${resources-root}/leaflet.css';
					linkcss.setAttribute('crossorigin', "anonymous");
					head.appendChild(linkcss);
					var div = document.createElement("div");
					div.id = "lblmap";
					div.style.visibility = 'visible';
					div.style.height = '370px';
					div.style.width = '400px';
					el.appendChild(div);
 
					var street = interview.getValue("street");
					var city = interview.getValue("city");
					var zip = interview.getValue("zip");
					var country = interview.getValue("country");
					var customer = interview.getValue("customer");
					var latitude;
					var longitude;
					var uri = street + "," + city + "," + zip + "," + country;
					var res = encodeURI(uri);
					var root = "https://api.mapbox.com/geocoding/v5/mapbox.places/" + res + ".json?access_token=pXXX";
 
					$.ajax({
						url: root,
						method: "GET",
						async: false,
						dataType: 'json'
					}).then(function (data) {
						//console.log("Cleaning Up Response");
						//for (var key in data.features) {
						if (data.features[0]) {
							var val = data.features[0];
							longitude = val.center[0];
							latitude = val.center[1];
							//console.log(" Returned " + latitude + " " + longitude);
							$.getScript("${resources-root}/leaflet.js", function () {
 
								//console.log("Script loaded");
								window.onerror = function (errorMsg, url, lineNumber, column, errorObj) {
									console.log('Error: ' + errorMsg + ' Script: ' + url + ' Line: ' + lineNumber
										 + ' Column: ' + column + ' StackTrace: ' + errorObj);
								}
 
								//console.log(document.readyState);
								//console.log(latitude + " " + longitude);
								//	document.onreadystatechange  = function () {
								//console.log("ReadyState Change Event");
								if (document.readyState === "complete") {
									//console.log("ReadyState is Complete");
									var siteIcon = L.icon({
											iconUrl: '${resources-root}/images/Logo_Marker.png',
											shadowUrl: '${resources-root}/images/Logo_Marker_Shadow.png',
											iconSize: [30, 40],
											shadowSize: [30, 40],
											iconAnchor: [0, 40],
											shadowAnchor: [0, 38],
											popupAnchor: [15, -20]
										});
									var myLatLng = new L.LatLng(latitude, longitude);
									var mymap = L.map('lblmap').setView(myLatLng, 13);
									L.tileLayer('https://api.tiles.mapbox.com/v4/{id}/{z}/{x}/{y}.png?access_token={accessToken}', {
										attribution: 'Map data © <a href="https://www.openstreetmap.org/">OpenStreetMap</a> contributors, <a href="https://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>, Imagery © <a href="https://www.mapbox.com/">Mapbox</a>',
										maxZoom: 18,
										id: 'mapbox.streets',
										accessToken: 'pk.eyJ1IjoidGhlb3BhaHViIiwiYSI6ImNqdDJ3Y2liNDFvMjE0M3BpampzOG16cDYifQ.0ppcKabM2WhuaPT-kCGwYw'
									}).addTo(mymap);
 
									var marker = L.marker(new L.LatLng(latitude, longitude), {
											icon: siteIcon
										}).addTo(mymap);
									marker.bindPopup(customer + " is based here.");
								};
 
 
 
							});
						}
 
 
					});
 
					// Assumes JS is loaded
 
				},
				unmount: function (el) {
					if (control.getProperty("name") == "xMap") {
 
						 var myMap = document.getElementById("lblmap");
                        myMap.parentNode.removeChild(myMap);
 
 
					}
 
				}
 
			}
 
		}
 
	}
})

Lines 0-40 : Pretty standard stuff, setting up the CSS file and the encoded URL for the REST call which will get us the latitude and longitude.
Lines 41- 52 : Used to call the service and get / parse the response, giving us the latitude and longitude
Lines 53 – 64 : We need to be sure that we wait until the leaflet JavaScript file is loaded before doing anything else
Lines 67 – 75 : Set up a Custom Marker
Lines 77 – 83 : Add a Map Layer and Credits
Lines 84 – 87 : Add a Marker using a custom image and shadow
Lines 85 – 88 : Add a Popup to the Marker click, and use some Oracle Policy Automation data in the popup.

The rest is just clean-up and go home (the unmount).

Geomapping with Leaflet JS : The Result

The end result is a nicely presented map, with a marker at the correct address, and a cute popup window when the marker is clicked.

Geomapping with Leaflet JS MapBox Example

We’ll see what else we can do with this nice alternative library in the coming weeks. In the meantime, you will find the sample Project in the OPA Hub Shop.

Importing Data into an Interview : Excel Example

Importing Data into an Interview : Excel Example

Readers will remember a while ago I explained briefly how to use Microsoft Excel to act as a Connection Datasource – in this overview article, followed by this one in a little more detail. Now we look at another challenge : Importing Data into an Interview.

Well, here comes another example of the ubiquitous nature of Microsoft Excel. The customer requirement was as follows:

Using a simple mechanism, let the user upload an existing Excel spreadsheet into the Interview. Parse the spreadsheet, read the data in it, create corresponding rows in an Entity. Let the user review the data but do not require any new data entry. There may be up to 250 rows of data to import. So how do you go about about Importing Data into an Interview?

So how can we face up to a challenge like that? We need:

  • An upload that isn’t a standard File Upload Group
  • A parsing mechanism to read Excel and extract the data in a given tab, or wherever
  • A custom Entity Collect to handle the data import / create the rows in a Screen

The shopping list above isn’t that long.

The File Upload is essentially an HTML 5 component to let the user select a file on their computer. We cannot access an arbitrary local path from JavaScript, so we need the user to point to the file they want to upload.

There are a number of JavaScript-based Excel parsers, including the excellent SheetJS js-xlsx which we used. It is capable of converting to and from Excel, which is no easy task when you consider that an Excel file is basically a Zip file with a bunch of complicated stuff inside it. The library can convert to HTML, CSV and magically (for our requirement) JSON. Awesome!

Plus, in a previous post we’ve also looked at the (large amount of) work required to build a Custom Entity Collect Extension. In fact when I was writing that article I was thinking, for goodness sake Richard, when do you think you will actually need to go to the trouble of building that Entity Collect Extension? Well, I’ve finally found a use for it – Importing Data into an Interview!

We need an Entity Collect Extension since we need some way of getting the Excel data into the Entity Collect, which ultimately means we need to do some work behind the scenes between the import of the data and the display of the Entity Collect. We need to rewire the Entity Collect temporarily so that it sucks our Excel data up, before we show it to the user so they can examine the results.

For the purposes of a raw demo, I unplugged all the other functionality (delete buttons, add buttons, etc.) and just concentrated on getting the data into the Entity Collect. There are how ever a few caveats. Once you get into the larger imports, at least in the Debugger, you can expect to see “concurrent record editing” errors. I’m trying to find out what the limit is exactly. But up to a few hundred I think it’s OK.

So let’s look at the items in turn.

File Upload and Data Load

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
{
					//console.log("Starting customInput Mount");
					var div = document.createElement("input");
					div.id = "myFile";
					div.type = "File";
					//div.value = control.getValue();
					el.appendChild(div);
 
					function handleFile(e) {
						var files = e.target.files,
						f = files[0];
						var reader = new FileReader();
						reader.onload = function (e) {
							var data = new Uint8Array(e.target.result);
							//console.log("In Change");
							var workbook = XLSX.read(data, {
									type: 'array'
								});
 
							worksheet = workbook.Sheets["YOURWORKSHEET"];
							jsonoutput = XLSX.utils.sheet_to_json(worksheet, {
						raw: true, header : 1
							});
						//console.log("Read " + jsonoutput );
						};
 
						reader.readAsArrayBuffer(f);	
						var completepath = $(':file').val();
							//console.log(completepath);
						interview.setInputValue("rest_filenameandpath", completepath);
					}
 
 
				var filedialog = document.getElementById("myFile");
					filedialog.addEventListener('change', handleFile, false);
 
					var completepath = $(':file').val();
						control.setValue(completepath);
						//console.log("Hello " + completepath);
						//console.log("Ending customInput Mount");
				}

Assuming you have a custom Input framework as your starting point, the above code will be in the mount. This will build an HTML5 file upload control, and attach an event handler. The code regarding Excel depends upon xlsx.full.min.js being in the resources directory. But that’s it. You’ve loaded the Excel file into a JSON object.

Entity Collect

The next step is to include a Custom Entity Collect in your project, and use the jsonoutput object (which you just created from the imported file above) in the mount of the Entity Collect to load the JSON into the Entity Collect. The following is an extract from the mount code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var numEntities = Object.size(jsonoutput);
					//console.log(numEntities);
					// Remove header row if the file has one
					jsonoutput.shift();
					// load records into the Entity Collect
 
					if (control.getRows() == 0) {
						for (j = 0; j &lt; numEntities - 1; j++) {
							control.addNewRow();
							var mycurrentrecords = control.getRows();
							mycurrentrecords[j][0].setValue(jsonoutput[j][0]);
							mycurrentrecords[j][1].setValue(jsonoutput[j][1]);
							mycurrentrecords[j][2].setValue(jsonoutput[j][2]);
							mycurrentrecords[j][3].setValue(jsonoutput[j][3])
drawrows();

The end result is something like this:

Importing Data into an Interview

The File is loaded into the Entity Collect, and the contents displayed to the user. In my case I unhooked all the code related to modification (the onchange stuff from the original idea) and removed the add / delete buttons, since it was designed to just allow the user to see the loaded result, not modify it.

Importing Data into an Interview

If you want to have a look at the project, just download the very basic example here.

Custom Options : Dynamic List of Values

Custom Options : Dynamic List of Values

One of the things that I always found strange about early Oracle Policy Modeling 12 versions was the inability to use Excel (or Word) as the basis for a List of Values. You either dropped them into the Screen Control as Values, or you built a Value List in the Project tab.

Neither of those seemed to exactly be the “business friendly” sort of thing that was everywhere else in Oracle Policy Modeling. Then along came the “Use Values From Rules” button that showed up a few versions ago:

Custom Options - Use Values from rules

And I thought, Great! Now I can have dynamic values read from an Excel or Word document. But actually, that’s not what it means. It imports the values from the source document, but the link is not dynamic. Change the values, and you would have to click the button again. To me, that just doesn’t really give me what I want.

Now, of course, I’m not about to suggest that every list of values needs to be dynamically loaded at run-time, nor do I want to have to do that. But consider a very simple idea. I have an Entity, with a bunch of instances. And I want to use those instances to be the dynamic content of my list of values. So if the user enters instances on Screen 1, I want to show a drop-down on Screen 12 with the instances as the source of my list of values. I don’t want to create a relationship control : this is not about relationships. This is just about using dynamic values for my list. It seems so obvious to me. But given I may not be thinking like everyone else is, that’s fine too.

One final point : with my luck, the Oracle Policy Automation team will deliver this functionality in the next version!

So here is what we want to do:

  1. Build an Excel sheet with some data
  2. Use that to display a dynamic list of values

This will be a Custom Options extension, since that allows us to load data from an Object in JavaScript. Let’s take the example of a list of train stations (from a previous project we’ve used on the OPA Hub Website).

Custom Options Data Source

Now we need to build a Custom Options JavaScript extension, using the standard template.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
OraclePolicyAutomation.AddExtension({
	customOptions: function (control, interview) {
		if (control.getProperty("name") == "xOptionsParent") {
			console.log("Custom Options Extension found " + control.getProperty("name"));
				var entityId = control.getProperty("entityname");
				var entities = interview._session.config.data;
				var entityinstance;
				var entity;
				var myStations = []
				for (i = 0; i &lt; entities.length; i++) {
					entity = entities[i];
					if (entity.entityId === entityId) {
						break;
					}
				}
				var Stations = entity.instances;
				for (i = 0; i &lt; Stations.length; i++) {
 
					myObject = new Object();
					for (j = 0; j &lt; Stations[i].attributes.length; j++) {
						entityinstance = Stations[i].attributes[j];
 
						if (entityinstance.attributeId === "station") {
							textofentry = entityinstance.value
						}
 
					}
 
					myObject.text = textofentry.toString();
					myObject.value = textofentry.toString();
					myStations.push(myObject);
				}
 
				console.log("List of stations now ready" + JSON.stringify(myStations));
 
				return {
					options: myStations,
					controlType: "Dropdown"
				}
 
		}
	}
});

Let’s look through the code:
Lines 6-10 : Using a Custom Property, cycle through the entities on the Screen and find our Source entity.
Lines 16-30 : iterate through all the instances of that entity, pulling out one attribute. Push that attribute into an Object, twice : once for the value, once for the display value. Of course this is where you could add a Filter and do something clever like only show values that meet some criteria or other.
Lines 35-38 : return the Object, and request a Drop-down (you can also specify other choices like “Radiobutton” or “text-button-group”). The value is case sensitive.

As always, this code is just for educational and entertainment purposes, and I definitely do not imply any warranty or fitness for purpose. Quite the opposite.

To implement it, place the following on a Screen

  1. Your attribute that displays the data. I’m using a Global attribute.
  2. Remember that all the things you want to reference in JavaScript need an attribute name, or an entity name.
  3. An entity container for your Source entity. I chose to hide mine using a Boolean which I hard coded in a Word document. Remember that currently, you need the information source to be present on the Screen to be able to access it in JavaScript.
  4. Add two Custom  Properties to the Control : one for the name and one for the entityname.

It might look like this :

  1. At a minimum add an Entity Container for your Source.
  2. Add the attribute that will be the Custom Options list.
  3. Configure the attribute to have the Custom Properties

At run-time, your drop-down will appear:

Now don’t go loading 500 values into your drop-down, since this is all handled at run-time. But of course, moderation in all things will mean you get what you want.

I’ve added this to the OPA Hub Shop – in the next part of this article, we will take Custom Options to the next level and find a way to make a hierarchy of dynamic values, without having to mess with XML formats, or import anything, or hook up a Connection.

Temporal Reasoning #5 : Temporal Attributes in Interviews

Temporal Reasoning #5 : Temporal Attributes in Interviews

In the final part of this mini-series, the OPA Hub Website continues to look at Temporal Reasoning and in this particular chapter discusses the display of Temporal Attributes data in the Interview experience.

If you missed any of the previous parts of the series, they are here:

So now that the we have a bit of understanding of how these data elements work, what about displaying them in a good way to the end user in the Interview. Well, part of the answer is hidden in part four of this series. We cannot actually display temporal attributes for data entry in an Interview (so conversion comes in very handy).

Let’s be more specific. Using the project from the previous chapter, in 18C, when an attempt is made to display the temporal information on a Screen, various things happen that underline the some important concepts:

Temporal Attributes in Interviews Animation OPA 12Please excuse the retro-style animated GIF above. But the demonstration above illustrates the fact that Temporal attributes and their change-points are complex data types that cannot be handled in a “normal” way. The one exception to the above is if you have a Temporal attribute whose values are the Goal of the interview, then an Explanation will display the data in a coherent way:

OPA 12 - Viewing Temporal Attributes in Interviews

If your project is using RuleScript and your data is pre-seeded, then potentially you could access it by deriving a string value which is a concatenation of the different values and displaying that attribute using a label control.

// RuleScript(myString) &lt;- (temporalBankBalance)
function FindHighest(global) {
	console.log("Starting RuleScript");
	var v = global.temporalBankBalance;
 
		console.log("Base Value of v " + v._baseValue);
		var idx,
		maxVal = null;
		var myString = "";
		console.log("Length of Temporal Array " + v._changeValues.length);
 
	if (v._changeValues.length &lt;= 0) {
		global.myString = "No Temporal Values";
	} else {
		console.log("Getting Temporal Values");
		for (idx = 0; idx &lt; v._changeValues.length; idx++) { var changeValue = v._changeValues.valueAt(idx); if (changeValue != null &amp;&amp; (maxVal == null || changeValue &gt; maxVal))
				maxVal = changeValue;
		}
		global.myString = global.myString + changeValue + ";";
	}
 
}

But given that RuleScript is experimental, that it’s use cases are highly controlled this would be a very bad idea. Plus it would only work if the temporal values were pre-seeded into the Interview, which renders it all very complex.

So what is plan B? It might be to use something like the Entity Container Extension that we have previously looked at (which would imply using the conversion technique mentioned in part four).

If you are looking for a quick and dirty way to get at the Temporal values, even though the JavaScript Extensions do not actually support them today, the values are still accessible. They can be accessed using something like this. I have not published this as a download since the snippet below is very primitive.

 

/**
 * Richard Napier The OPA Hub Website August 2018
 * Educational Example of Custom Container with a Timeline inside
 * I will remember this is for demonstration and educational purposes only
 */
OraclePolicyAutomation.AddExtension({
	customLabel: function (control, interview) {
		if (control.getProperty("name") === "xTimeline") {
 
			return {
				mount: function (el) {
					var script_tag_addition = document.createElement('script');
					script_tag_addition.setAttribute('src', '${resources-root}/js/timeline.min.js');
					script_tag_addition.setAttribute('type', 'text/javascript');
					document.getElementsByTagName('head')[0].appendChild(script_tag_addition);
					var stylesheet_tag_addition = document.createElement('link');
					stylesheet_tag_addition.setAttribute('href', '${resources-root}/css/timeline.min.css');
					stylesheet_tag_addition.setAttribute('rel', 'stylesheet');
					stylesheet_tag_addition.setAttribute('type', 'text/css');
					document.getElementsByTagName('head')[0].appendChild(stylesheet_tag_addition);
 
					var myDiv = document.createElement("div");
					myDiv.setAttribute("class", "timeline");
										myDiv.setAttribute("data-mode", "horizontal");
					el.appendChild(myDiv);
 
 
					var myDivWrap = document.createElement("div");
					myDivWrap.setAttribute("class", "timeline__wrap");
					$(".timeline").append(myDivWrap);
 
					var myDivChild = document.createElement("div");
					myDivChild.setAttribute("class", "timeline__items");
					$(".timeline__wrap").append(myDivChild);
 
					// Don't do this, it relies upon the temporal attribute
					// being the only one in Global exposed on the page as a %label%
					var myTemporal = []
					myTemporal = control._source.screen.config.data[0].instances[0].attributes[0].value.changePoints
						for (var i in myTemporal) {
							var iterateDIV = document.createElement("div");
							iterateDIV.setAttribute("class", "timeline__item");
							iterateDIV.setAttribute("id", i);
							$(".timeline__items").append(iterateDIV)
							var iterateDIVContent = document.createElement("div");
							iterateDIVContent.setAttribute("class", "timeline__content");
							$('.timeline__item[id="' + i + '"]').append( "GBP "  + myTemporal[i].value + " on " + myTemporal[i].date)
						}
				},
				update: function (el) {},
				unmount: function (el) {
					var myDiv = $(".timeline");
					myDiv.remove();
 
				}
 
			}
		}
	}
});

The above snippet relies upon a jQuery plugin to display the data. This gives something like this, a visual timeline on the left of this page:

Temporal Attributes in Interviews JavaScript Extension

Of course there are hundreds of Timeline plugins, each more sophisticated than the other, on the Internet.

We hope that this series has brought to light some of the key features of temporal reasoning, and thanks to Orlando who suggested this series in the first place. Have a good day and see you soon!

 

Oracle Policy Automation Embed Website in Interview End

Oracle Policy Automation Embed Website in Interview

Oracle Policy Automation Embed Website in Interview

This request comes up quite often, at least often enough that I feel the need to mention it today. The example we are going to use is to embed the OPA Hub Website in an Oracle Policy Automation Interview. In addition we are going to pass an attribute to the website so that it performs a search for us. So, let us start our tutorial “Oracle Policy Automation Embed Website in Interview”.

Now, I am sure many of you are old enough to have spent years trying to avoid IFRAME integrations in applications : Siebel, SAP, they all do it or have done it at some point in time, and they are awful for the most part – whether it be from an accessibility, SEO, browser restriction or other perspective. So here are our goals for this mission:

  • Don’t use an IFRAME
  • Add the Website in a way that does not destroy the look and feel of the Interview
  • The Website must actually function properly

The steps to create this Project are shown below. You should be aware (and not be surprised) that this will not work well in the built-in embedded Browser in Debug Mode, so run Debug mode using Ctrl+Debug or deploy the Project to see the final results.

Oracle Policy Automation Embed Website in Interview Pre-requisites:

A New Project called “Oracle Policy Automation Embed Website in Interview” or something shorter.

Create a global attribute with the text the subject with a name of subject.

Create a new Screen to ask what is the subject. I suggest a Drop-down list with the Values “Siebel Integration, JavaScript Extensions,Service Cloud”.

Create a second Screen to display the Website.

  1. Create a label, using the name  of  the subject to display the chosen subject.
  2. Place this inside a Container
  3. Make sure you add a Property for the Container, naming the Object.

By now the Screen should look like this:

Oracle Policy Automation Embed Website in Interview Design

Now we come to the code. This is quite simple,although we will also need some CSS to make it look right. Add a JavaScript file and a CSS file to your resources folder for this Project. The explanation is after this code, which is as always provided for educational and investigative purposes only:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/**
 * Richard Napier The OPA Hub Website April 2018
 * Educational Example of Custom Container with a Website inside
 * I will remember this is for demonstration and educational purposes only
 */
 
OraclePolicyAutomation.AddExtension({
	customContainer: function (control,interview) {
		if (control.getProperty("name") === "xWebsite") {
 
			return {
				mount: function (el) {
					var myDiv = document.createElement("div");
					myDiv.setAttribute("id", "mySpecialDIV");
					document.body.appendChild(myDiv);
					$("#mySpecialDIV").width(900);
					$("#mySpecialDIV").height(600);
					var mySubject = interview.getValue("subject");
					$("#mySpecialDIV").html('XXXXXX'+ mySubject + '"&gt;');
				},
				update: function (el) {},
				unmount: function (el) {
					var myDiv = $("#mySpecialDIV");
					myDiv.remove();
 
				}
 
			}
		}
	}
});

Oracle Policy Automation Embed Website in Interview Code

In the code above, here are the salient points.

Lines 13 to 17 create a DIV and insert it into DOM, appending it to the body of the document.

Line 18 retrieves the value selected by the user on the previous page that is present in the Container as a label.

Line 19 is the most important one. Notice the “XXXXX”. Replace this with the website and any URL construct you need. For example, replace it with the following:

<object data="https://theopahub.com/main/?s=

The code viewer didn’t correctly display that part. Insert it exactly as shown, so that you are concatenating the URL with the user selected subject. The key point here is the use of the object tag rather than an nasty IFRAME. Thanks to Stackoverflow!

The rest of the code just tidies up when the unmount happens.

Oracle Policy Automation Embed Website in Interview CSS

The CSS is quite important here, to ensure that the embedding is seamless. Add this CSS code to your CSS file.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
object {
    height: 100%;
    width: 100%;
}
#mySpecialDIV {
	float: left;
    margin: 0px;
	-webkit-border-radius: 10px;
	-moz-border-radius: 10px;
	border-radius: 10px;
	background-color: #404040;
}
html
{
    border: 0px;
    margin: 0px;
    overflow: hidden;
    padding: 0px;
}
body
{
    border: 0px;
    margin: 0px;
    overflow: hidden;
    padding: 0px;
}

Let’s take a look at the CSS. We use styling to position the DIV, as well as styling to ensure the object tag uses all of the available DIV. Finally we use some tricks to eliminate overflowing content and remove the horizontal scrollbar.

Oracle Policy Automation Embed Website in Interview Debugging

As mentioned earlier, this may be best tested in the Browser, not in the Debug Embedded Browser, so make sure you start with Ctrl+Debug.

The first screen will be straightforward:

Oracle Policy Automation Embed Website in Interview 1

The second screen will display and if your Internet connection is slow, you may have time to witness the two stages of display:

Stage 1 : Show the styled DIV that has been added to the Screen. Of course you don’t have to use this colour, I just wanted to use it for positioning and effect.

Oracle Policy Automation Embed Website in Interview DIV

Stage 2 : The embedded Website is displayed. The embedding is seamless (nice colour scheme!).

Oracle Policy Automation Embed Website in Interview End

  1. Notice that the attribute value has been passed to the OPA Hub Website and a search has been performed for you. The site is fully functional and can be accessed from the Interview Window.

Have a nice day! (The PDF is in the OPA Hub Shop).

JavaScript Extensions with a Live Entity-based Chart in Oracle Policy Modelling

JavaScript Extensions with a Live Entity-based Chart in Oracle Policy Modelling

[NB : An updated post has been written in February 2019]

One of the most interesting questions I have been asked during my current assignment in beautiful Madrid has been the following :

“How can I make a Chart”

Well, that’s easy I said : pick your Charting platform (for example, you might choose D3 as the charting library as it is well-known and incredibly powerful. Or alternatively you could manipulate SVG arcs and lines yourself if you want to, to achieve the same thing.

“How can I make a Chart based on Oracle Policy Automation data”

Well, that’s easy I said : you even have an example in the Oracle Policy Automation Example Projects, called the Loan Advisor. You can see a screenshot from that project, right here:

JavaScript Extensions with a Live Entity-based Chart : Intro

Digging deeper into that Project, we discover that the attributes that are used to create the chart are global :

JavaScript Extensions with a Live Entity-based Chart : Intro 2

“How can I make a Chart that uses Entity data”

Well that’s easy I said, you could hook up the D3 library to the entity, and use a similar system to our custom Entity Container example to retrieve the data. After all, the Pie Chart in D3  accepts any array of data organized into labels and values. You may remember in that example, we sought the name of the entity using EntityId and then iterated through the instances in the Entity to display them.

But upon investigation this is not really satisfactory. Adding an instance to the entity container does not refresh the Chart. In fact, you have to navigate forward and then backwards to get the Chart to refresh. That’s because, of course, your new “instance” does not actually exist yet. It does not get added into the entity.instances[i] list until you leave the current screen.

“How Can I make JavaScript Extensions with a Live Entity-based Chart in Oracle Policy Modelling”

You will have guessed by now that my entourage here is pretty demanding! So here goes. Firstly, the scenario. I have created an Entity called the payment. It has two attributes, the identifier, a text attribute, and the payment amount, a currency or number as you wish.

The goal : on ONE Screen, enter data and view a live chart that updates live, without changing Screens, as data is added or removed.

To experiment further with this, I also decided to create another Entity called the shadow payment. This is a copy of the original, created by the simple rule in Word that follows:

JavaScript Extensions with a Live Entity-based Chart Intro 3

The idea was to test if it would work both on inferred and normal entity instances. It seemed to, so you could probably use the code with either. To enable my shadow entities, I set up the relationship for the above Word rules to function properly.

JavaScript Extensions with a Live Entity-based Chart in Oracle Policy Modelling Intro 5

The Code Concept

Now we come to the code itself. This is, as I always state clearly, just code that I have hacked together to see if a concept had the ability to be taken further. It is in no way production or even unit test ready. That’s your problem. But I hope you find it interesting and inspiring.

Another note, I have seen that this “code view” messes with some characters. The OPA Hub Shop has a PDF version which you can use to compare and correct anything that looks wrong, notably the > and < characters don’t show up correctly here.

The code uses D3 for the pie chart, and the explanation follows after the code. Pop D3 in a JS file in the resources folder, as well as the D3 library.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
/**
 * Richard Napier The OPA Hub Website April 2018
 * Educational Example of Custom D3 Chart Extension with live refresh on a screen
 * I will remember this is for demonstration and educational purposes only
 */
 
OraclePolicyAutomation.AddExtension({
	customContainer: function (control) {
		if (control.getProperty("name") === "xChart") {
 
			return {
				mount: function (el) {
 
				},
				update: function (el) {
 
					if (document.readyState == "complete") {
 
						var entity = control._source.screen.serverState.shadowpayment;
						var myFlatList = [];
						var myObject;
 
						var width = 300, //width
						height = 300, //height
						radius = 150, //radius
 
						color = d3.scaleOrdinal(d3.schemeCategory10);
						var size = Object.keys(entity).length;
 
						for (i = 1; i &lt; size+1; i++) {
							myObject = new Object();
							myObject.label =control._source.screen.serverState.shadowpayment["@_@opa-rel-target={payment[the payment" + i + "]}/global[global]"].shadow_payment;
							myObject.value = control._source.screen.serverState.shadowpayment["@_@opa-rel-target={payment[the payment" + i + "]}/global[global]"].shadow_amount;
							myFlatList.push(myObject);
							//console.log(" Flattened the list - item " + i);
						}
						data = myFlatList;
						var vis = d3.select(el);
						vis.select("svg").remove();
						var vis = d3.select(el)
						.append("svg")
							.data([data])
							.attr("width", width)
							.attr("height", height)
							.append("g")
							.attr('transform', 'translate(' + (width / 2) +
								',' + (height / 2) + ')'); 
						var arc = d3.arc().outerRadius(radius)
							.innerRadius(0); ;
						var pie = d3.pie()
							.value(function (d) {
								return d.value;
							});
						var arcs = vis.selectAll("g.slice")
							.data(pie)
							.enter()
							.append("svg:g")
							.attr("class", "slice");
						arcs.append("svg:path")
						.attr("fill", function (d, i) {
							return color(i);
						})
						.attr("d", arc);
						arcs.append("svg:text")
						.attr("transform", function (d) {
							d.innerRadius = 0;
							d.outerRadius = radius;
							return "translate(" + arc.centroid(d) + ")";
						})
						.attr("text-anchor", "middle")
						.text(function (d, i) {
							return data[i].label;
						});
					}
				}
			}
		}
	}
});

Review of JavaScript Extensions with a Live Entity-based Chart

Line 8 : This is a Custom Container. So make sure that you drop a container into your Screen, and in the container make sure you add your Entity. In my example, I displayed both the original the payment entity, for the user to add or delete records, as well as the shadow payment which I leveraged to draw the pie chart.

Line 19 ; This example uses control._source.screen.serverState.shadowpayment. This JavaScript object contains the instances of your entity, and is updated as new instances are added, before you leave the page. Of course the name is the technical name  of your entity so don’t forget to add that to your Data tab.

Line 27 : Selecting a standard set of colors from the D3 color categories

Line 28: Finding how many objects are inside the control._source.screen.serverState.shadowpayment object. Each instance is a child object.

Lines 32 and 33 : Creating dynamically the identifiers of each row to select the label and the value using the names of the attributes in your entity, and copying them into a flat object with values and labels

Line 37 : Set the flat list as the data source for the Pie Chart

Line 40 : Add the Chart into the Container

Line 41 : Add the pie chart, setting the origin to the center of the Container, and hooking up to the data we created

Line 50 and beyond : Using D3, draw the arcs and fills that make the Pie Chart by going through the total set of data and dividing the pie into the right number of pieces.

This is the result, in full Hollywood glory, of JavaScript Extensions with a Live Entity-based Chart in Oracle Policy Modelling.

The PDF version is on the OPA Hub Shop, just search for the Pie Chart example.

We will of course be revisiting this to investigate making it more robust, but it’s a good starting point. Have fun and see you soon!

Custom Entity Container with JavaScript Extensions Revisited

Custom Entity Container with JavaScript Extensions Revisited

Assiduous readers will recall that we followed a series of adventures in Entity Container extension some time ago, from a basic tool that worked only in Debug Mode to a more interesting and robust concept that worked once deployed. For reference those Custom Entity Container with JavaScript Extensions articles can be found in the following links

So why come back to this example? For several reasons it seems appropriate to talk again about Custom Entity Container with JavaScript Extensions. Firstly, it is something that is often coming up in classes or on customer sites. So, subjectively I want to talk about it. Secondly, it is a great way of learning the ins and outs of the JavaScript extensions in general.

Yesterday, I was mad

I noticed that the PDF generator I had used for the third (and most interesting and useful example) had pretty much destroyed part of the file : specifically a couple of lines were duplicated and others were truncated. So it is time to revisit this, if only to correct the errors (I have uploaded a more up-to-date file, so that some of the errors have gone).

So let’s set the scene first. We want to display some entity instances. These are generated in my case by an Excel Spreadsheet. They contain one entity, the insult and this entity has three attributes : an Id number, the text of the insult and an insult level – a numeric categorisation of the insult. The higher the number, the more severe the insult. The insults themselves come from Tintin, or more precisely Captain Haddock.

There are no conditions in this Excel file, so the instances are created. There are 240, so we need a good display of our instances. The default display is too long, with no useful scroll bar. We want to replace this with jsGrid, a lightweight jQuery grid. We want something that replaces the style on the left with the style on the right:

Custom Entity Container with JavaScript Extensions Revisited

We would like

  • A grid format using little space
  • A scroll bar
  • A pagination control

The visual elements will be provided by jsGrid, a lightweight JavaScript control. We are also going to set the bar a little higher than last time. We want to have a dynamic filter of the grid, so that the user can view what they want (and not always have the 240 instances on the grid).

Custom Entity Container with JavaScript Extensions Revisited 2

Note: we must tread very carefully here. We must not change the business logic in any way. We must separate the concerns and provide purely UX elements in our JavaScript extension. But given this is inferred data, I think a little filtering is fine, as long as the underlying relationship is not tampered with.

The code would be based on the standard template, so I will simply put it here, in all of it’s quickly-strung together glory, so that you can read it, learn about it, clean it and make it industrial. As I always like to make clear, anything I post here is strictly not-ready, big-picture, here’s-an-idea for you to look at and make your own. This Custom Entity Container with JavaScript Extensions example is available on the OPA Hub Shop for download, as usual. It is listed as example #3 of Custom Entity Container.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
/**
* Richard Napier The OPA Hub Website April 2018
* Educational Example of Custom EntityContainer Extension
* I will remember this is for demonstration and educational purposes only
*/
OraclePolicyAutomation.AddExtension({
customEntityContainer: function (control, interview) {
//console.log("Get Array Reference");
if (control.getProperty("name") == "xEntity") {
var entities = interview._session.config.data;
var entityId = "entitypublicname";
var entity;
for (= 0; i &lt; entities.length; i++) {
entity = entities[i];
if (entity.entityId === entityId) {
break;
}
}
return {
mount: function (el) {
//console.log("Beginning customEntityContainer jsGrid");
var myDiv = document.createElement("div");
myDiv.setAttribute("id", "mySpecialDIV");
//console.log("Styled customEntityContainer");
el.appendChild(myDiv);
var myFlatList = [];
var myObject;
for (= 0; i &lt; entity.instances.length; i++) {
myObject = new Object();
myObject.insult = entity.instances[i].attributes[0].value.toString();
myObject.insult_text = entity.instances[i].attributes[1].value.toString();
myObject.insult_score = entity.instances[i].attributes[2].value.toString();
myFlatList.push(myObject);
//console.log(" Flattened the list - item " + i);
}
$("#mySpecialDIV").jsGrid({
width: "80%",
height: "400px",
sorting: true,
paging: true,
pagelndex: 1,
pageSize: 10,
pageButtonCount: 10,
data: myFlatList,
fields: [{
name: "insult",
type: "text",
width: 20,
title: "id"
}, {
name: "insult_text",
type: "text",
width: 150,
title: "text"
}, {
name: "insult_score",
type: "number",
width: 20,
title: "score"
}
],
controller: {
loadData: function (filter) {
return $.grep(myFlatList, function (item) {
return item.insult_score === filter.insult_score
})
}
}
});
//console.log("Finished customEntityContainer");
},
update: function (el) {
var myslidervalue = $("[role*='slider']").attr("aria-valuetext");
$("#mySpecialDIV").jsGrid("search", {
insult_score: myslidervalue
}).done(function () {
//console.log("filtering completed with slider value " + myslidervalue);
});
},
unmount: function (el) {
var myDiv = $("#mySpecialDIV");
myDiv.remove();
//console.log(" Removed the customEntityContainer ");
}
}
}
}
});

So now let’s look at the key elements (don’t forget to download and place jQuery and jsGrid files into your resources folder) :

Line 11 – this should be replaced with the name of your entity (not the text, but the name or XML tag as some call it). We are going to search amongst the entities until we find yours.

Lines 30 to 35 –  the code extracts your entity and pulls out three attributes from the entity. Note of course that these three attributes need to be placed in your Interview Screen, inside the Entity Container, for this data to be available. Essentially the extracted information is made into a JavaScript object, and the object added to an array.

Line 38 – this is the start of the jsGrid code.

Line 47 – this is the definition of the three columns of data in the table and how to display them.

Line 66 – this is the custom filter function which will hide any instances that do not have the selected score.

Line 77 – this is where we obtain the value of the slider and we refresh the table to only show those records using the filter function.

Thanks to the Madrid crew for their suggestions. In the next few days we will look at another Custom Entity Container with JavaScript Extensions example, this time with a dynamic chart using the same principle. Please note as usual that for best results when debugging, use Ctrl+F5 to debug in a decent browser.

JavaScript Custom Extension Google Maps for Addresses and Reverse Geocoding

JavaScript Custom Extension Google Maps for Addresses and Reverse Geocoding Input

What is it about Google maps? This little example of a JavaScript Custom Extension Google Maps for Addresses and Reverse Geocoding was often requested by customers and students alike. Whether it be in the Siebel Hub or the OPA Hub Websites, Google Maps always seems to be a popular topic!  When I did a Google maps customlabel in Oracle Policy Modelling a few months ago, it was one of the most downloaded examples we have ever had.

JavaScript Custom Extension Google Maps for Addresses and Reverse Geocoding

And so, all I have decided to revisit the example.  This time however we will create a custom input control, and the user will click on the map to show where they are located.  These might be useful in an emergency situation, or something far more mundane such as simply be able to say what your address is, quickly.

To do this, we’re going to use the Google Maps JavaScript API once again.  We will use the same functionality as before, with new twists:

  • Marker on the map to display their browser-reported location (seen previously)
  • Information window to display text  (seen previously)
  • From the latitude and longitude obtain the Street Address
  • The possibility to drag the marker to another location if incorrect 
  • A button to confirm the selection of the address
  • The chosen the address will be passed to an attribute in the  Project

In this example therefore, we will need to use reverse geocoding.  Specifically, we need to convert the latitude and longitude into an address.  Using this Google service will normally provide us with a close match.  There may be multiple matches, or indeed no match at all.

For simplicity we will only handle the case where at least one match is found, and we will take the first match which is typically the closest.  We will not look at the other cases where there are multiple choices.  We are reserving that for another post in a little  bit!

In the example provided, which is as usual for education and amusement purposes only, the information from Google goes to our interview and is passed into an attribute.  Of course, what happens after that, it’s you that decides!

To keep things simple, the initial part of the script is the same.  We use your location to plant the marker on the map.  The exact icon used, depends entirely on what you chose in the interview : we used a simple example where the user declares an accident or a fire or some other emergency.  The marker is planted on the map. Then we introduce drag and dragend handlers, as well as a click handler.

The first page of the Interview is shown below.

JavaScript Custom Extension Google Maps for Addresses and Reverse Geocoding 1

Then we have a typical page displaying only a single Input Control for our demo.

OPA 12 - JavaScript Custom Extension Google Maps for Addresses and Reverse Geocoding 5

Our code will handle a few events. The marker click handler will display the address of the location in an infowindow. The drag and dragend handlers manage the user dragging the marker to a new location: maybe they didn’t get it right first time, so we let them do it again.

JavaScript Custom Extension Google Maps for Addresses and Reverse Geocoding 2

The two main functions in this code are the confirm_my_address function, which lets the user click a button in the marker and pass the address to Oracle Policy Automation to confirm that the marker is in the correct location, and the geocodeLatLng function, which does exactly what it says.

JavaScript Custom Extension Google Maps for Addresses and Reverse Geocoding 3It takes the latitude and longitude of the marker and queries Google to find the address of the marker before it is pushed to our attribute.

JavaScript Custom Extension Google Maps for Addresses and Reverse Geocoding 4

Because it doesn’t always work, we also have to manage the situations when had no address is returned. In this case we handle it with nothing more than an alert, which is fine for our little test. Regarding geocoding and so on you can find much more information of course on the Google Maps JavaScript API Documentation page.

Enjoy the video and if you are interested, you can download the code example for free, with no warranties and just for fun, from the OPA Hub Shop as usual. Remember to use Ctrl+F5 to run it in a real browser.

video

JavaScript Extension Custom Header as a Timer in Oracle Policy Modelling

JavaScript Extension Custom Header as a Timer in Oracle Policy Modelling

Sometimes I get distracted. I was supposed to be preparing for a workshop last night on an unrelated, Siebel-centric topic. I happened to be reading (again) through some material on our sister site, https://siebelhub.com, and I came across an example – which I have always been impressed by – from Duncan Ford, one of the Siebel Open UI and JavaScript gurus who contributes to the Siebel content on our blog. And it got me thinking about and Oracle Policy Automation JavaScript Extension Custom Header as a Timer.

The basic tenet of the script was building a set of statistics for timing performance : how long did such and such a page take to load and be ready, in a sense. In Siebel-world, this is a constant worry and ongoing process. So I got distracted by this and thought about Oracle Policy Automation and how we might use some of the ideas in our own script. I settled on the basic principle that I would want to know about how long it took people to finish a given Screen (ultimately you could extend it to the Stage concept as well). So not directly about performance, more about user time.

To do this, I figure we already have a bucket-load of exciting charts from the Hub :

JavaScript Extension Custom Header as a Timer

You know the sort of things I mean, available for each project : you can grab a set of information about a Project, change the type of chart, decide how to split the axes, decide which version to look at, filter on Service Cloud criteria and number of days and so forth.

These are great  tools. Of course, you also might have the mindbogglingly powerful In-Memory Policy Analysis with TimesTen and so on.

But you might not have any of that to hand, and you might want to work out how much time, each person spends on each Screen. Of course the navigation paradigm in Oracle Policy Automation is different to a Customer Relationship Management application like Siebel – you can go back and forth between the Screens quite a lot. All I really want to do in this case – because it was useful to me – was to identify the cumulative time spent on each Screen, and display it in the Browser at the end of the Interview:

JavaScript Extension Custom Header as a Timer

It sounds really easy but there are a couple of things that will be interesting to talk about along the way.

JavaScript Extension Custom Header as a Timer in Oracle Policy Modelling  – Data

I decided to choose the customHeader as my starting point. The documentation states that this renders the header for an interview. So by logical extension it probably has the things we might need :

  • Access to the Interview Object
  • Access to the Stages and Screens in the Stage
  • Adding code that does not implement any UI probably won’t cause a problem, since the UI does not have to display a Header (it’s an option in the Styles dialog)

For the second part of the requirement, the display of the results, I decided to use the customLabel approach with jsGrid, similar to the example back in December of last year.

So I knew I would need 2 files, one for the custom Header and one for the custom Label. Since the results would only display on the last page (“Interview Complete”), I wanted to be able to pass the results from the Header to the Label easily. So how did the JavaScript Extension Custom Header as a Timer in Oracle Policy Modelling work out? Well, as usual I was just experimenting so it is rough, ready and not very robust. But it is interesting enough to provide some talking points. Let’s look at the code for the Header first.

First the opening salvo of the customHeader:

JavaScript Extension Custom Header as a Timer

  1. We are going to store results in an array of JavaScript objects. This is similar to how the actual Screens are accessed in the Interview, and also it allows us to plug the data straight into the jsGrid component.
  2. So here is the Array
  3. I’m loading the set of Screens here. Be aware, that the Screens you load will only be those in the current Stage. So our myScreens array will be used to store (as you navigate) all the screens in all the stages, to have only one big array when you have finished.
  4. I’m checking in my Array of Objects to see if the Screen we are on has ever been visited : is it in my Screens array?
  5. If it is in my Array, this is not the first time you have been on the Screen in question. We update the information about the Screen in the Object, calculating the elapsed time using a very rough and ready technique.

JavaScript Extension Custom Header as a Timer

Here is the rest of the script, which I have split out into a second part for readability.

  1. If the Screen is not in myScreens, then it is a new Screen. We add it and set up the basic information.
  2. We hook our myScreens array to the Interview object.

JavaScript Extension Custom Header as a Timer in Oracle Policy Modelling – Label

Over in the Label code, the process is very familiar:

JavaScript Extension Custom Header as a Timer 5

  1. Create a custom DIV on the final Screen using the Custom Property “name” to make sure we only execute on the label we are interested in. Get a reference to our myScreens array.
  2. Spawn a jsGrid from the DIV (which implies of course that jQuery and jsGrid files are in the /resources folder of your Project).
  3. Build the field object, noting that you can format the fields, or add / remove fields as you see fit from your array of Objects.

So if all goes according to plan you might get something like this. Note that the whole thing only works if you navigate normally – that is to say you navigate using the navigation Buttons or stage Buttons. Using the Debugger tree on the left does not have the same effect at the array will be empty. The array will also be empty if you do not reset between each debugging session. Once deployed, there should be none of those issues of course, or you could Ctrl+F5 to debug in Chrome or your favorite browser.

OPA 12 - JavaScript Extension Custom Header as a Timer in Oracle Policy Modelling 6

This being a bit of an experiment, I didn’t go much beyond 5 screens in 4 different stages, nor did I add any events to time the Stages, or to time Controls  – such as “on Focus” and “on Blur” which I thought about doing to be able to get the times in and out of individual controls and which I might do at some point.

JavaScript Extension Custom Header as a Timer – Conclusion

Anyway I hope the ideas, if not the implementation, have given you some pause for thought – namely how to get the Screens and Stages and load them into your own Array, and pass them to other Controls. The experimental code is on the OPA Hub Shop, search for customHeader.

Have a nice day!