Page 1 of 1

Dynamic Informational Message Box

Posted: Tue Jan 14, 2014 12:50 pm
by DaveLClarkI
We have need of a dynamic informational message box which all of our applications can use. We can't use the widget-based message support built into Profound UI or the screen-based message support because those always show up as error messages.

We may want just an informational message or one that isn't tied to a widget. I know we could manually create such a message box in each screen, but we'd prefer that this be done dynamically just by calling a function in our custom.js file. So, we get to my question...

What would be the recommended means of creating such a dynamic widget within the Profound UI page? Note that I do have advanced JavaScript skills which I can use to create advanced HTML structures on the fly. But, I want this to also work well with Profound UI. Your recommendation? Thanks.

Re: Dynamic Informational Message Box

Posted: Tue Jan 14, 2014 7:12 pm
by Scott Klement
There are a bunch of different options here. Not exactly sure what you're after? By "widget based message support", I assume you're referring to the "error messages" property on the widgets? Yes, that's always styled as an error message. But, it is hardly the only way to display messages... you could simply use an output field... or an output field on a dialog with an OK button, etc.

So, if you want to drive this from RPG (which is the way most of our customers would do it, and seems the most practical) you could write an RPG program that just takes the message as a parameter... maybe the message type (info, warning, error, whatever) and maybe also returns replies, if that's desired. (I'm thinking like the MessageBox() API in Windows). So this would just be a standalone RPG progam you can CALL when you want to show a message box. The RPG program would have it's own display file with just the one record format you need to display this, and it would have keywords like "assume" and "Show as window" to make it overlay existing screens. This is probably the best approach.

If you wanted to drive this from JavaScript, (which I don't think is ideal here, since the flow of the application may change based on the response, and it just makes more sense to drive the flow of the application from RPG vs. JavaScript) then you could simply use standard DOM calls to whack a div into the screen, position it, etc. Standard dynamic HTML stuff... and return the result from your function so that JS routines could use it... This might make sense if you're coding a lot of the program logic into the display file with JavaScript for some reason.

Re: Dynamic Informational Message Box

Posted: Wed Jan 15, 2014 10:42 am
by DaveLClarkI
OK, I've started down the road of a native JavaScript solution. It *is* driven by RPG; but, not directly. There will be a hidden text box which can receive a string of HTML from RPG. In the onload event for the screen, I have this code:

Code: Select all

if (get("htmlMessage") > '')
{
  wob.displayMessageText('', get("htmlMessage"), 3000, 'mouse');
}
The reason for the empty first parm is because this function will serve double-duty. The first parm is for a widget id that the programmer chose to code directly into their screen. Thus, if a value for that parm is omitted, then the function will build its own, dynamic widget. The third parm is for a jQuery fadeout() and the fourth parameter is for positioning. Options include 'top', 'bottom', 'left', 'right', and 'center' in addition to the 'mouse' option shown.

Still working on the positioning, but this is what I have for building it so far:

Code: Select all

		dyno = document.createElement("div");	// then, (re)create it
		dyno.setAttribute("id", dynoId);
		dyno.setAttribute("class", "dropShadow");
		dyno.style.position = "absolute";
		dyno.style.left = "0px";
		dyno.style.top = "0px";
		dyno.style.background = "no-repeat white";
		dyno.style.border = "3px solid green";
		dyno.style.borderRadius = "3px";
		dyno.style.font = "bold normal 12px 'Arial, sans-serif'";
		dyno.style.padding = "0px 5px 5px 0px";	// bottom, left, right, top
		dyno.style.width = "200px";
		dyno.style.zIndex = "200";
		dyno.innerHTML = messageText;			// set value of dynamic widget
		getObj("5250").appendChild(dyno);		// add to page
		$("#" + dynoId).fadeOut(fadeDuration);	// jQuery method to fade object
Note that I am placing it in the "5250" container. What is the name of the container when there is no Genie session? ...or, is there a PUI-provided means of obtaining the container name currently in use so that I don't have to hard-code it? Thanks.

Re: Dynamic Informational Message Box

Posted: Wed Jan 15, 2014 11:03 am
by David
You'll want to use 'pui.runtimeContainer', rather than referencing the div "5250" by id. The div is called something else when running outside of Genie. This property always has a reference to the correct div element, regardless of how you run the session.

Re: Dynamic Informational Message Box

Posted: Wed Jan 15, 2014 11:33 am
by DaveLClarkI
So I can code the following?

Code: Select all

pui.runtimeContainer.appendChild(dyno);      // add to page

Re: Dynamic Informational Message Box

Posted: Wed Jan 15, 2014 11:57 am
by DaveLClarkI
I tried that and it is working. Thanks.

Re: Dynamic Informational Message Box

Posted: Fri Jan 17, 2014 4:44 pm
by DaveLClarkI
If anyone is interested, I'm attaching the code I created for this. It can be used in three ways -- to load and animate an existing message widget defined in the page, to animate a dynamic widget driven from your RPG code, and to animate a dynamic widget driven by JavaScript only. There is also a couple of CSS definitions that go with it. Lastly, jQuery™ is required for the fade animation effects.

NOTE: "wob" is the name of our Genie skin so you can change that part to what you want.

To use the dynamic widget driven from your RPG code, you can use something similar to the following in your screen "onload" event -- which this code uses txtMessage as a hidden text box bound to your RPG.

Code: Select all

wob.displayMessageText(null, getObj("txtMessage"), 0, 'center');
To use the dynamic widget driven by JavaScript only, the following is an example using the grid onrowclick event. In my case, the function I call here is one which counts the currently selected grid rows and if more than one is selected the dynamic message function is called.

Code: Select all

window.setTimeout(function()
{
  var cnt = wob.countGridRowSelections("ACLKUV1S", "*IN30");
  if (cnt > 1)
  {
    changeElementValue("txtMessage", cnt + " selections active.");
    wob.displayMessageText(null, getObj("txtMessage"), 0, "mouse", event);
  }
}, 100);
These are the CSS definitions:

Code: Select all

.dropShadow {
  -moz-box-shadow:    5px 5px 3px #333;
  -webkit-box-shadow: 5px 5px 3px #333;
  box-shadow:         5px 5px 3px #333;
}

#dynoInfoBox {
  background:    no-repeat white;
  border:        3px solid green;
  border-radius: 3px;
  color:         black;
  font-family:   Arial, sans-serif;
  font-size:     12px;
  font-weight:   bold;
  padding:       5px 10px 5px 10px; /* top, left, bottom, right */
  z-index:       200;
}
These are all the functions required:

