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:

  • Field calculated value, pre/post-default, validation
  • Data Validation Manager
  • User properties
  • Workflow processes and UI Tasks
  • Predefined queries
  • Runtime events
  • EAI/BC Data Map

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.


Back end


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.


Front end


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.

Here is the code from my most recent version: Snippet
/* 
@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;
} 
Bookmarklet code: Snippet
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()}}}(); 
And a bookmarklet: Bookmarklet

Updated


Here is a new version with a built-in beautifier from another topic:

  • A parser to be placed at /public/scripts/3rdParty/SiebelQueryLang.js: file
  • Source code: Snippet
  • /* 
    @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;
    } 
  • Bookmarklet code: Snippet
  • 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()})}}}(); 
  • And a bookmarklet link: Bookmarklet

 

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.


Back-end


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:


Front-end


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.


Recent release


Don't have time building your own playground? Try mine, it is free and has some advanced features you might enjoy:

  • JavaScript code editor;
  • log() function to print intermediate results also works with PropertySets;
  • Prints outputs into browser console;
  • Run full snippet or a select a partucular peace of code to run it;
  • Orginise a library of your favourite code snippets;
  • Key bindings (Ctrl+Enter, Ctrl+S);

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