If you are not quite satisfied with out-of-the-box [Inspect Workspace UI], in this article I offer you to build your own UI or to check out my version.
As usual, the front-end going to be based on a JQuery dialog and compiled into a bookmarklet while the back-end is another method in the [FWK Runtime] business service (BS).
The idea is simple: to find a workspace record in [Repository Workspace] BusComp by name and run OpenWS and PreviewWS methods.
Here is how your BS method might look like:
function InspectWS(Inputs, Outputs) {
var name = Inputs.GetProperty("Name");
var bo = TheApplication().GetBusObject("Workspace");
var bc = bo.GetBusComp("Repository Workspace");
try {
bc.SetSearchExpr('[Name] = "' + name + '"');
bc.SetViewMode(AllView);
bc.ExecuteQuery(ForwardBackward);
if (bc.FirstRecord()) {
bc.InvokeMethod("OpenWS");
bc.InvokeMethod("PreviewWS");
} else {
throw "Workspace name not found: " + name;
}
} catch (e) {
throw e;
} finally {
bc = null;
bo = null;
}
}
Don't forget to publish your BS through [ClientBusinessService] application user property and probably make it a client-side business service, so you won't have a problem with the upstream migration.
Here is a simplified dialog with a text field where you paste a workspace name and a button to run a BS method:
(() => {
if ("undefined" === typeof SiebelApp) {
alert("It works only in Siebel OUI session!");
return;
}
const func = "SiebelInspectWS";
const html = `<div title="Inspect Workspace"><input type="text" id = "${func}" style="width:100%"></div>`;
const $d = $(html).dialog({
modal: true,
width: 640,
buttons: [{
text: 'Inspect',
click: () => {
const service = SiebelApp.S_App.GetService("FWK Runtime");
let ps = SiebelApp.S_App.NewPropertySet();
ps.SetProperty("Name", $('#' + func).val());
let config = {
async: false,
cb: function (methodName, inputSet, outputSet) {
if (outputSet.GetProperty("Status") == "Error") {
sRes = outputSet.GetChildByType("Errors").GetChild(0).GetProperty("ErrMsg");
}
alert(sRes || "Done!");
}
};
service.InvokeMethod("InspectWS", ps, config);
}
}, {
text: 'Close (Esc)',
click: () => $(this).dialog('close')
}]
});
})();
To open the UI just run the above snippet from the browser console, snippet, file or compile it into a bookmarklet. Make sure you transform/compile it before using in old browsers like IE.
I've advanced the initial version of Inspect Workspace UI with quite a few features, so check out my latest implementation below.
Features:
One of the reasons this approach is so quick, is because it doesn't clear all cache every time. So, remember to clear the cache separately if required. For example, if you created a new view in the workspace, clear the responsibility cache after inspecting it.
Be careful when re-inspecting a workspace from the same view where changes are, it might crash the application. It helps if you step out of the test view before re-inspecting.
Source code (v1.5):
/*
@desc Inspect Workspace UI
@author VB(xapuk.com)
@version 1.5 2020/11/08
@requires "FWK Runtime" business service to be published (Application ClientBusinessService usep property)
@features
+elements: help text hidden by default, input field with the history, message bar, 3 buttons
+don't accept value shorter then 3 chars excluding *
+async call with busy overlay
+highlight search pattern in all found results
+shows a text if number of results = limit
+cut history to 5 items and don't store empty requests
+insect ws on click of <a>
+close on right-click of whitespace
+change a default ws filter to only filter out Delivered WSs
+copy ws name on right-click of link
+make a ws name in a sucess message a link
+put a timestamp in the message
+fix contextmenu on text input
+before opening a dialog check if it exists, and if so run auto inspect
+clicking a ws name inspects the first in the list
+dialog should have a unique selector itself so we don't mess with other dialogs
+print a message before server call, like inspecting ws blabla, or searching for workspace blabla
+use a function to print the message, keep a history of 3 messages
+close when click outside
+make it work and test in IE/Edge/Firefox
+ES6 => Babel => ES5 => Bookmarklet
@Fixed in 1.2:
+print placeholder text on empty call
+don't highlight search specs
+clear results before next search
+fix char limit error
+fix hightlight
+print user name instead of "my"
@Fixed in 1.3:
+placeholder text color
+<> in placeholder text
+when searching for exact ws name, shouldn't highlight it
+link click doesn't work if clicked on highlighted part (<b>)
+don't close on whitespace click
@Fixed in 1.4
+change to the layout
+fixed empty call problem
+instruction changes
@fixed in 1.5
+remove "workspace" from messages so inspecting latest workspace wording make sence
+search results shouldn't be empty - when inspecting should spool a ws name, while search/inspect in progress put ...
+more instructions
*/
(() => {
if ("undefined" === typeof SiebelApp) {
alert("It works only in Siebel OUI session!");
return;
}
// snippet id
const func = "SiebelInspectWS";
// selector preffix
const id = "#" + func;
const cls = "." + func;
// max number of output records
const iLimit = 10;
// history of recent calls
let aHistory = JSON.parse(window.localStorage[func] || "[]");
// messages
let aMsg = [];
// double click of bookmarklet
if ($("." + func).length) {
$("." + func).find(id + "IBtn").click();
return;
}
const placeholder = `${SiebelApp.S_App.GetUserName()||"my"} recent undelivered workspace`;
const help = `<i><p>Welcome to Inspect Workspace UI</p>
Text field accepts several formats:<br>
<ul><li> - an exact workspace name: vbabkin_20200924_d419_1</li>
<li> - a search pattern of workspace name: *d419*</li>
<li> - an exact search spec for Repository Workspace BC: [Parent Name] = "Release 21" AND [Created By] = LoginId()</li>
<li> - leave it empty to search / inspect most recent undelivered workspaces created by active user</li></ul>
<p>Hit Enter to search for 10 most recent workspaces matching the provided name/pattern/spec and then click one of the workspaces in the list to inspect it.</p>
<p>Hit Ctrl+Enter to inspect the most recent workspaces matching the provided name/pattern/spec.</p>
<p>If you want to inspect/re-inspect your recent undelivered workspace, just hit Ctrl+Enter upon opening a dialog or double click a bookmark link.</p>
<p>Right click on workspace name to copy it.</p>
<p>Click anywhere outside of the dialog to close it.</p>
<p>Check out <a href="http://xapuk.com/index.php?topic=125" target="_blank">http://xapuk.com/</a> for details.</p></i>`;
const html = `<div title="Inspect workspace">
<span id = "${func}Help" style = "display:none">${help}</span>
<input placeholder = "<${placeholder}>" type="text" id = "${func}" list="${func}History" autocomplete="off">
<ul id="${func}List">Provide a search criteria above and run [Search] to see a list of available workspaces<br>and/or run [Inspect] directly to inspect the most recent workspace matching the criteria</ul>
<p id = "${func}Msg"></p>
<datalist id = "${func}History"></datalist>
<style>
.${func} input {
width: 100%!Important;
margin-bottom: 10px;
}
#${func}::placeholder {
color: lightslategrey;
}
#${func}List {
margin-left: 15px;
}
#${func}Help i {
font-size: 0.9rem;
}
.${func} li {
list-style-type: disc;
margin-left: 30px;
}
#${func}Msg {
border-top: 1px solid lightslategrey;
padding-top: 5px;
}
</style>
</div>`;
const $d = $(html).dialog({
modal: true,
width: 640,
classes: {
"ui-dialog": func
},
buttons: [{
text: 'Search (Enter)',
click: () => Run(false)
}, {
id: func + "IBtn",
text: 'Inspect (Ctrl+Enter)',
click: () => Run(true)
}, {
text: 'Help',
click: () => $d.find(id + "Help").toggle()
}, {
text: 'Close (Esc)',
click: () => $d.dialog('close')
}],
open: function () {
const $this = $(this);
// autofocus
$this.find('#' + func).focus();
// key bindings
$this.parent(".ui-dialog").contextmenu(function (e) {
const scope = e.target;
if (scope.nodeName === "A") {
// copy value on right-click of link
e.stopPropagation();
e.preventDefault();
// replace link with an input
$(scope).hide().after(`<input id='${func}Copy'>`);
$d.find(id + "Copy").val($(scope).text()).select();
// attempt to copy value
if (document.execCommand("copy", false, null)) {
// if copied, display a message for a second
$d.find(id + "Copy").attr("disabled", "disabled").css("color", "red").val("Copied!");
setTimeout(() => {
$d.find(id + "Copy").remove();
$(scope).show();
}, 700);
} else {
// if failed to copy, keep input element until blur, so it can be copied manually
$d.find(id + "Copy").blur(() => {
$(this).remove();
$d.find("a").show();
});
}
}
}).click((e) => {
var a = $(e.target).closest("a");
if (a.length && a.closest(id + "List").length) {
Run(true, a.text());
}
}).find(id).keydown((event) => {
if (event.keyCode === 13) {
Run(event.ctrlKey);
}
});
// close dialog when click outside
$('.ui-widget-overlay').click(() => $d.dialog("close"));
// render history
aHistory.forEach((i) => $this.find(id + "History").append(`<option>${i}</option>`));
},
close: () => {
$d.dialog('destroy').remove();
}
});
function Run(bInspect, inpname) {
const name = inpname ? inpname : $('#' + func).val();
// don't accept specs shorter then 3 chars
if (name && name.replace(/\*/gm, "").length < 3) {
printMsg(`Value should be longer then 3 characters! ${name}`);
return;
}
//clean up results before search
if (!bInspect) {
$d.find(id + "List").empty();
}
// save last query
if (name) {
if (aHistory.indexOf(name) > -1) {
aHistory.splice(name, 1);
}
aHistory.unshift(name);
// limit history stack volume to 5
if (aHistory.length > 5) {
aHistory.pop();
}
window.localStorage[func] = JSON.stringify(aHistory);
}
// invoke BS
const service = SiebelApp.S_App.GetService("FWK Runtime");
let ps = SiebelApp.S_App.NewPropertySet();
ps.SetProperty("Name", name);
ps.SetProperty("Inspect", bInspect ? "Y" : "N");
ps.SetProperty("Limit", iLimit);
let config = {
async: true,
scope: this,
mask: true,
cb: function (methodName, inputSet, outputSet) {
if (outputSet.GetProperty("Status") == "Error") {
sRes = outputSet.GetChildByType("Errors").GetChild(0).GetProperty("ErrMsg");
} else {
let psRS = outputSet.GetChildByType("ResultSet");
if (psRS) {
sRes = psRS.GetProperty("Result");
sWorkspaces = psRS.GetProperty("Workspaces");
if (!sRes) {
if (bInspect) {
sRes = `Workspace <b><a href='#'>${sWorkspaces||"?"}</a></b> inspected successfully!`;
} else {
if (sWorkspaces) {
// print a list of workspaces
$d.find(id + "List").empty();
let aWorkspaces = sWorkspaces.split(",");
aWorkspaces.forEach((w) => $d.find(id + "List").append(`<li><a href='#'>${highlightText(name, w)}</a></li>`));
if (aWorkspaces.length == iLimit) {
$d.find(id + "List").append(`<p><i>${iLimit} most recent workspaces are shown.</i></p>`);
}
}
}
} else if (sRes.indexOf("No workspace found") > -1) {
$d.find(id + "List").html(`Workspace not found, please provide a valid search criteria and run [Search] again...`);
}
}
}
if (sRes) {
printMsg(sRes);
}
}
};
printMsg(`${bInspect?'Inspecting':'Searching for'} ${name||placeholder}`);
service.InvokeMethod("InspectWS", ps, config);
}
function highlightText(pattern, value) {
if (pattern && value && !pattern.match(/\[.*\]/gm) && pattern.replace(/\*/gm, "").length < value.length) {
const patterns = pattern.split("*");
let i, lastIndex = -1;
value = patterns.reduce((res, p) => {
let i = res.indexOf(p, lastIndex);
if (p && i > -1) {
res = `${res.substr(0, i)}<b>${p}</b>${res.substr(i + p.length)}`;
lastIndex = i;
}
return res;
}, value);
}
return value;
}
function printMsg(txt) {
txt = (new Date).toLocaleTimeString() + ' >> ' + txt;
// limit a message stack to 3 items
aMsg.push(txt);
if (aMsg.length > 3) {
aMsg.shift();
}
$d.find(id + "Msg").html(aMsg.join("<br>"));
}
})();
javascript:(function(){function e(e,a){var c=a?a:$("#"+n).val();if(c&&c.replace(/\*/gm,"").length<3)return void i("Value should be longer then 3 characters! "+c);e||d.find(r+"List").empty(),c&&(s.indexOf(c)>-1&&s.splice(c,1),s.unshift(c),s.length>5&&s.pop(),window.localStorage[n]=JSON.stringify(s));var l=SiebelApp.S_App.GetService("FWK Runtime"),u=SiebelApp.S_App.NewPropertySet();u.SetProperty("Name",c),u.SetProperty("Inspect",e?"Y":"N"),u.SetProperty("Limit",o);var f={async:!0,scope:this,mask:!0,cb:function(n,s,a){if("Error"==a.GetProperty("Status"))sRes=a.GetChildByType("Errors").GetChild(0).GetProperty("ErrMsg");else{var p=a.GetChildByType("ResultSet");if(p)if(sRes=p.GetProperty("Result"),sWorkspaces=p.GetProperty("Workspaces"),sRes)sRes.indexOf("No workspace found")>-1&&d.find(r+"List").html("Workspace not found, please provide a valid search criteria and run [Search] again...");else if(e)sRes="Workspace <b><a href='#'>"+(sWorkspaces||"?")+"</a></b> inspected successfully!";else if(sWorkspaces){d.find(r+"List").empty();var l=sWorkspaces.split(",");l.forEach(function(e){return d.find(r+"List").append("<li><a href='#'>"+t(c,e)+"</a></li>")}),l.length==o&&d.find(r+"List").append("<p><i>"+o+" most recent workspaces are shown.</i></p>")}}sRes&&i(sRes)}};i((e?"Inspecting":"Searching for")+" "+(c||p)),l.InvokeMethod("InspectWS",u,f)}function t(e,t){return e&&t&&!e.match(/\[.*\]/gm)&&e.replace(/\*/gm,"").length<t.length&&function(){var i=e.split("*"),n=-1;t=i.reduce(function(e,t){var i=e.indexOf(t,n);return t&&i>-1&&(e=e.substr(0,i)+"<b>"+t+"</b>"+e.substr(i+t.length),n=i),e},t)}(),t}function i(e){e=(new Date).toLocaleTimeString()+" >> "+e,a.push(e),a.length>3&&a.shift(),d.find(r+"Msg").html(a.join("<br>"))}if("undefined"==typeof SiebelApp)return void alert("It works only in Siebel OUI session!");var n="SiebelInspectWS",r="#"+n,o=10,s=JSON.parse(window.localStorage[n]||"[]"),a=[];if($("."+n).length)return void $("."+n).find(r+"IBtn").click();var p=(SiebelApp.S_App.GetUserName()||"my")+" recent undelivered workspace",c='<i><p>Welcome to Inspect Workspace UI</p>Text field accepts several formats:<br><ul><li> - an exact workspace name: vbabkin_20200924_d419_1</li><li> - a search pattern of workspace name: *d419*</li><li> - an exact search spec for Repository Workspace BC: [Parent Name] = "Release 21" AND [Created By] = LoginId()</li><li> - leave it empty to search / inspect most recent undelivered workspaces created by active user</li></ul><p>Hit Enter to search for 10 most recent workspaces matching the provided name/pattern/spec and then click one of the workspaces in the list to inspect it.</p><p>Hit Ctrl+Enter to inspect the most recent workspaces matching the provided name/pattern/spec.</p><p>If you want to inspect/re-inspect your recent undelivered workspace, just hit Ctrl+Enter upon opening a dialog or double click a bookmark link.</p><p>Right click on workspace name to copy it.</p><p>Click anywhere outside of the dialog to close it.</p><p>Check out <a href="http://xapuk.com/index.php?topic=125" target="_blank">http://xapuk.com/</a> for details.</p></i>',l='<div title="Inspect workspace"><span id = "'+n+'Help" style = "display:none">'+c+'</span><input placeholder = "<'+p+'>" type="text" id = "'+n+'" list="'+n+'History" autocomplete="off"><ul id="'+n+'List">Provide a search criteria above and run [Search] to see a list of available workspaces<br>and/or run [Inspect] directly to inspect the most recent workspace matching the criteria</ul><p id = "'+n+'Msg"></p><datalist id = "'+n+'History"></datalist><style>.'+n+" input {width: 100%!Important;margin-bottom: 10px;}#"+n+"::placeholder {color: lightslategrey;}#"+n+"List {margin-left: 15px;}#"+n+"Help i {font-size: 0.9rem;}."+n+" li {list-style-type: disc;margin-left: 30px;}#"+n+"Msg {border-top: 1px solid lightslategrey;padding-top: 5px;}</style></div>",d=$(l).dialog({modal:!0,width:640,classes:{"ui-dialog":n},buttons:[{text:"Search (Enter)",click:function(){return e(!1)}},{id:n+"IBtn",text:"Inspect (Ctrl+Enter)",click:function(){return e(!0)}},{text:"Help",click:function(){return d.find(r+"Help").toggle()}},{text:"Close (Esc)",click:function(){return d.dialog("close")}}],open:function(){var t=$(this);t.find("#"+n).focus(),t.parent(".ui-dialog").contextmenu(function(e){var t=this,i=e.target;"A"===i.nodeName&&(e.stopPropagation(),e.preventDefault(),$(i).hide().after("<input id='"+n+"Copy'>"),d.find(r+"Copy").val($(i).text()).select(),document.execCommand("copy",!1,null)?(d.find(r+"Copy").attr("disabled","disabled").css("color","red").val("Copied!"),setTimeout(function(){d.find(r+"Copy").remove(),$(i).show()},700)):d.find(r+"Copy").blur(function(){$(t).remove(),d.find("a").show()}))}).click(function(t){var i=$(t.target).closest("a");i.length&&i.closest(r+"List").length&&e(!0,i.text())}).find(r).keydown(function(t){13===t.keyCode&&e(t.ctrlKey)}),$(".ui-widget-overlay").click(function(){return d.dialog("close")}),s.forEach(function(e){return t.find(r+"History").append("<option>"+e+"</option>")})},close:function(){d.dialog("destroy").remove()}})})();
Thanks to Manan for his contribution!
Another playground for you Guys. This time it is to evaluate a "calc field" expressions. The official name of the syntax is Siebel Query Language and it is used widely through Siebel apps, for example:
A playground is very handy when it comes to prototyping or debugging expressions or simply if you need to get a value of an active field or profile attribute.
Less words more code, let's get down to the implementation.
This time we will be invoking a built-in BusComp method EvalExpr. So let's create another method in runtime business service:
function EvalExpr (Inputs, Outputs) {
var bc:BusComp;
try {
bc = TheApplication().ActiveBusObject().GetBusComp(Inputs.GetProperty("BC"));
Outputs.SetProperty("Result", bc.InvokeMethod("EvalExpr", Inputs.GetProperty("Expr")));
} catch(e) {
Outputs.SetProperty("Result", e.toString());
} finally {
bc = null;
}
}
Here is a full version of my client-side Business Service.
For UI we will be using a bookmarklet with a dialog which contains a drop-down with active BusComps, two text areas for input expression and a result, and a button to run the service.
/*
@desc UI allowing to evaluate expressions (EvalExpr) on active BCs
@author VB(xapuk.com)
@version 1.2 2018/07/23
@requires BS "FWK Runtime" to be published
*/
if ("undefined" == typeof SiebelApp){
alert("It works only in Siebel OUI session!");
}else{
var func = "SiebelEvalExpr";
$("#" + func).parent().remove();
var a = LoadBCs();
if (a.length === 0){
alert("No BusComps/Records available!");
}else{
var s = '<div title="Runtime calculations">' +
'<label for="' + func + 'List">Business Component:</label>' +
'<select id = "' + func + 'List" style="display:block"></select>' +
'<label for="' + func + '">Expression:</label>' +
'<textarea id = "' + func + '" rows="3"></textarea>' +
'<label for="' + func + 'Out">Results:</label>' +
'<textarea id = "' + func + 'Out" disabled rows="2"></textarea>' +
'<style>select,textarea{width:100%!Important}</style>' +
'</div>';
var d = $(s).dialog({
modal: true,
width: 1024,
heigth: 640,
open: function(){
$('#'+func).focus();
// key bindings
$("#"+func+"Out").parent().keydown(function(event) {
if (event.ctrlKey && event.keyCode === 13) { // ctrl + Enter
EvalExpr();
}
});
// list of BCs
$("#" + func + "List").append("<option>" + a.join("</option><option>") + "</option>");
$("#" + func + "List").val(SiebelApp.S_App.GetActiveView().GetActiveApplet().GetBusComp().GetName());
// recent expression
$("#" + func).val(JSON.parse(window.localStorage[func]));
//style
$(this).append('<style type="text/css">textarea, select { height:auto; width:100% }</style>');
},
close: function(){
$(this).dialog('destroy').remove();
},
buttons: [
{
text:'Run (Ctrl+Enter)',
click: EvalExpr
},
{
text:'Close (Esc)',
click: function() {
$(this).dialog('destroy').remove();
}
}
]
});
// bind and trigger auto-adjust
$(d).find("#" + func).keyup(function(){
TextAdjust(this, 3);
}).keyup();
}
}
function EvalExpr(){
var sExpr = $('#'+func).val();
var sRes = "";
// save last query
window.localStorage[func] = JSON.stringify(sExpr);
// if there is a selection
var ele = document.getElementById(func);
if(ele.selectionStart !== undefined && ele.selectionStart != ele.selectionEnd){// Normal browsers
sExpr = ele.value.substring(ele.selectionStart, ele.selectionEnd);
}else if(document.selection !== undefined){// IE
ele.focus();
var sel = document.selection.createRange();
sExpr = sel.text;
}
// invoke BS
var service = SiebelApp.S_App.GetService("FWK Runtime");
var ps = SiebelApp.S_App.NewPropertySet();
ps.SetProperty("Expr", sExpr);
ps.SetProperty("BC", $("#" + func + "List").val());
var outputSet = service.InvokeMethod("EvalExpr", ps);
if (outputSet.GetProperty("Status") == "Error"){
sRes = outputSet.GetChildByType("Errors").GetChild(0).GetProperty("ErrMsg");
}else{
sRes = outputSet.GetChildByType("ResultSet").GetProperty("Result");
console.log(outputSet);
}
TextAdjust($('#'+func + "Out").show().text(sRes)[0]);
}
// auto-ajust textarea height
function TextAdjust(scope, minrows, maxrows) {
maxrows = maxrows>0?maxrows:10;
minrows = minrows>0?minrows:2;
var txt = scope.value;
var cols = scope.cols;
var arraytxt = txt.split('\n');
var rows = arraytxt.length;
if (rows > maxrows) {
scope.rows = maxrows;
} else if (rows < minrows) {
scope.rows = minrows;
} else {
scope.rows = rows;
}
}
// gather the list of active BCs
function LoadBCs(){
var a = [];
for(var i in SiebelApp.S_App.GetActiveBusObj().GetBCMap()){
var bc = SiebelApp.S_App.GetActiveBusObj().GetBCMap()[i];
if (a.indexOf(bc.GetName()) == -1 && bc.GetNumRows() > 0){
a.push(bc.GetName());
}
}
return a;
}
javascript:void function(){function e(){var e=$("#"+i).val(),o="";window.localStorage[i]=JSON.stringify(e);var l=document.getElementById(i);if(void 0!==l.selectionStart&&l.selectionStart!=l.selectionEnd)e=l.value.substring(l.selectionStart,l.selectionEnd);else if(void 0!==document.selection){l.focus();var r=document.selection.createRange();e=r.text}var s=SiebelApp.S_App.GetService("FWK Runtime"),a=SiebelApp.S_App.NewPropertySet();a.SetProperty("Expr",e),a.SetProperty("BC",$("#"+i+"List").val());var n=s.InvokeMethod("EvalExpr",a);"Error"==n.GetProperty("Status")?o=n.GetChildByType("Errors").GetChild(0).GetProperty("ErrMsg"):(o=n.GetChildByType("ResultSet").GetProperty("Result"),console.log(n)),t($("#"+i+"Out").show().text(o)[0])}function t(e,t,o){o=o>0?o:10,t=t>0?t:2;var i=e.value,l=(e.cols,i.split("\n")),r=l.length;r>o?e.rows=o:t>r?e.rows=t:e.rows=r}function o(){var e=[];for(var t in SiebelApp.S_App.GetActiveBusObj().GetBCMap()){var o=SiebelApp.S_App.GetActiveBusObj().GetBCMap()[t];-1==e.indexOf(o.GetName())&&o.GetNumRows()>0&&e.push(o.GetName())}return e}if("undefined"==typeof SiebelApp)alert("It works only in Siebel OUI session!");else{var i="SiebelEvalExpr";$("#"+i).parent().remove();var l=o();if(0===l.length)alert("No BusComps/Records available!");else{var r='<div title="Runtime calculations"><label for="'+i+'List">Business Component:</label><select id = "'+i+'List" style="display:block"></select><label for="'+i+'">Expression:</label><textarea id = "'+i+'" rows="3"></textarea><label for="'+i+'Out">Results:</label><textarea id = "'+i+'Out" disabled rows="2"></textarea><style>select,textarea{width:100%!Important}</style></div>',s=$(r).dialog({modal:!0,width:1024,heigth:640,open:function(){$("#"+i).focus(),$("#"+i+"Out").parent().keydown(function(t){t.ctrlKey&&13===t.keyCode&&e()}),$("#"+i+"List").append("<option>"+l.join("</option><option>")+"</option>"),$("#"+i+"List").val(SiebelApp.S_App.GetActiveView().GetActiveApplet().GetBusComp().GetName()),$("#"+i).val(JSON.parse(window.localStorage[i])),$(this).append('<style type="text/css">textarea, select { height:auto; width:100% }</style>')},close:function(){$(this).dialog("destroy").remove()},buttons:[{text:"Run (Ctrl+Enter)",click:e},{text:"Close (Esc)",click:function(){$(this).dialog("destroy").remove()}}]});$(s).find("#"+i).keyup(function(){t(this,3)}).keyup()}}}();
Here is a new version with a built-in beautifier from another topic:
/*
@desc UI allowing to evaluate expressions (EvalExpr) on active BCs
@author VB(xapuk.com)
@version 1.3 2019/03/10
@requires BS "FWK Runtime" to be compiled and published
*/
if ("undefined" == typeof SiebelApp){
alert("It works only in Siebel OUI session!");
} else {
var func = "SiebelEvalExpr";
$("#" + func).parent().remove();
var bBeauty = false;
var a = LoadBCs();
if (a.length === 0){
alert("No BusComps/Records available!");
}else{
var s = '<div title="Runtime calculations">' +
'<label for="' + func + 'List">Business Component:</label>' +
'<select id = "' + func + 'List" style="display:block"></select>' +
'<label for="' + func + '">Expression:</label>' +
'<textarea id = "' + func + '" rows="5" nowrap></textarea>' +
'<label for="' + func + 'Out">Results:</label>' +
'<textarea id = "' + func + 'Out" disabled rows="2"></textarea>' +
'<style type="text/css">.ui-dialog textarea, .ui-dialog select {height:auto; width:100%; margin-bottom:10px} .ui-dialog label{margin-top:20px!}</style>'
'</div>';
var d = $(s).dialog({
modal: true,
width: 1024,
heigth: 640,
open: function(){
$('#'+func).focus();
// key bindings
$("#"+func+"Out").parent().keydown(function(event) {
if (event.ctrlKey && event.keyCode === 13) { // ctrl + Enter
EvalExpr();
}
});
// list of BCs
$("#" + func + "List").append("<option>" + a.join("</option><option>") + "</option>");
$("#" + func + "List").val(SiebelApp.S_App.GetActiveView().GetActiveApplet().GetBusComp().GetName());
// recent expression
$("#" + func).val(JSON.parse(window.localStorage[func]));
},
close: function(){
$(this).dialog('destroy').remove();
},
buttons: [
{
text:'Format/Linarise',
click: Beauty,
id: 'BeautyBtn'
},{
text:'Run (Ctrl+Enter)',
click: EvalExpr
},{
text:'Close (Esc)',
click: function() {
$(this).dialog('destroy').remove();
}
}
]
});
// bind and trigger auto-adjust
$(d).find("#" + func).keyup(function(){
TextAdjust(this, 5);
}).keyup();
// bind a beautifier
$(".ui-dialog #BeautyBtn").hide();
require(["3rdParty/SiebelQueryLang"], function(e){
console.log("Beautifier loaded!");
$(".ui-dialog #BeautyBtn").show();
});
}
}
function Beauty(){
var s = $('#'+func).val();
if (s){
if (bBeauty){
// linarise
s = s.replace(/\n(\t|\s)*/gm,"");
$('#'+func).val(s).attr("wrap", "on");
bBeauty = false;
} else {
// beautify
try {
var o = SiebelQueryLang.parse(s);
s = trav(o.expression, "");
$('#'+func).val(s).attr("wrap", "off");
bBeauty = true;
} catch(e) {
// silence the error
console.log(e);
}
}
TextAdjust($('#'+func)[0]);
}
}
function trav(o, t, f) {
var r = "";
if ("object" === typeof o) {
var p = o.par;
var n = o.not;
if (o.type === "bin") {
r = trav(o.left, t) + " " + o.operator + " " + trav(o.right, t);
} else if (o.type === "log") {
if(p) { // format logical operators eclosed in brackets
tt = t + "\t";
r = "(\n";
r += tt + trav(o.left, tt, true);
r += "\n" + tt + o.operator + " " + trav(o.right, tt, true);
r += "\n" + t + ")";
p = false;
} else {
if(f) {
r = trav(o.left, t, true);
r += "\n" + t + o.operator + " " + trav(o.right, t, true);
} else {
r = trav(o.left, t) + " " + o.operator + " " + trav(o.right, t);
}
}
} else if (o.type === "func") {
var l = o.arguments.length;
var f = l > 2; // split params when more then 2
var s = (f ? "\n" + t : "");
var st = (f ? s + "\t" : "");
r = o.name + "(";
o.arguments.forEach(function(a, i) {
r += st + trav(a, t + "\t") + (i < l - 1 ? ", " : "");
});
r += s + ")";
} else if (o.type === "field") {
r = "[" + o.field + "]";
} else if (o.type === "param") {
r = "[&" + o.param + "]";
} else if (o.type === "num") {
r = o.value;
} else if (o.type === "str") {
r = '"' + o.value +'"';
}
if (p) {
r = "(" + r + ")";
}
if (n) {
r = "NOT " + r;
}
} else {
r = o;
}
return r;
}
function EvalExpr(){
var sExpr = $('#'+func).val();
var sRes = "";
// save last query
window.localStorage[func] = JSON.stringify(sExpr);
// if there is a selection
var ele = document.getElementById(func);
if(ele.selectionStart !== undefined && ele.selectionStart != ele.selectionEnd){// Normal browsers
sExpr = ele.value.substring(ele.selectionStart, ele.selectionEnd);
}else if(document.selection !== undefined){// IE
ele.focus();
var sel = document.selection.createRange();
sExpr = sel.text;
}
// invoke BS
var service = SiebelApp.S_App.GetService("FWK Runtime");
var ps = SiebelApp.S_App.NewPropertySet();
ps.SetProperty("Expr", sExpr);
ps.SetProperty("BC", $("#" + func + "List").val());
var outputSet = service.InvokeMethod("EvalExpr", ps);
if (outputSet.GetProperty("Status") == "Error"){
sRes = outputSet.GetChildByType("Errors").GetChild(0).GetProperty("ErrMsg");
}else{
sRes = outputSet.GetChildByType("ResultSet").GetProperty("Result");
console.log(outputSet);
}
TextAdjust($('#'+func + "Out").show().text(sRes)[0]);
}
// auto-ajust textarea height
function TextAdjust(scope, minrows, maxrows) {
maxrows = maxrows>0?maxrows:30;
minrows = minrows>0?minrows:5;
var txt = scope.value;
var cols = scope.cols;
var arraytxt = txt.split('\n');
var rows = arraytxt.length;
if (rows > maxrows) {
scope.rows = maxrows;
} else if (rows < minrows) {
scope.rows = minrows;
} else {
scope.rows = rows;
}
}
// put active BC in the list with active applet's bc as selected
function LoadBCs(){
var a = [];
for(var i in SiebelApp.S_App.GetActiveBusObj().GetBCMap()){
var bc = SiebelApp.S_App.GetActiveBusObj().GetBCMap()[i];
if (a.indexOf(bc.GetName()) == -1 && bc.GetNumRows() > 0){
a.push(bc.GetName());
}
}
return a;
}
javascript:void function(){function e(){var e=$("#"+a).val();if(e){if(l)e=e.replace(/\n(\t|\s)*/gm,""),$("#"+a).val(e).attr("wrap","on"),l=!1;else try{var o=SiebelQueryLang.parse(e);e=t(o.expression,""),$("#"+a).val(e).attr("wrap","off"),l=!0}catch(r){console.log(r)}i($("#"+a)[0])}}function t(e,o,i){var r="";if("object"==typeof e){var a=e.par,l=e.not;if("bin"===e.type)r=t(e.left,o)+" "+e.operator+" "+t(e.right,o);else if("log"===e.type)a?(tt=o+" ",r="(\n",r+=tt+t(e.left,tt,!0),r+="\n"+tt+e.operator+" "+t(e.right,tt,!0),r+="\n"+o+")",a=!1):i?(r=t(e.left,o,!0),r+="\n"+o+e.operator+" "+t(e.right,o,!0)):r=t(e.left,o)+" "+e.operator+" "+t(e.right,o);else if("func"===e.type){var n=e.arguments.length,i=n>2,s=i?"\n"+o:"",p=i?s+" ":"";r=e.name+"(",e.arguments.forEach(function(e,i){r+=p+t(e,o+" ")+(n-1>i?", ":"")}),r+=s+")"}else"field"===e.type?r="["+e.field+"]":"param"===e.type?r="[&"+e.param+"]":"num"===e.type?r=e.value:"str"===e.type&&(r='"'+e.value+'"');a&&(r="("+r+")"),l&&(r="NOT "+r)}else r=e;return r}function o(){var e=$("#"+a).val(),t="";window.localStorage[a]=JSON.stringify(e);var o=document.getElementById(a);if(void 0!==o.selectionStart&&o.selectionStart!=o.selectionEnd)e=o.value.substring(o.selectionStart,o.selectionEnd);else if(void 0!==document.selection){o.focus();var r=document.selection.createRange();e=r.text}var l=SiebelApp.S_App.GetService("FWK Runtime"),n=SiebelApp.S_App.NewPropertySet();n.SetProperty("Expr",e),n.SetProperty("BC",$("#"+a+"List").val());var s=l.InvokeMethod("EvalExpr",n);"Error"==s.GetProperty("Status")?t=s.GetChildByType("Errors").GetChild(0).GetProperty("ErrMsg"):(t=s.GetChildByType("ResultSet").GetProperty("Result"),console.log(s)),i($("#"+a+"Out").show().text(t)[0])}function i(e,t,o){o=o>0?o:30,t=t>0?t:5;var i=e.value,r=(e.cols,i.split("\n")),a=r.length;a>o?e.rows=o:t>a?e.rows=t:e.rows=a}function r(){var e=[];for(var t in SiebelApp.S_App.GetActiveBusObj().GetBCMap()){var o=SiebelApp.S_App.GetActiveBusObj().GetBCMap()[t];-1==e.indexOf(o.GetName())&&o.GetNumRows()>0&&e.push(o.GetName())}return e}if("undefined"==typeof SiebelApp)alert("It works only in Siebel OUI session!");else{var a="SiebelEvalExpr";$("#"+a).parent().remove();var l=!1,n=r();if(0===n.length)alert("No BusComps/Records available!");else{var s='<div title="Runtime calculations"><label for="'+a+'List">Business Component:</label><select id = "'+a+'List" style="display:block"></select><label for="'+a+'">Expression:</label><textarea id = "'+a+'" rows="5" nowrap></textarea><label for="'+a+'Out">Results:</label><textarea id = "'+a+'Out" disabled rows="2"></textarea><style type="text/css">.ui-dialog textarea, .ui-dialog select {height:auto; width:100%; margin-bottom:10px} .ui-dialog label{margin-top:20px!}</style>',p=$(s).dialog({modal:!0,width:1024,heigth:640,open:function(){$("#"+a).focus(),$("#"+a+"Out").parent().keydown(function(e){e.ctrlKey&&13===e.keyCode&&o()}),$("#"+a+"List").append("<option>"+n.join("</option><option>")+"</option>"),$("#"+a+"List").val(SiebelApp.S_App.GetActiveView().GetActiveApplet().GetBusComp().GetName()),$("#"+a).val(JSON.parse(window.localStorage[a]))},close:function(){$(this).dialog("destroy").remove()},buttons:[{text:"Format/Linarise",click:e,id:"BeautyBtn"},{text:"Run (Ctrl+Enter)",click:o},{text:"Close (Esc)",click:function(){$(this).dialog("destroy").remove()}}]});$(p).find("#"+a).keyup(function(){i(this,5)}).keyup(),$(".ui-dialog #BeautyBtn").hide(),require(["3rdParty/SiebelQueryLang"],function(e){console.log("Beautifier loaded!"),$(".ui-dialog #BeautyBtn").show()})}}}();
What if you can run eScript code without even opening Siebel Tools. Right from the Siebel client.
Need to code a new service? Click your favourite bookmarklet button and prototype it immediately, no need of recompilations/restarts.
Need to hook into a current BO context and manipulate active BCs? Easy!
Before I start, I have to say this idea haven't visited my head first. If you need a full immersion you should ask my masterminds - Jason or Roy.
So, here is how you build a lightweight eScript interactive playground.
As you may have guessed, it is all around eval() function. First step is to make it invokable from the browser:
1. Create a Business Service, as simple as this one:
function Service_PreInvokeMethod (MethodName, Inputs, Outputs) {
if (MethodName == “EvalScript”){
try {
Outputs.SetProperty(“Result”, eval(Inputs.GetProperty(“Expr”)));
} catch(e) {
Outputs.SetProperty(“Result”, e.toString());
}
}
return (CancelOperation);
}
A little trick here is to create the BS as a client-side business service, so it wouldn't be a part of repository => neither part of regular migrations => no security bleach on production.
2. Publish the service, so it can be accessible from a browser:
You will need a dialog with input and output text areas and a button to run the BS. Here is how your JS will look like:
// dialog html
var s = '<div title="eScript">'
+ '<textarea id = "SiebelEvalScript" style="height:150px"></textarea>'
+ '<textarea id = "SiebelEvalScriptOut" rows="4" disabled></textarea>'
+ '<style>textarea{width:100%!Important}</style>'
+ '</div>';
// display dialog
$(s).dialog({
modal: true,
width: 1024,
buttons: [{text:'Run', click: Eval}]
});
// run Business Service
function Eval(){
var sRes = "";
var ps = SiebelApp.S_App.NewPropertySet();
ps.SetProperty("Expr", $('#SiebelEvalScript').val());
var outputSet = SiebelApp.S_App.GetService("FWK Runtime").InvokeMethod("EvalScript", ps);
if (outputSet.GetProperty("Status") == "Error"){
sRes = outputSet.GetChildByType("Errors").GetChild(0).GetProperty("ErrMsg");
}else{
sRes = outputSet.GetChildByType("ResultSet").GetProperty("Result");
}
$('#SiebelEvalScriptOut').text(sRes);
}
Try it in browser console or compile into a bookmarklet.
Don't have time building your own playground? Try mine, it is free and has some advanced features you might enjoy:
Front: Source code, Bookmarklet code, Bookmarklet
/*
@desc Framework allowing to run/evaluate eScript code
@author VB(xapuk.com)
@version 1.3 2018/12/05
@requires BS=FWK Runtime to be published
*/
if ("undefined" == typeof SiebelApp){
alert("It works only in Siebel OUI session!");
}else{
var editor; // AceJS editor object
var func = "SiebelEvalScript"; // function identifier
var snip; // an array of saved snippets
var last; // current snippet name
// dialog html
var s = '<div title="eScript">'
+ '<select id = "' + func + 'List" style="display:block"><option value="*">New...</option></select>'
+ '<textarea id = "' + func + '" placeholder="eSciript code..." style="height:150px"></textarea>'
+ '<label id = "' + func + '_lbl" for="' + func + '">Initialised</label>'
+ '<textarea id = "' + func + 'Out" rows="4" disabled></textarea>'
+ '<style>select,textarea{width:100%!Important}.ui-dialog-content{padding:0.5em 1em}</style>'
+ '</div>';
// hard-remove dialog object from DOM, just in case
$("#"+func + "List").parent().remove();
var d = $(s).dialog({
modal: true,
width: 1024,
open: function(){
$('#'+func).focus();
// load acejs plugin
if (typeof(ace) == "undefined"){
// injecting a script tag, also you can use require() function instead
var jsCode = document.createElement('script');
jsCode.setAttribute('src', "https://cdnjs.cloudflare.com/ajax/libs/ace/1.4.2/ace.js");
jsCode.setAttribute('async', true);
jsCode.onload = attachACE;
document.body.appendChild(jsCode);
}else{
attachACE();
}
// List onchange
$("#"+func+"List").change(function(event) {
var n = $(this).val();
if (n != "*" && n > ""){
if (editor){
editor.setValue(snip[n]);
}else{
$("#"+func).text(snip[n]);
}
window.localStorage[func+"Last"] = n;
}
});
// key bindings
$("#"+func+"Out").parent().keydown(function(event) {
if (event.ctrlKey && event.keyCode === 13) { // ctrl + Enter
Eval();
return false;
}else if (event.ctrlKey && event.keyCode === 83) { // ctrl + S
Save();
return false;
}
});
Load(); // load presaved params
},
close: function(){
$(this).dialog('destroy').remove();
},
buttons: [
{
text:'Run (Ctrl+Enter)',
click: Eval
},
{
text:'Save (Ctrl + S)',
click: Save
},
{
text:'Remove',
click: Delete
},
{
text:'Close (Esc)',
click: function() {
$(this).dialog('close');
}
}
]
});
}
function Eval(){
var sExpr = GetCode();
var sRes = "";
var dTS = new Date();
var isChrome = !!window.chrome;
var isFirefox = typeof InstallTrigger !== 'undefined';
// execution timestamp
$('#'+func + "_lbl").text("Last executed at " + dTS.toISOString().replace("T", " ").replace("Z", " "));
Save(); // save snippets every time you run it
// invoke BS
var service = SiebelApp.S_App.GetService("FWK Runtime");
var ps = SiebelApp.S_App.NewPropertySet();
ps.SetProperty("Expr", sExpr);
var outputSet = service.InvokeMethod("EvalScript", ps);
if (outputSet.GetProperty("Status") == "Error"){
sRes = outputSet.GetChildByType("Errors").GetChild(0).GetProperty("ErrMsg");
}else{
sRes = outputSet.GetChildByType("ResultSet").GetProperty("Result");
}
$('#'+func + "Out").text(sRes);
// show results in browser console
if (console) {
var a = sRes.split(String.fromCharCode(13));
for(var i = 0; i < a.length; i++) {
// split into 3 parts for styling
var a2 = a[i].split('\t|\t');
var s1 = "", s2 = "", s3= "";
if (a2.length > 1){
if (a2.length > 2){
s1 = a2[0];
s2 = a2[1];
for(var j = 2; j < a2.length; j++) {
s3 += "\t" + a2[j];
}
} else {
s1 = a2[0];
s3 = a2[1];
}
} else {
s3 = a[i];
}
// collapse miltiline results
if (s3.indexOf("\n") > -1) {
if (isFirefox || isChrome ) {
console.groupCollapsed("%c" + s1 + " \t%c" + s2, "color:DarkCyan;", "color:Maroon;font-weight:bold");
} else {
console.groupCollapsed(s1 + " \t" + s2);
}
console.log(s3);
console.groupEnd();
} else {
if (isFirefox || isChrome ) {
console.log("%c" + s1 + " \t%c" + s2 + " \t%c" + s3, "color:DarkCyan;", "color:Maroon;font-weight:bold", "color:black;font-weight:normal");
} else {
console.log(s1 + " \t" + s2 + " \t" + s3);
}
}
}
}
}
// attach acejs plugin
function attachACE(){
editor = ace.edit(func);
editor.session.setMode("ace/mode/javascript");
$(".ace_editor").css("height","300");
}
// save button
function Save(){
var n = $('#' + func + "List").val();
if (n == "*" || n == null){ // new
n = prompt("Snippet name");
if(n){
if (n.match(/.{2,}/)){
snip[n] = GetCode(true);
window.localStorage[func] = JSON.stringify(snip);
$('#' + func + "List").append('<option value="' + n + '">' + n +'</option>');
$('#' + func + "List").val(n).change();
}else{
alert("Invalid snippet name!");
}
}
}else{ // existing
snip[n] = GetCode(true);
window.localStorage[func] = JSON.stringify(snip);
}
}
// Remove button
function Delete(){
var n = $('#' + func + "List").val();
if (confirm("Are you sure you want to delete a snippet: " + n)){
if (n && n != "*"){
delete snip[n]; // remove item
window.localStorage[func] = JSON.stringify(snip);
delete window.localStorage[func + "Last"];
Load(); // reload list
}
}
}
// loads preserved code snippets
function Load() {
var s = window.localStorage[func];
// remove all dropdown items
$("#" + func + "List option").remove();
//clear editor
if (editor){
editor.setValue("");
}else{
$("#"+func).text("");
}
// retrieve code snippets saved in local storage
var li = '';
if (s){
snip = JSON.parse(s);
for (k in snip){
li += '<option value="' + k + '">' + k + '</option>';
}
}else{
snip={};
}
$("#" + func + "List").append(li);
//last snippet
last = window.localStorage[func+"Last"];
if(last){
$('#' + func + "List").val(last).change();
}
}
// returns either selected peace of code or full value from text area or ACEJS plugin
function GetCode(bFull)
{
var sRes;
if (editor){
if (bFull || editor.getSelectedText() === ""){
sRes = editor.getValue();
}else{
sRes = editor.getSelectedText();
}
}else{
var textComponent = document.getElementById(func);
if (bFull){
sRes = $('#'+func).val();
}else if(textComponent.selectionStart !== undefined && textComponent.selectionStart != textComponent.selectionEnd){// Normal browsers
sRes = textComponent.value.substring(textComponent.selectionStart, textComponent.selectionEnd);
}else if(document.selection !== undefined){// IE
textComponent.focus();
var sel = document.selection.createRange();
sRes = sel.text;
}else{
sRes = $('#'+func).val();
}
}
return sRes;
}
javascript:void function(){function e(){var e=n(),t="",l=new Date,i=!!window.chrome,a="undefined"!=typeof InstallTrigger;$("#"+s+"_lbl").text("Last executed at "+l.toISOString().replace("T"," ").replace("Z"," ")),o();var r=SiebelApp.S_App.GetService("FWK Runtime"),c=SiebelApp.S_App.NewPropertySet();c.SetProperty("Expr",e);var d=r.InvokeMethod("EvalScript",c);if(t="Error"==d.GetProperty("Status")?d.GetChildByType("Errors").GetChild(0).GetProperty("ErrMsg"):d.GetChildByType("ResultSet").GetProperty("Result"),$("#"+s+"Out").text(t),console)for(var p=t.split(String.fromCharCode(13)),u=0;u<p.length;u++){var f=p[u].split(" | "),g="",v="",S="";if(f.length>1)if(f.length>2){g=f[0],v=f[1];for(var y=2;y<f.length;y++)S+=" "+f[y]}else g=f[0],S=f[1];else S=p[u];S.indexOf("\n")>-1?(a||i?console.groupCollapsed("%c"+g+" %c"+v,"color:DarkCyan;","color:Maroon;font-weight:bold"):console.groupCollapsed(g+" "+v),console.log(S),console.groupEnd()):a||i?console.log("%c"+g+" %c"+v+" %c"+S,"color:DarkCyan;","color:Maroon;font-weight:bold","color:black;font-weight:normal"):console.log(g+" "+v+" "+S)}}function t(){a=ace.edit(s),a.session.setMode("ace/mode/javascript"),$(".ace_editor").css("height","300")}function o(){var e=$("#"+s+"List").val();"*"==e||null==e?(e=prompt("Snippet name"),e&&(e.match(/.{2,}/)?(r[e]=n(!0),window.localStorage[s]=JSON.stringify(r),$("#"+s+"List").append('<option value="'+e+'">'+e+"</option>"),$("#"+s+"List").val(e).change()):alert("Invalid snippet name!"))):(r[e]=n(!0),window.localStorage[s]=JSON.stringify(r))}function l(){var e=$("#"+s+"List").val();confirm("Are you sure you want to delete a snippet: "+e)&&e&&"*"!=e&&(delete r[e],window.localStorage[s]=JSON.stringify(r),delete window.localStorage[s+"Last"],i())}function i(){var e=window.localStorage[s];$("#"+s+"List option").remove(),a?a.setValue(""):$("#"+s).text("");var t="";if(e){r=JSON.parse(e);for(k in r)t+='<option value="'+k+'">'+k+"</option>"}else r={};$("#"+s+"List").append(t),c=window.localStorage[s+"Last"],c&&$("#"+s+"List").val(c).change()}function n(e){var t;if(a)t=e||""===a.getSelectedText()?a.getValue():a.getSelectedText();else{var o=document.getElementById(s);if(e)t=$("#"+s).val();else if(void 0!==o.selectionStart&&o.selectionStart!=o.selectionEnd)t=o.value.substring(o.selectionStart,o.selectionEnd);else if(void 0!==document.selection){o.focus();var l=document.selection.createRange();t=l.text}else t=$("#"+s).val()}return t}if("undefined"==typeof SiebelApp)alert("It works only in Siebel OUI session!");else{var a,r,c,s="SiebelEvalScript",d='<div title="eScript"><select id = "'+s+'List" style="display:block"><option value="*">New...</option></select><textarea id = "'+s+'" placeholder="eSciript code..." style="height:150px"></textarea><label id = "'+s+'_lbl" for="'+s+'">Initialised</label><textarea id = "'+s+'Out" rows="4" disabled></textarea><style>select,textarea{width:100%!Important}.ui-dialog-content{padding:0.5em 1em}</style></div>';$("#"+s+"List").parent().remove();{$(d).dialog({modal:!0,width:1024,open:function(){if($("#"+s).focus(),"undefined"==typeof ace){var l=document.createElement("script");l.setAttribute("src","https://cdnjs.cloudflare.com/ajax/libs/ace/1.4.2/ace.js"),l.setAttribute("async",!0),l.onload=t,document.body.appendChild(l)}else t();$("#"+s+"List").change(function(e){var t=$(this).val();"*"!=t&&t>""&&(a?a.setValue(r[t]):$("#"+s).text(r[t]),window.localStorage[s+"Last"]=t)}),$("#"+s+"Out").parent().keydown(function(t){return t.ctrlKey&&13===t.keyCode?(e(),!1):t.ctrlKey&&83===t.keyCode?(o(),!1):void 0}),i()},close:function(){$(this).dialog("destroy").remove()},buttons:[{text:"Run (Ctrl+Enter)",click:e},{text:"Save (Ctrl + S)",click:o},{text:"Remove",click:l},{text:"Close (Esc)",click:function(){$(this).dialog("close")}}]})}}}();
Back: Service, and don't forget about Application UserProp
Here I want to share the way you can prettify Siebel built-in code editor and make Client-side Business Service development process a bit more pleasant.
Regarding to to my choice of online code editor. After a quick loop through "top 5 editors", I've picked AceJS - mostly because I had a recent experience working with it.
After you download lib (https://github.com/ajaxorg/ace-builds/), place the content of "src-min-noconflict" subfolder in "\PUBLIC\enu\23048\SCRIPTS\3rdParty\ace\".
Create Presentation Model and Physical Renderer files:
if (typeof (SiebelAppFacade.CSBS_PM) === "undefined") {
SiebelJS.Namespace("SiebelAppFacade.CSBS_PM");
define("siebel/custom/CSBS_PM", ["siebel/pmodel"],
function () {
SiebelAppFacade.CSBS_PM = (function () {
function CSBS_PM(pm) {
SiebelAppFacade.CSBS_PM.superclass.constructor.apply(this, arguments);
}
SiebelJS.Extend(CSBS_PM, SiebelAppFacade.PresentationModel);
CSBS_PM.prototype.Init = function () {
SiebelAppFacade.CSBS_PM.superclass.Init.apply(this, arguments);
}
CSBS_PM.prototype.Setup = function (propSet) {
SiebelAppFacade.CSBS_PM.superclass.Setup.apply(this, arguments);
this.AddProperty("ScriptValue", ""); // property to pass field value PM->PR
bc = this.Get("GetBusComp");
var sField = "Script"; // hardcoded field name to attach ace plugin
this.AddProperty("ScriptFieldName", sField);
// update ace editor value everytime script field value changes
this.AddMethod("GetFormattedFieldValue", function (control) {
if (control.GetFieldName() == sField && bc.GetFieldValue(sField) != this.Get("ScriptValue")) {
this.SetProperty("ScriptValue", bc.GetFieldValue(sField));
}
}, {
sequence: false,
scope: this
});
}
return CSBS_PM;
}());
return "SiebelAppFacade.CSBS_PM";
})
}
if (typeof (SiebelAppFacade.CSBS_PR) === "undefined") {
SiebelJS.Namespace("SiebelAppFacade.CSBS_PR");
define("siebel/custom/CSBS_PR", ["3rdParty/ace/ace", // determines dependencies (path to ace.js file)
"siebel/phyrenderer"],
function () {
SiebelAppFacade.CSBS_PR = (function () {
function CSBS_PR(pm) {
SiebelAppFacade.CSBS_PR.superclass.constructor.apply(this, arguments);
}
SiebelJS.Extend(CSBS_PR, SiebelAppFacade.PhysicalRenderer);
CSBS_PR.prototype.Init = function () {
SiebelAppFacade.CSBS_PR.superclass.Init.apply(this, arguments);
}
CSBS_PR.prototype.ShowUI = function () {
SiebelAppFacade.CSBS_PR.superclass.ShowUI.apply(this, arguments);
var pm = this.GetPM(); // to use in global scope functions
var bc = pm.Get("GetBusComp");
var sField = pm.Get("ScriptFieldName");
// get original control
var oOrig = $("textarea[name='" + pm.Get("GetControls")[sField].GetInputName() + "']");
// add control for ace editor
var sNewId = "ace_code_editor";
SiebelJS.Log($(oOrig).parent().after('<div id="' + sNewId + '"></div>'));
var oNew = $("#" + sNewId);
// attach ace editor
var oAce = ace.edit(sNewId);
oAce.setTheme("ace/theme/monokai");
oAce.getSession().setMode("ace/mode/javascript");
oAce.$blockScrolling = Infinity;
// to be replaced with css file
oNew.css("height", oOrig.height() + "px"); // copy control height
oOrig.remove(); // remove orig control
$("#s_" + pm.Get("GetFullId") + "_div").find(".mceLabel").remove(); // remove labels
// copy value from ace editor into the field
oAce.getSession().on('change', function () {
bc.SetFieldValue(sField, oAce.getValue());
});
// copy field value to ace editor
this.AttachPMBinding("ScriptValue", function () {
field_value = pm.Get("ScriptValue");
if (field_value != oAce.getValue()) {
oAce.setValue(field_value);
oAce.gotoLine(1); // first line by default
}
});
}
CSBS_PR.prototype.BindEvents = function () {
SiebelAppFacade.CSBS_PR.superclass.BindEvents.apply(this, arguments);
}
CSBS_PR.prototype.BindData = function () {
SiebelAppFacade.CSBS_PR.superclass.BindData.apply(this, arguments);
}
return CSBS_PR;
}());
return "SiebelAppFacade.CSBS_PR";
})
}
Don't forget to attach PM/PR files to applet (Business Service Script Editor Applet2) on Application -> Manifest Administration view.
Optimise a view layout (custom .SWT?).
Maybe attach a custom CSS to prettify a view.
Put BS simulator applets on the same page and pre-default BS Name there.
Enjoy your runtime playground!