Code: Select all

/*	================================================================================
	displayMessageText()	sets the value of a widget to the specified message text,
							makes the widget visible, and then fades the widget to
							invisibility -- ready for the next go-round.  If you do
							NOT want a fade effect, specify -1 for the fade duration.
							Optionally, the first parm can be supplied as null and
							this causes a dynamic message box to be built, displayed,
							and faded automatically.  The position and event parms
							apply only to this dynamic message box; where: position
							can be specified as 'center', 'top', 'bottom', 'left',
							or 'right'.  Alternatively, if there is an associated
							mouse event, then position may be specified as 'mouse'
							to align the popup message with the mouse at the time
							the mouse event occurred.  Supply the event object parm
							in this case.
*/
wob.displayMessageText = function(widgetId, msgSource, fadeDuration, position, event)
{
	var messageText = get(msgSource.id);	// get any message text
	changeElementValue(msgSource.id, "");	// clear any message text
	if (messageText <= " ") return;			// if no message text, then exit

	var fadeDefault = 2000 + (Math.ceil((messageText.split(/\s+/)).length / 2) * 1000); 
	fadeDuration = (fadeDuration) ? fadeDuration : fadeDefault; // set default if missing
	if (widgetId)							// if Profound widget identified
	{
		changeElementValue(widgetId, messageText);			// set value of widget
		applyProperty(widgetId, "visibility", "visible");	// show the widget
		$("#"+widgetId).fadeIn(0);							// jQuery method to show object
		if (fadeDuration > 0)								// if fade out requested
		{
			$("#"+widgetId).fadeOut(fadeDuration);			// jQuery method to fade object
		}
	}
	else									// else, create dynamic widget
	{
		fadeDuration = (fadeDuration > 0) ? fadeDuration : fadeDefault; // force a fadeout
		position = (!position) ? 'center'
				 : (event && position == 'mouse') ? position
				 : (('top,bottom,left,right,center').indexOf(position) > -1) ? position
				 : 'center';					// need a default position?
		var offsets = wob.getContainerOffsets(msgSource.parentNode, event);	// get page and mouse offsets
		var dynoId = "dynoInfoBox";				// set id of dynamic widget
		var dyno = document.getElementById(dynoId);	// get pointer to dynamic widget
		if (dyno && dyno.id == dynoId)			// if dynamic widget already exists
		{
			dyno.parentNode.removeChild(dyno);		// trash it
		}
		dyno = document.createElement("div");	// then, (re)create it
		dyno.setAttribute("id", dynoId);
		dyno.setAttribute("class", "dropShadow");
		dyno.style.position = "absolute";
		dyno.style.left = "300px";
		dyno.style.top = "200px";
		if (messageText.length > 50)			// if this is a long message
		{
			dyno.style.width = "280px";			// limit width of message box
			dyno.style.whiteSpace = "pre-wrap";	// and perform word wrap
		}
		else									// else default width to length of text
		{
			dyno.style.whiteSpace = "nowrap";	// and no word wrap
		}
		dyno.style.visibility = "hidden";		// initial setting to allow repositioning
		dyno.innerHTML = '<img style="float: none; width: 16px; height: 16px; vertical-align: text-bottom;"'
					   + ' src="/profoundui/proddata/images/icons/information.png"></img> ';
		dyno.innerHTML += messageText;			// set value of dynamic widget
		dyno = msgSource.parentNode.appendChild(dyno);	// add to container
		switch (position)
		{
			case 'mouse':
				dyno.style.left = (offsets.mouseLeft + 10) + "px";
				dyno.style.top = (offsets.mouseTop - dyno.offsetHeight - 10) + "px";
				break;
			case 'center':
				dyno.style.left = (Math.floor(offsets.width / 2) - Math.floor(dyno.offsetWidth / 2)) + "px";
				dyno.style.top = (Math.floor(offsets.height / 2) - dyno.offsetHeight) + "px";
				break;
			case 'top':
				dyno.style.left = (Math.floor(offsets.width / 2) - Math.floor(dyno.offsetWidth / 2)) + "px";
				dyno.style.top = "5px";
				break;
			case 'left':
				dyno.style.left = "5px";
				dyno.style.top = (Math.floor(offsets.height / 2) - dyno.offsetHeight) + "px";
				break;
			case 'bottom':
				dyno.style.left = (Math.floor(offsets.width / 2) - Math.floor(dyno.offsetWidth / 2)) + "px";
				dyno.style.top = (offsets.height - dyno.offsetHeight - 5) + "px";
				break;
			case 'right':
				dyno.style.left = (offsets.width - dyno.offsetWidth - 5) + "px";
				dyno.style.top = (Math.floor(offsets.height / 2) - dyno.offsetHeight) + "px";
				break;
			default:
		}
		dyno.style.visibility = "visible";
		$("#" + dynoId).fadeIn(0).fadeOut(fadeDuration);	// jQuery method to fade object
	}
}

/*	================================================================================
	getContainerOffsets()	returns an object describing various dimension and
							position attributes for the specified container object.
							Optionally, if the event object parameter is passed, the
							returned object also includes adjusted mouse offsets.
*/
wob.getContainerOffsets = function(container, event)
{
	var offsets = wob.browserPageOffsets();		// get scrolled offset of page
	offsets.client = wob.browserClientArea();	// get client area of page

	var ele = container;   						// starting from the container
	do					   						// calculate container's offsets
	{
		offsets.left += ele.offsetLeft;				// include left offset
		offsets.top += ele.offsetTop;				// include top offset
	}
	while (ele = ele.offsetParent);				// which includes all offset parents

	var coord = pui.getDimensions(container);	// get coordinates of effective container dimensions
	offsets.width = coord.x2 - coord.x1;		// calculate container width
	offsets.height = coord.y2 - coord.y1;		// calculate container height

	if (event)									// if an event object was included
	{											// then set offset for mouse coordinates
		offsets.mouseLeft = getMouseX(event) - offsets.left;
		offsets.mouseTop = getMouseY(event) - offsets.top;
	}

	return offsets;								// return offsets to caller
}

/*	================================================================================
	browserClientArea()	returns the width and height of the displayable area of the
						browser page that does not require scrolling.
*/
wob.browserClientArea = function()
{
	if (document.documentElement
	&& typeof document.documentElement.clientWidth != 'undefined')
	{
		return { width: document.documentElement.clientWidth,
				height: document.documentElement.clientHeight };
	}
	if (typeof self.innerWidth != 'undefined')
	{
		return { width: self.innerWidth,
				height: self.innerHeight };
	}
	if (document.body
	&& typeof document.body.clientWidth != 'undefined')
	{
		return { width: document.body.clientWidth,
				height: document.body.clientHeight };
	}
	return null;
}

/*	================================================================================
	browserPageOffsets()	returns the left, top, width, and height offsets of the
							scrolled browser page: where, left and top will be zero
							if the page is not scrolled.
*/
wob.browserPageOffsets = function()
{
	if (document.documentElement
	&& typeof document.documentElement.scrollTop != 'undefined')
	{
		return { left: document.documentElement.scrollLeft,
				  top: document.documentElement.scrollTop,
				width: document.documentElement.scrollWidth,
			   height: document.documentElement.scrollHeight };
	}
	if (typeof self.pageXOffset != 'undefined')
	{
		return { left: self.pageXOffset,
				  top: self.pageYOffset,
				width: document.width,
			   height: document.height };
	}
	if (document.body
	&& typeof document.body.scrollLeft != 'undefined')
	{
		return { left: document.body.scrollLeft,
				  top: document.body.scrollTop,
				width: document.body.scrollWidth,
			   height: document.body.scrollHeight };
	}
	return null;
}

Re: Dynamic Informational Message Box

Posted: Wed Jan 22, 2014 5:50 pm
by DaveLClarkI
The above post has been corrected as a result of new things I've learned in this process.