Great news, a freshly baked version of the About View bookmarklet has arrived.

Been working on it slowly for quite a long time. Started this version from scratch to get rid of EJS and experiment more with plain ES6 JS. Hope it deserves a separate post.

New features in version 2.0:

  • First of all, the source code is now much more friendly, so don't hesitate to make your hands dirty and tune it to your needs.
  • It now has settings that are stored in localStorage:
    • Advanced properties - turn it off when overwhelmed with unnecessary OUI properties
    • Options to customize how applets and controls are listed
    • Event handlers for links, whitespace, bookmark icon clicks
    • Focus feature to highlight the deepest expanded element
  • Applets arranged hierarchically from primary BC at the top to indented child BC and popup applets at the bottom
  • Hierarchy of custom PR, PM, and PW classes for each element
  • Current workspace name and version. Keep in mind it is not refreshed when inspected through my quick inspect tool until you refresh a page.
  • Plenty of new attributes including a screen name, current page recordset, search spec (when available) and others.
  • Also fixed several defects here and there.

As I mentioned it is relatively fresh, so stay tuned for further upgrades and I'll greatly appreciate all feedback.

Recent version:

  • About View 2.0 source code
  • (() => {
    
        if ("undefined" == typeof SiebelApp) {
            alert("It works only in Siebel OUI session!");
            return;
        }
    
        const Id = "XapuksAboutView2";
    
        // read options localStorage
        let options = {};
        if (localStorage.getItem(Id)) {
            options = JSON.parse(localStorage.getItem(Id));
        } else {
            resetOptions();
        }
    
        let $d = $(`.ui-dialog.${Id}`);
    
        // event handling
        const handlers = {
            "None": (e) => {
                return true;
            },
            "Expand / Special": (e) => {
                id = $(e.target).attr("data-ul");
                if (id) {
                    $d.find(`ul.ul_show:not(#${id}):not(:has(#${id})):not(.keep_open)`).removeClass("ul_show").addClass("ul_hide");
                    $d.find("#" + id).toggleClass(['ul_show', 'ul_hide']);
                    e.stopPropagation();
                    return false;
                } else {
                    e.stopPropagation();
                    return true;
                }
            },
            "Expand active context": (e) => {
                const a = SiebelApp.S_App.GetActiveView().GetActiveApplet();
                if (a) {
                    $d.find(`ul#${a.GetFullId()}, ul#${a.GetFullId()}_controls, ul#${a.GetActiveControl()?.GetInputName()}`).removeClass("ul_hide").addClass("ul_show");
                }
                e?.stopPropagation();
                return false;
            },
            "Copy active applet": (e) => {
                const a = SiebelApp.S_App.GetActiveView().GetActiveApplet();
                if (a) {
                    handlers["Copy value"]({
                        target: $d.find(`a[data-ul=${a.GetFullId()}]`)[0]
                    });
                }
            },
            "Collapse item": (e) => {
                $d.find(`ul.ul_show:not(:has(.ul_show:not(.keep_open))):not(.keep_open)`).removeClass("ul_show").addClass("ul_hide");
                e.stopPropagation();
                return false;
            },
            "Collapse all": (e) => {
                $d.find(`ul.ul_show:not(.keep_open)`).removeClass("ul_show").addClass("ul_hide");
                e.stopPropagation();
                return false;
            },
            "Close dialog": (e) => {
                $d.dialog("close");
                e.stopPropagation();
                return false;
            },
            "Options": (e) => {
                rOptions();
                e.stopPropagation();
                return false;
            },
            "Copy value and close": (e) => {
                handlers["Copy value"](e);
                handlers["Close dialog"](e);
                e.stopPropagation?.call(this);
                return false;
            },
            "Copy value": (e) => {
                const scope = e.target;
                // replacing link with intput and select the value
                const val = $(scope).text();
                $(scope).hide().after("<input id='" + Id + "i'>");
                $d.find("#" + Id + "i").val(val).select();
                // attempt to copy value
                if (document.execCommand("copy", false, null)) {
                    // if copied, display a message for a second
                    $d.find("#" + Id + "i").attr("disabled", "disabled").css("color", "red").val("Copied!");
                    setTimeout(() => {
                        $d.find("#" + Id + "i").remove();
                        $(scope).show();
                    }, 700);
                } else {
                    // if failed to copy, leave input until blur, so it can be copied manually
                    $d.find("#" + Id + "i").blur(() => {
                        $(this).remove();
                        $d.find("a").show();
                    });
                }
                e.stopPropagation?.call(this);
                return false;
            },
            "Invoke applet method": (e) => {
                var $target = $(e.target);
                var applet = SiebelApp.S_App.GetActiveView().GetAppletMap()[$target.attr("data-applet")];
                const method = $target.text();
                applet.InvokeMethod(method);
                e.stopPropagation?.call(this);
                return false;
            },
            "Invoke control method": (e) => {
                var $target = $(e.target);
                var applet = SiebelApp.S_App.GetActiveView().GetAppletMap()[$target.attr("data-applet")];
                var control = applet.GetControls()[$target.attr("data-control")];
                const method = $target.text();
                SiebelApp.S_App.uiStatus.Busy();
                try {
                    applet.GetPModel().OnControlEvent(SiebelApp.Constants.get("PHYEVENT_INVOKE_CONTROL"), control.GetMethodName(), control.GetMethodPropSet(), {
                        async: true,
                        cb: () => SiebelApp.S_App.uiStatus.Free()
                    });
                } catch (e) {
                    console.error(e.toString());
                    SiebelApp.S_App.uiStatus.Free();
                }
                e.stopPropagation?.call(this);
                return false;
            },
            "Focus": (e) => {
                var $target = $(e.target);
                const $el = $($target.attr("data-focus"));
                $d.dialog('close');
                $el.focus();
                e.stopPropagation?.call(this);
                return false;
            },
            "Expand related": (e) => {
                var $target = $(e.target);
                var sel = $target.attr("data-selector");
                $d.find(`ul.ul_show:not(.keep_open)`).removeClass("ul_show").addClass("ul_hide");
                $d.find(sel).toggleClass(['ul_show', 'ul_hide']);
                e.stopPropagation?.call(this);
                return false;
            }
    
        };
    
        // handle double click
        if ($d.length) {
            var o = options["bmk_dbl"];
            handlers[o]();
            return;
        }
    
        // render the dialog
        let guid = 0;
        const css = [`<style>`, ...[
            `ul {margin-left:20px}`,
            `a.x_active {text-decoration:underline}`,
            `a.x_hidden {font-style:italic}`,
            `select {display:block; margin-bottom:15px}`,
            `.options {background-color:lightgray; padding:15px; margin:10px}`,
            `.ul_hide {display:none}`,
            `.ul_show {border-bottom: 1px solid; border-top: 1px solid; margin: 5px; padding: 5px; border-color: lightgray;}`,
            options["focus_feature"] == "true" ? `ul:has(.ul_show:not(.keep_open)) :is(label,a) {color:darkgray!important}` : ``,
            options["focus_feature"] == "true" ? `ul.ul_show:not(:has(.ul_show:not(.keep_open))) :is(label,a) {color:black!important}` : ``,
            `a[data-ul] {font-weight:bold}`,
            `a[data-ul]:before {content:"> "; opacity:0.5; font-style:normal}`,
            `a[data-handler]:before, a[data-focus]:before {content:"<["; opacity:0.5; font-style:normal}`,
            `a[data-handler]:after, a[data-focus]:after {content:"]>"; opacity:0.5; font-style:normal}`,
            `label {font-size:1rem; margin:0px; font-weight:bold;}`,
            `table {display:block; overflow-x:auto; whitespace: nowrap}`,
            `td {border:solid 1px}`,
            `.options select {width:250px}`,
        ].map((i) => i ? `.${Id} ${i}` : ``), `</style>`].join("\n");
    
        $d = $(`<div class="container" title="About View 2.0">${rApplication()}${css}</div>`).dialog({
            dragStop: () => $d.dialog({ height: 'auto' }),
            classes: { "ui-dialog": Id },
            modal: true,
            width: options["width"],
            close: () => $d.dialog('destroy').remove(),
            buttons: [
                {
                    text: 'Help',
                    click: () => window.open('http://xapuk.com/index.php?topic=145', '_blank')
                }, {
                    text: 'Feedback',
                    click: () => window.location.href = "mailto:[email protected]?subject=AboutView2"
                }, {
                    text: 'Settings',
                    click: rOptions,
                }, {
                    text: 'Reset Settings',
                    click: resetOptions
                }, {
                    text: 'Close (esc)',
                    click: () => $d.dialog('close')
                }
            ]
        });
    
        function dispatchEvent(e, cb) {
            var $target = $(e.target);
            if (cb.name?.indexOf("Special") > 0) {
                if ($target.attr("data-handler") == "applet method") {
                    return handlers["Invoke applet method"](e);
                } else if ($target.attr("data-handler") == "control method") {
                    return handlers["Invoke control method"](e);
                } else if ($target.attr("data-selector")) {
                    return handlers["Expand related"](e);
                }
            }
            if ($target.attr("data-focus")) {
                return handlers["Focus"](e);
            }
            return cb(e);
        }
    
        $d.contextmenu(handlers[options["ws_right"]]);
        $d.click(handlers[options["ws_click"]]);
        $d.find("a").off("click").click((e) => dispatchEvent(e, handlers[options["link_click"]]));
        $d.find("a").off("contextmenu").contextmenu((e) => dispatchEvent(e, handlers[options["link_right"]]));
        $(".ui-widget-overlay").click(handlers[options["out_click"]]);
        $(".ui-widget-overlay").contextmenu(handlers[options["out_right"]]);
    
        function resetOptions(e) {
            options = {
                "bmk_dbl": "Expand active context",
                "ws_click": "None",
                "ws_right": "Collapse item",
                "link_click": "Copy value",
                "link_right": "Expand / Special",
                "out_click": "Close dialog",
                "out_right": "Close dialog",
                "adv": "true",
                "width": "1000",
                "ctrl_list_by": "name",
                "applet_list": "applet / bc",
                "applet_list_by": "name",
                "focus_feature": "false"
            };
            localStorage.setItem(Id, JSON.stringify(options));
        }
    
        // render functions
        function rDropdown(caption, field, list) {
            const id = field;
            const value = options[field];
            return [`<li>`,
                `<label for="${id}">${caption}</label>`,
                `<select id="${id}">`,
                list.map((i) => `<option value="${i}" ${i == value ? 'selected' : ''}>${i}</option>`),
                `</select>`,
                `<li>`].join("");
        }
    
        function rOptions() {
            if ($d.find(".options").length) {
                $d.find(".options").remove();
            } else {
                let html = [
                    `<div class="options">`,
                    `<h4>SETTINGS</h4>`,
                    rDropdown(`Advanced properties`, `adv`, [`false`, `true`]),
                    rDropdown(`Dialog width`, `width`, [`600`, `800`, `1000`]),
                    rDropdown(`Show in main list`, `applet_list`, [`applet`, `applet / bc`, `applet / bc / rowid`]),
                    rDropdown(`List applets by`, `applet_list_by`, [`name`, `title`]),
                    rDropdown(`List controls by`, `ctrl_list_by`, [`name`, `caption`]),
                    rDropdown(`Link click`, `link_click`, [`Copy value`, `Copy value and close`, `Expand / Special`, `None`]),
                    rDropdown(`Link right click`, `link_right`, [`Copy value`, `Copy value and close`, `Expand / Special`, `None`]),
                    rDropdown(`Bookmarklet double click`, `bmk_dbl`, [`Expand active context`, `Copy active applet`]),
                    rDropdown(`Whitespace click`, `ws_click`, [`None`, `Close dialog`, `Options`, `Expand active context`, `Collapse item`, `Collapse all`]),
                    rDropdown(`Whitespace right click`, `ws_right`, [`None`, `Close dialog`, `Options`, `Expand active context`, `Collapse item`, `Collapse all`]),
                    rDropdown(`Outside click`, `out_click`, [`Close dialog`, `Expand active context`, `Collapse item`, `Collapse all`]),
                    rDropdown(`Outside right click`, `out_right`, [`Close dialog`, `Expand active context`, `Collapse item`, `Collapse all`]),
                    rDropdown(`Focus feature`, `focus_feature`, [`false`, `true`]),
                    `<\div>`
                ].join("\n");
                $d.append(html);
                $d.find("select").change((e) => {
                    const c = e?.target;
                    if (c) {
                        options[c.id] = c.value;
                        localStorage.setItem(Id, JSON.stringify(options));
                    }
                });
            }
        }
    
        function rPS(prop) {
            return rItem(`<a href='#'>${prop[0]}</a>`, prop[1]);
        }
    
        function rHierarchy(caption, value, advanced) {
            var a = [];
            while ("object" === typeof value && value?.constructor?.name?.length > 1) {
                a.push(value.constructor.name);
                value = value.constructor.superclass;
            }
            return rItem(caption, a, advanced);
        }
    
        function rItem(caption, value, advanced, attribs = {}) {
            if (value && (!Array.isArray(value) || value.length) || "boolean" === typeof value) {
                if (!advanced || options["adv"] === `true`) {
                    guid++;
                    let id = Id + guid;
                    let sAttr = Object.entries(attribs).map(([p, v]) => `${p}="${v}"`).join(" ");
                    return (Array.isArray(value) ? [
                        `<li>`,
                        `<label for="${id}_0">`, caption, `:</label> `,
                        value.map((e, i) => `<a href="#" id="${id}_${i}" ${sAttr}>${e}</a>`).join(" > "),
                        `</li>`
                    ] : [
                        `<li>`,
                        `<label for="${id}">`, caption, `:</label> `,
                        `<a href="#" id="${id}" ${sAttr}>`, escapeHtml(value), `</a>`,
                        `</li>`
                    ]).join("");
                }
            }
            return "";
        }
    
        function rControl(control) {
            const id = control.GetInputName();
            const applet = control.GetApplet();
            const pr = SiebelAppFacade.ComponentMgr.FindComponent(applet.GetName())?.GetPR();
            const bc = applet.GetBusComp();
            const ps = control.GetMethodPropSet();
            const up = control.GetPMPropSet();
            const con = SiebelApp.Constants;
            let sel = `#${applet.GetFullId()} [name=${control.GetInputName()}]`;
            if (con.get("SWE_CTRL_RTCEMBEDDED") === control.GetUIType()) {
                sel = `#${applet.GetFullId()} #cke_${control.GetInputName()}`;
            }
            const cls = control === applet.GetActiveControl() ? 'x_active' : $(sel).length == 0 || $(sel).is(":visible") ? '' : 'x_hidden'
            return [`<li>`,
                `<a href="#" data-ul="${id}_c" class="${cls}">`,
                options["ctrl_list_by"] == 'caption' && control.GetDisplayName() ? control.GetDisplayName() : control.GetName(),
                `</a>`,
                `<ul id="${id}_c" class="ul_hide">`,
                rItem(control.GetControlType() == con.get("SWE_PST_COL") ? "List column" : control.GetControlType() == con.get("SWE_PST_CNTRL") ? "Control" : control.GetControlType(), control.GetName()),
                rItem("Display name", control.GetDisplayName()),
                rItem("Field", control.GetFieldName(), false, { "data-handler": "field", "data-selector": `ul#${applet.GetFullId()}_bc,ul#${applet.GetFullId()}_bc_${applet.GetBusComp().GetFieldMap()[control.GetFieldName()]?.index}` }),
                rItem("Value", bc?.GetFieldValue(control.GetFieldName())),
                rItem("Message", control.GetControlMsg()),
                ...(control.GetMessageVariableMap() && Object.keys(control.GetMessageVariableMap()).length ?
                    [`<li><a href="#" data-ul="${id}_var">Message variables (${Object.keys(control.GetMessageVariableMap()).length}):</a></li>`,
                    `<ul id="${id}_var" class="ul_hide">`,
                    Object.entries(control.GetMessageVariableMap()).map(rPS).join("\n"),
                        `</ul>`] : []),
                rItem("Type", control.GetUIType()),
                rItem("LOV", control.GetLovType()),
                rItem("MVG", control.IsMultiValue()),
                rItem("Method", control.GetMethodName(), false, { "data-handler": "control method", "data-applet": applet.GetName(), "data-control": control.GetName() }),
                ...(options["adv"] === `true` && ps && ps.propArrayLen ?
                    [`<li><a href="#" data-ul="${id}_method">Method properties (${ps.propArrayLen}):</a></li>`,
                    `<ul id="${id}_method" class="ul_hide">`,
                    Object.entries(ps.propArray).map(rPS).join("\n"),
                        `</ul>`] : []),
                //rItem("Id", id),
                rItem("Id", options["adv"] === `true` && $(sel).is(":focusable") ? [id, `<a href="#" data-focus='${sel}'>Focus</a>`] : id),
                rItem("Immidiate post changes", control.GetPostChanges()),
                rItem("Display format", control.GetDisplayFormat()),
                rItem("HTML attribute", control.GetHTMLAttr(), true),
                rItem("Display mode", control.GetDispMode()),
                rItem("Popup", control.GetPopupType() && [control.GetPopupType(), control.GetPopupWidth(), control.GetPopupHeight()].join(" / ")),
                rHierarchy("Plugin wrapper", SiebelApp.S_App.PluginBuilder.GetPwByControl(pr, control), true),
                rItem("Length", control.GetFieldName() ? control.GetMaxSize() : ""),
                ...(options["adv"] === `true` && up && up.propArrayLen ?
                    [`<li><a href="#" data-ul="${id}_up">User properties (${up.propArrayLen}):</a></li>`,
                    `<ul id="${id}_up" class="ul_hide">`,
                    Object.entries(up.propArray).map(rPS).join("\n"),
                        `</ul>`] : []),
                rItem("Object", `SiebelApp.S_App.GetActiveView().GetAppletMap()["${applet.GetName()}"].GetControls()["${control.GetName()}"]`, true),
                $(sel).length > 0 ? rItem("Node", `$("${sel}")`, true) : ``,
                `</ul>`, `</li>`].join("");
        }
    
        function rApplet(applet) {
            const cm = Object.values(applet.GetControls());
            const mm = applet.GetCanInvokeArray();
            const id = applet.GetFullId();
            return [`<ul id="${id}" class="ul_hide">`,
            rItem("Applet", applet.GetName()),
            rItem("BusComp", applet.GetBusComp()?.GetName(), false, { "data-handler": "buscomp", "data-selector": `ul#${applet.GetFullId()}_bc` }),
            rItem("Title", SiebelApp.S_App.LookupStringCache(applet.GetTitle())),
            rItem("Mode", applet.GetMode()),
            rItem("Record counter", applet.GetPModel().GetStateUIMap().GetRowCounter, true),
            rHierarchy("PModel", applet.GetPModel(), true),
            rHierarchy("PRender", SiebelAppFacade.ComponentMgr.FindComponent(applet.GetName())?.GetPR(), true),
            rItem("Object", `SiebelApp.S_App.GetActiveView().GetAppletMap()["${applet.GetName()}"]`, true),
            rItem("Node", `$("#${applet.GetFullId()}")`, true),
            `<li><a href="#" data-ul="${id}_methods">Methods (${mm.length}):</a></li>`,
            `<ul id="${id}_methods" class="ul_hide">`,
            mm.map(m => [`<li>`, `<a href="#" data-handler="applet method" data-applet="${applet.GetName()}">`, m, `</a>`, `</li>`].join("")).join("\n"),
                `</ul>`,
            `<li><a href="#" data-ul="${id}_controls">Controls (${cm.length}):</a></li>`,
            `<ul id="${id}_controls" class="ul_show keep_open">`, //<ul id="${id}_controls" class="ul_show">
            ...cm.map(rControl),
                `</ul>`,
                `</ul>`].join("\n");
        }
    
        function rField(field, id) {
            const bc = field.GetBusComp();
            const name = SiebelApp.S_App.LookupStringCache(field.GetName());
            return [`<li>`,
                `<a href="#" data-ul="${id}">`, name, `</a>`,
                `<ul id="${id}" class="ul_hide">`,
                rItem("Field", name),
                rItem("Value", field.GetBusComp().GetFieldValue(name)),
                rItem("Type", field.GetDataType()),
                rItem("Length", field.GetLength()),
                rItem("Search spec", field.GetSearchSpec()),
                rItem("Calculated", !!field.IsCalc()),
                rItem("Bounded picklist", !!field.IsBoundedPick()),
                rItem("Read only", !!field.IsReadOnly()),
                rItem("Immediate post changes", !!field.IsPostChanges()),
                rItem("Object", `SiebelApp.S_App.GetBusObj().GetBusCompByName("${bc.GetName()}").GetFieldMap()["${name}"]`, true),
                `</ul>`, `</li>`].join("\n");
        }
    
        function rBC(a, id) {
            var bc = a.GetBusComp();
            const fields = Object.values(bc.GetFieldMap());
            return [`<ul id="${id}" class="ul_hide">`,
            rItem("BusComp", bc.GetName()),
            rItem("Commit pending", !!bc.commitPending, true),
            rItem("Can update", !!bc.canUpdate),
            rItem("Search spec", bc.GetSearchSpec()),
            rItem("Sort spec", bc.GetSortSpec()),
            rItem("Current row id", bc.GetIdValue()),
            rItem("Object", `SiebelApp.S_App.GetBusObj().GetBusComp("${a.GetBCId()}")`, true),
            `<li><label><a href="#" data-ul="${id}_rec">Records: ${Math.abs(bc.GetCurRowNum())} of ${bc.GetNumRows()}${bc.IsNumRowsKnown() ? '' : '+'}</a></label></li>`,
            `<ul id="${id}_rec" class="ul_hide">`,
                `<table>`,
                `<tr>`,
            ...Object.keys(bc.GetFieldMap()).map((i) => `<th>${i}</th>`),
                `</tr>`,
            ...bc.GetRecordSet().map((r, i) => [
                `<tr>`,
                ...Object.values(r).map(v => [
                    `<td><a href="#" ${bc.GetSelection() == i ? ` class="x_active"` : ``}>`,
                    v,
                    `</a></td>`
                ].join("")),
                `</tr>`
            ].join("")),
                `</table>`,
                `</ul>`,
            `<li><label><a href="#" data-ul="${id}_fields">Fields(${bc.GetFieldList()?.length}):</a></label></li>`,
            `<ul id="${id}_fields" class="ul_show keep_open">`,
            ...fields.map((field, i) => rField(field, id + "_" + field.index)),
                `</ul>`,
                `</ul>`].join("\n");
        }
    
        function rApplication() {
            const app = SiebelApp.S_App;
            const view = app.GetActiveView();
            const bo = app?.GetBusObj();
            const bm = bo?.GetBCArray();
            const scrPM = SiebelApp.S_App.NavCtrlMngr()?.GetscreenNavigationPM();
            let am = Object.values(view?.GetAppletMap());
            var ws = SiebelApp.S_App.GetWSInfo().split("_");
            var wsver = ws.pop();
    
            var amCache = {};
            Object.assign(amCache, view?.GetAppletMap());
    
            // Identifying a primary BC
            var paa = Object.values(SiebelApp.S_App.GetActiveView().GetAppletMap()).filter((a) => !a.GetParentApplet() && (!a.GetBusComp() || !a.GetBusComp().GetParentBusComp()));
            if (!paa.length) {
                alert("Failed to identify a primary BusComp!")
            }
    
            return [`<ul>`,
                rItem("Application", app.GetName()),
                rItem("Screen", scrPM?.Get("GetTabInfo")[scrPM?.Get("GetSelectedTabKey")]?.screenName),
                rItem("View", view.GetName()),
                rItem("Task", view.GetActiveTask()),
                rHierarchy("PModel", SiebelAppFacade.ComponentMgr.FindComponent(view.GetName())?.GetPM(), true),
                rHierarchy("PRender", SiebelAppFacade.ComponentMgr.FindComponent(view.GetName())?.GetPR(), true),
                rItem("BusObject", bo?.GetName()),
                rItem("Workspace", [ws.join("_"), wsver]),
                `<label>Applets (${am.length}) / BusComps (${bm.length}):</label>`,
                `<ul>`,
                hierBC(paa[0].GetBusComp(), 0, amCache),
                ...Object.values(amCache).map((a) => rAppletName(a, 0, amCache)),
                `</ul></ul>`].join("\n");
        }
    
        // prints applet name
        function rAppletName(a, l, amCache) {
            delete amCache[a.GetName()];
            return [`<li>`,
                `<ul>`.repeat(l),
                `<a href="#" data-ul="${a.GetFullId()}" class="${a === SiebelApp.S_App.GetActiveView().GetActiveApplet() ? 'x_active' : $(`#${a.GetFullId()}`).is(":visible") ? '' : 'x_hidden'}">`,
                options['applet_list_by'] == 'title' && SiebelApp.S_App.LookupStringCache(a.GetTitle()) ? SiebelApp.S_App.LookupStringCache(a.GetTitle()) : a.GetName(),
                `</a>`,
                a.GetBusComp() && options["applet_list"].indexOf("bc") > -1 ? ` / <a href="#" data-ul="${a.GetFullId()}_bc">${a.GetBusComp().GetName()}</a>` : ``,
                a.GetBusComp() && a.GetBusComp().GetIdValue() && options["applet_list"].indexOf("rowid") > -1 ? ` / <a href="#">${a.GetBusComp().GetIdValue()}</a>` : ``,
                rApplet(a),
                a.GetBusComp() && rBC(a, a.GetFullId() + "_bc"),
                `</ul>`.repeat(l),
                `</li>`].join("");
        }
    
        // prints applets based on bc or parent applet (rec)
        function hierApplet(bc, pa, l, amCache) {
            return Object.values(amCache).filter((a) => bc && a.GetBusComp() === bc || pa && a.GetParentApplet() === pa).map((a) => !(a.GetName() in amCache) ? "" : [
                rAppletName(a, l, amCache),
                hierApplet(null, a, l + 1, amCache) // look for child applets
            ].join("\n"));
        }
    
        // prints applets based on BC hierarchy (rec)
        function hierBC(bc, l, amCache) {
            return [
                hierApplet(bc, null, l, amCache)?.join("\n"),
                ...SiebelApp.S_App.GetActiveBusObj().GetBCArray().filter((e) => e.GetParentBusComp() === bc).map((b) => hierBC(b, l + 1, amCache))
            ].join("\n");
        }
    
        // utilities
        function escapeHtml(html) {
            return html.toString()
                .replace(/&/g, "&")
                .replace(/</g, "<")
                .replace(/>/g, ">")
                .replace(/"/g, """)
                .replace(/'/g, "'");
        }
    })() 
  • About View 2.0 bookmarklet code to copy and paste into bookmark URL.
  • javascript:(function()%7B%2F*%0A%20%20%20%20%2B1.%20%5BClick%5D%20option%20should%20be%20against%20Type%3A%20Button%2C%20not%20method%0A%20%20%20%20%2B2.%20%5BInvoke%5D%20option%20should%20be%20available%20against%20every%20control%20with%20InvokeMethod%0A%09%2B3.%20Reorginize%20the%20code%2C%20so%20it%20is%20granular%20and%20can%20be%20easily%20reconfigured%20%0A%094.%20Fetch%20Applet%20and%20view%20UPs%20through%20PM%3A%0A%09%09var%20appletComponent%20%3D%20SiebelAppFacade.ComponentMgr.FindComponent(%7B%0A%09%09%09id%3A%20%5BopenAppletPopupJSON.AppletName%5D%5B0%5D%0A%09%09%7D)%3B%0A%09%09var%20popupAppletPM%20%3D%20appletComponent.GetPM()%3B%0A%20%20%20%20%2B5.%20Display%20a%20hierarchy%20of%20PM%2FPR%20classes%20for%20View%2FApplet%2FControl%20%0A%20%20%20%20%09-%20use%20Siebel%20facade%20hierarchy%20code%20snippet%0A%20%20%20%20%09-%20on%20click%20of%20PM%2FPR%20run%20inspect%20to%20open%20a%20source%20file%20when%20in%20Chrome%20DevTools%0A%20%20%20%20%2B6.%20BusComp%20object%20selector%20(make%20object%20selector%20hidden%20and%20coppy%20on%20double%20click%3F)%0A%20%20%20%207.%20make%20focus%20button%20a%20link%20and%20put%20it%20outside%20the%20spoiler%0A%20%20%20%20%2B8.%20another%20spoiler%20at%20the%20bottom%20with%20Options%3A%0A%20%20%20%20%09-%20left%2Fright%20click%20%3D%20copy%20%2B%20close%20%2B%20copy%20and%20close%20%2B%20expand%0A%20%20%20%20%09-%20advanced%20checkbox%20-%20if%20N%20-%20hide%20all%20OUI%20related%20properties%0A%20%20%20%20%09-%20store%20options%20in%20localStorage%0A%20%20%20%20%09-%20checkbox%20-%20always%20expand%20active%20applet%0A%20%20%20%20%09-%20checkbox%20-%20always%20expand%20active%20control%20(available%20only%20if%20prev%20option%20%3D%20Y)%0A%20%20%20%20-9.%20applet%20%2F%20control%20DOM%20selectors%20to%20have%20Inspect%20option%20-%20on%20click%20run%20inspect()%20-%20not%20possible%20to%20call%20inspect%20from%20JS%20%0A%20%20%20%20%2B10.%20IP19%20compatible%0A%20%20%20%20-11.%20Renders%20hidden%20content%20when%20expanded%2C%20not%20in%20advance%20-%20more%20reliable%20and%20fast%0A%20%20%20%2012.%20for%20applets_div%20and%20controls%20container%20print%20out%20event%20listeners%20(event%2C%20selector%2C%20inspect(function))%20-%20code%20snippet%20%3D%20Node%20event%20listeners%0A%20%20%20%20%2B13.%20display%20applets%2FBCs%20in%20a%20hierarchycal%20order%20(use%20space%20or%20a%20glyph%20to%20indent)%0A%20%20%20%2014.%20Display%20a%20tree%20of%20all%20pm.Get()%20properties%20for%20view%20and%20applet%20-%20in%20our%20case%20it%20is%20where%20all%20portal%20config%20is%20stored%0A%20%20%20%2015.%20also%20display%20all%20SiebelApp.Constants%3F%20How%20to%20get%20a%20list%3F%0A%20%20%20%20%2B16.%20Get%20value%20through%20GetControlMsg()%20if%20GetFieldName()%20doesn't%20exist%20in%20the%20BC%20recordset%0A%20%20%20%2017.%20dblclick%20to%20run%20special%20methods%20(configurable)%20-%20focus%2C%20inspect%2C%20invoke%2C%20force%20show%2C%20etc%2C%20dblclick%20on%20whitespace%20gives%20you%20advanced%20look%0A%20%20%20%2018.%20print%20standalone%20applet%20in%20collapsed%20section%3F%20Through%20SiebelAppFacade.%0A%20%20%20%20-19.%20applet%2C%20application%20UPs%20and%20other%20staff%20comming%20from%20the%20server%20through%20SiebelApp.S_App.GetAppPropertySet()%20-%20nope%2C%20not%20applet%20UPs%0A%20%20%20%20%2B21.%20cater%20for%20empty%20applets%20with%20no%20records%2C%20export%20and%20import%20applets%0A%20%20%20%2022.%20expose%20RO%20attributte%20for%20all%20controls%20and%20have%20an%20option%20to%20update%20a%20field%20right%20away%0A%20%20%20%2023.%20commands%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20bookmarklet%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20-%20double%20click%20%3D%20expand%20active%20applet%20and%20control%2Fcopy%20active%20applet%2Fcopy%20active%20control%20name%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20-%20tripple%20click%20%3D%20reset%20all%20options%20to%20default%0A%20%20%20%20%20%20%20%20%20%20%20%20whitespace%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20-%20click%20%3D%20show%20more%20options%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20-%20right%20click%20%3D%20close%20window%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20-%20double%20click%20%3D%20expand%20all%20or%20collapse%20all%20if%20expanded%0A%20%20%20%20%20%20%20%20%20%20%20%20links(%3Ca%3E)%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20-%20left%20click%20%3D%20special%20command%20(expand%2C%20run%20method%2C%20inspect%2C%20etc)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20-%20right%20click%20%3D%20copy%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20-%20doule%20click%20%3D%20%0A%20%20%20%20%20%20%20%20%20%20%20%20other%20option%20example%20-%20right%20mb%20down%20while%20left%20mb%20is%20down%0A%20%20%20%20%2B24.%20need%20another%20way%20of%20fetching%20PR%20class%2C%20through%20FindComponent%3F%0A%20%20%20%20%20%20%20%20%20%20%20%20SiebelAppFacade.ComponentMgr.FindComponent(SiebelApp.S_App.GetActiveView().GetAppletMap()%5B'FaCS%20UX2%20NQSC%20Applicant%20Portal%20Application%20Left%20Pane%20Menu%20Items%20Applet'%5D.GetName()).GetPR()%3B%0A%20%20%20%20%2B25.%20Double%20click%20BM%20to%20expand%20current%20applet%20%2B%20carrent%20control%2C%20third%20click%20will%20expand%20all%0A%20%20%20%2026.%20wrap%20render%20functions%20into%20try%2Fcatch%20and%20return%20error%20text%20istead%20of%20html%0A%20%20%20%20%2B27.%20Print%20the%20entire%20RecordSet%20and%20hightlight%20an%20active%20record%20instead%20of%20just%20active%20record%0A%20%20%20%2028.%20option%20to%20print%20jquery%20selector%20%24(%22%23123%22)%20or%20node%20selector%20%24(%22%23123%22)%5B0%5D%0A%20%20%20%20%2B29.%20expose%20view%20and%20applet%20title%20as%20applet%20attribute%20and%20as%20a%20value%20for%20AppletTitle%20control%0A%20%20%20%2030.%20option%20to%20toggle%20collapse%20all%20sections%20when%20expanding%20a%20neighbour%0A%20%20%20%20%2B31.%20toggle%20to%20show%20control%2Fapplet%20display%20name%20(GetDisplayName)%20or%20physical%20name%20(GetName)%20in%20the%20list%0A%20%20%20%20%2B32.%20get%20record%20count%20from%20SiebelApp.S_App.GetActiveView().GetAppletMap()%5B'FaCS%20UX2%20NQSC%20Application%20List%20Applet'%5D.GetPModel().GetStateUIMap().GetRowCounter%0A%20%20%20%20%2B34.%20escape%20field%20and%20message%20values%0A%09%2B34.%20invokable%20methods%2C%20swe%20properties%20of%20invokable%20control%0A%09%2B34.%20buscomp%20selector%20should%20be%20by%20id%0A%20%20%20%20%2B35.%20option%20to%20autoclose%20after%20copy%20%0A%20%20%20%20%2B36.%20Option%20to%20display%20a%20current%20rowid%20next%20to%20the%20applet%20%2F%20bc%0A%20%20%20%20%2B37.%20control%20UP%0A%20%20%20%20%2B38.%20link%20from%20the%20control%20to%20the%20field%20or%20just%20expand%20another%20level%20(call%20of%20the%20same%20renderer%20function)%20on%20right%20click%0A%20%20%20%20%2B39.%20fetch%20screen%20name%20using%20SiebelApp.S_App.NavCtrlMngr().GetscreenNavigationPM().Get(%22GetTabInfo%22)%2C%20active%20screen%20%3D%20SiebelApp.S_App.NavCtrlMngr().GetscreenNavigationPM().Get(%22GetSelectedTabKey%22)%0A%20%20%20%20%2B40.%20expandable%20items%20should%20have%20an%20indication%20like%20%22(%3E)%20(%5E)%20(%2B)%20collapsed%20item%22%20%22(V)%20(-)%20expanded%20item%22%0A%20%20%20%20%2B41.%20%5BReset%20options%5D%20button%20intead%20of%20event%20action%0A%20%20%20%20%2B42.%20Expand%20option%20should%20do%20applets%20and%20controls%0A%20%20%20%20%2B43.%20Keep%20Applet%20Controls%20list%20opened%20by%20default%0A%20%20%20%20%2B44.%20Special%20event%3A%0A%20%20%20%20%20%20%20%20-%20special%20attrib%20data-handler%20%3D%20%5Bfield%2C%20buscomp%2C%20applet%20method%2C%20control%20method%2C%20focus%5D%0A%20%20%20%20%20%20%20%20-%20special%20highlight%20that%20supports%20special%20event%20-%20%5Bvalue%5D%20(value)%20%3Cvalue%3E%0A%20%20%20%20%20%20%20%20-%20should%20be%20configurable%20for%20each%20instance%20or%20once%20for%20all%20SE%3F%0A%20%20%20%20%20%20%20%20-%20event%3F%20-%20%0A%20%20%20%20%20%20%20%20%20%20%20%20-%20dbl%20click%20also%20triggers%20single%20click%0A%20%20%20%20%20%20%20%20%20%20%20%20-%20right%20click%20already%20has%20an%20event%20handler%20(expand)%0A%20%20%20%20%20%20%20%20%2B%20(field%2C%20buscomp)%20SE%20on%20control%20field%20is%20to%20open%20a%20field%0A%20%20%20%20%20%20%20%20%2B%20(field)%20SE%20on%20applet%20buscomp%20is%20to%20open%20a%20buscomp%20-%20check%20how%20it%20works%20when%20buscomp%20is%20hidden%0A%20%20%20%20%20%20%20%20%2B%20(applet)%20SE%20on%20available%20method%20is%20to%20call%20it%0A%20%20%20%20%20%20%20%20%2B%20(applet%2C%20control)%20SE%20on%20control%20method%20is%20to%20call%20it%20-%20for%20all%20controls%20with%20InvokeMethod%0A%20%20%20%20%20%20%20%20%2B%20(applet%2C%20control)%20SE%20on%20control%20or%20control%20node%20selector%20or%20separate%20link%20%3Cfocus%3E%20is%20to%20focus%20on%20it%20-%20only%20works%20on%20visible%20elements%0A%20%20%20%20%2B45.%20right%20click%20on%20regular%20links%20(values)%20triggers%20right%20click%20on%20whitespace%0A%20%20%20%20%2B46.%20proper%20indent%20for%20expanded%20section%20is%20to%20wrap%20it%20into%20ul%20insted%20of%20indent%20-%20noticable%20on%20grandchild%20applets%0A%20%20%20%20%2B47.%20translate%20control%20type%20through%20the%20constants%20-%20check%20the%20full%20list%20in%20pwinfra%20and%20other%20PW%20%0A%20%20%20%20%2B48.%20Fancy%20CSS%20to%20fade%20everything%20otside%20the%20deepest%20expanded%20section%20to%20put%20atention%20to%20the%20expanded%20one%0A%20%20%20%20%20%20%20%20-%20not%20possible%20without%20js%20since%20(%3Anot%20and%20%3Ahas)%20are%20not%20supported%0A%20%20%20%20%20%20%20%20-%20append%20a%20special%20class%20%0A%20%20%20%20%2B49.%20fix%20all%20event%20handlers%20to%20be%20configurable%0A%20%20%20%2050.%20code%20review%0A%20%20%20%20%20%20%20%20-%20reorganise%20as%20class%3F%0A%20%20%20%20%2B51.%20revisit%20advanced%2Fdefauklt%20options%20(a%3Arecord%20counter%2C%20c%3Auser%20props%2C%20method%20props%2C%20focus%2C%20b%3Acommit%20pending%2C%20)%0A%20%20%20%20%2B52.%20value%20link%20right%20click%20should%20have%20a%20default%20handler%20(open%20menu%3F)%0A%20%20%20%20%2B53.%20option%20for%20focus%20border%20feature%0A%20%20%20%20%2B54.%20whitespace%20right%20click%20to%20collapse%20last%20expanded%20item%0A%20%20%20%2055.%20Check%20if%20there%20something%20else%20to%20expose%0A%20%20%20%20%2B58.%20list%20applet%20column%20visibility%3F%0A%20%20%20%20%2B59.%20view%20PR%2FPM%20doesn't%20work%20in%20fins%0A%20%20%20%20%2B61.%20rtf%20field%20visibility%2Fselector%0A%09%2B62.%20Show%20control%20length%20only%20if%20there%20a%20field%0A%20%20%20%20%0AVersion%203.0%20features%3A%0A%20%20%20%201.%20New%20concept%20-%20separate%20renderer%20and%20ref-data%3A%20%0A%20%20%20%20%20%20%20%20-%20ref-data%20example%3A%20%7B%0A%20%20%20%20%20%20%20%20%09%22Applet%22%3A%7B%0A%20%20%20%20%20%20%20%20%09%09%22Name%22%3A%22GetName%22%2C%0A%20%20%20%20%20%20%20%20%09%09%22PModel%22%3A%22GetPModel%22%0A%20%20%20%20%20%20%20%20%09%7D%2C%0A%20%20%20%20%20%20%20%20%09%22Control%22%3A%7B%0A%20%20%20%20%20%20%20%20%09%09...%0A%20%20%20%20%20%20%20%20%09%7D%0A%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20-%20store%20the%20structure%20in%20localStorage%0A%20%20%20%20%20%20%20%20-%20on%20label%20rightclick%20-%20exclude%20the%20prop%20from%20the%20tree%2C%20so%20user%20won't%20see%20it%20anymore%0A%20%20%20%20%20%20%20%20-%20under%20options%20-%20have%20a%20list%20of%20properties%20included%20and%20excluded%0A%20%20%20%2060.%20Make%20label%20also%20a%20link%20and%20copy%20function%20path%20on%20click%3F%0A%20%20%20%2056.%20Container%20page%20name%20expandable%20with%20a%20list%20of%20page%20items%20under%20it%20SiebelApp.S_App.GetChildren()%5B3%5D%0A%20%20%20%2057.%20Application%20UP%20%3D%20SiebelApp.S_App.GetPMPropSet()%0A%20%20%20%2033.%20print%20real%20event%20listeners%20since%20chrome%20leads%20to%20the%20jquery%20only%3F%0A%0A*%2F%0A%0A(()%3D%3E%7B%0A%0A%09if%20(%22undefined%22%20%3D%3D%20typeof%20SiebelApp)%7B%0A%09%09alert(%22It%20works%20only%20in%20Siebel%20OUI%20session!%22)%3B%0A%09%09return%3B%0A%09%7D%0A%09%0A%09const%20Id%20%3D%20%22XapuksAboutView2%22%3B%0A%0A%09%2F%2F%20read%20options%20localStorage%0A%09let%20options%20%3D%20%7B%7D%3B%0A%09if%20(localStorage.getItem(Id))%20%7B%0A%09%09options%20%3D%20JSON.parse(localStorage.getItem(Id))%3B%0A%09%7D%20else%20%7B%0A%09%09resetOptions()%3B%0A%09%7D%0A%0A%09let%20%24d%20%3D%20%24(%60.ui-dialog.%24%7BId%7D%60)%3B%09%0A%0A%20%20%20%20%2F%2F%20event%20handling%0A%09const%20handlers%20%3D%20%7B%0A%09%09%22None%22%3A(e)%3D%3E%7B%0A%09%09%09return%20true%3B%0A%09%09%7D%2C%0A%09%09%22Expand%20%2F%20Special%22%3A(e)%3D%3E%7B%0A%09%09%09id%20%3D%20%24(e.target).attr(%22data-ul%22)%3B%0A%09%09%09if%20(id)%20%7B%0A%09%09%09%09%24d.find(%60ul.ul_show%3Anot(%23%24%7Bid%7D)%3Anot(%3Ahas(%23%24%7Bid%7D))%3Anot(.keep_open)%60).removeClass(%22ul_show%22).addClass(%22ul_hide%22)%3B%0A%09%09%09%09%24d.find(%22%23%22%20%2B%20id).toggleClass(%5B'ul_show'%2C'ul_hide'%5D)%3B%0A%09%09%09%20%20%20%20e.stopPropagation()%3B%0A%09%09%09%09return%20false%3B%0A%09%09%09%7D%20else%20%7B%0A%09%09%09%09e.stopPropagation()%3B%0A%09%09%09%09return%20true%3B%0A%09%09%09%7D%0A%09%09%7D%2C%0A%09%09%22Expand%20active%20context%22%3A(e)%3D%3E%7B%0A%09%09%09const%20a%20%3D%20SiebelApp.S_App.GetActiveView().GetActiveApplet()%3B%0A%09%09%09if%20(a)%20%7B%0A%09%09%09%09%24d.find(%60ul%23%24%7Ba.GetFullId()%7D%2C%20ul%23%24%7Ba.GetFullId()%7D_controls%2C%20ul%23%24%7Ba.GetActiveControl()%3F.GetInputName()%7D%60).removeClass(%22ul_hide%22).addClass(%22ul_show%22)%3B%0A%09%09%09%7D%0A%09%09%09e%3F.stopPropagation()%3B%0A%20%20%20%20%09%20%20%20%20return%20false%3B%0A%09%09%7D%2C%0A%09%09%22Copy%20active%20applet%22%3A(e)%3D%3E%7B%0A%09%09%09const%20a%20%3D%20SiebelApp.S_App.GetActiveView().GetActiveApplet()%3B%0A%09%09%09if%20(a)%20%7B%0A%09%09%09%09handlers%5B%22Copy%20value%22%5D(%7B%0A%09%09%09%09%09target%3A%24d.find(%60a%5Bdata-ul%3D%24%7Ba.GetFullId()%7D%5D%60)%5B0%5D%0A%09%09%09%09%7D)%3B%0A%09%09%09%7D%0A%09%09%7D%2C%0A%09%09%22Collapse%20item%22%3A(e)%3D%3E%7B%0A%09%09%09%24d.find(%60ul.ul_show%3Anot(%3Ahas(.ul_show%3Anot(.keep_open)))%3Anot(.keep_open)%60).removeClass(%22ul_show%22).addClass(%22ul_hide%22)%3B%0A%09%09%09e.stopPropagation()%3B%0A%20%20%20%20%09%20%20%20%20return%20false%3B%0A%09%09%7D%2C%0A%09%09%22Collapse%20all%22%3A(e)%3D%3E%7B%0A%09%09%09%24d.find(%60ul.ul_show%3Anot(.keep_open)%60).removeClass(%22ul_show%22).addClass(%22ul_hide%22)%3B%0A%09%09%09e.stopPropagation()%3B%0A%20%20%20%20%09%20%20%20%20return%20false%3B%0A%09%09%7D%2C%0A%09%09%22Close%20dialog%22%3A(e)%3D%3E%7B%0A%09%09%09%24d.dialog(%22close%22)%3B%0A%09%09%09e.stopPropagation()%3B%0A%20%20%20%20%09%20%20%20%20return%20false%3B%0A%09%09%7D%2C%0A%09%09%22Options%22%3A(e)%3D%3E%7B%0A%09%09%09rOptions()%3B%0A%09%09%09e.stopPropagation()%3B%0A%20%20%20%20%09%20%20%20%20return%20false%3B%0A%09%09%7D%2C%0A%09%09%22Copy%20value%20and%20close%22%3A(e)%3D%3E%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20handlers%5B%22Copy%20value%22%5D(e)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20handlers%5B%22Close%20dialog%22%5D(e)%3B%0A%09%09%09e.stopPropagation%3F.call(this)%3B%0A%20%20%20%20%09%20%20%20%20return%20false%3B%0A%09%09%7D%2C%0A%09%09%22Copy%20value%22%3A(e)%3D%3E%7B%0A%09%09%09const%20scope%20%3D%20e.target%3B%0A%09%09%09%2F%2F%20replacing%20link%20with%20intput%20and%20select%20the%20value%0A%09%09%09const%20val%20%3D%20%24(scope).text()%3B%0A%09%09%09%24(scope).hide().after(%22%3Cinput%20id%3D'%22%20%2B%20Id%20%2B%20%22i'%3E%22)%3B%0A%09%09%09%24d.find(%22%23%22%20%2B%20Id%20%2B%20%22i%22).val(val).select()%3B%0A%09%09%09%2F%2F%20attempt%20to%20copy%20value%0A%09%09%09if%20(document.execCommand(%22copy%22%2C%20false%2C%20null))%7B%0A%09%09%09%09%2F%2F%20if%20copied%2C%20display%20a%20message%20for%20a%20second%0A%09%09%09%09%24d.find(%22%23%22%20%2B%20Id%20%2B%20%22i%22).attr(%22disabled%22%2C%20%22disabled%22).css(%22color%22%2C%22red%22).val(%22Copied!%22)%3B%0A%09%09%09%09setTimeout(()%3D%3E%7B%0A%09%09%09%09%09%24d.find(%22%23%22%20%2B%20Id%20%2B%20%22i%22).remove()%3B%0A%09%09%09%09%09%24(scope).show()%3B%0A%09%09%09%09%7D%2C%20700)%3B%0A%09%09%09%7D%20else%20%7B%0A%09%09%09%09%2F%2F%20if%20failed%20to%20copy%2C%20leave%20input%20until%20blur%2C%20so%20it%20can%20be%20copied%20manually%0A%09%09%09%09%24d.find(%22%23%22%20%2B%20Id%20%2B%20%22i%22).blur(()%3D%3E%7B%0A%09%09%09%09%09%24(this).remove()%3B%0A%09%09%09%09%09%24d.find(%22a%22).show()%3B%0A%09%09%09%09%7D)%3B%20%20%20%20%09%0A%09%09%09%7D%0A%09%09%09e.stopPropagation%3F.call(this)%3B%0A%20%20%20%20%09%20%20%20%20return%20false%3B%0A%09%09%7D%2C%0A%09%09%22Invoke%20applet%20method%22%3A%20(e)%20%3D%3E%20%7B%0A%09%09%09var%20%24target%20%3D%20%24(e.target)%3B%0A%09%09%09var%20applet%20%3D%20SiebelApp.S_App.GetActiveView().GetAppletMap()%5B%24target.attr(%22data-applet%22)%5D%3B%0A%09%09%09const%20method%20%3D%20%24target.text()%3B%0A%09%09%09applet.InvokeMethod(method)%3B%0A%09%09%09e.stopPropagation%3F.call(this)%3B%0A%20%20%20%20%09%20%20%20%20return%20false%3B%0A%09%09%7D%2C%0A%09%09%22Invoke%20control%20method%22%3A%20(e)%20%3D%3E%20%7B%0A%09%09%09var%20%24target%20%3D%20%24(e.target)%3B%0A%09%09%09var%20applet%20%3D%20SiebelApp.S_App.GetActiveView().GetAppletMap()%5B%24target.attr(%22data-applet%22)%5D%3B%0A%09%09%09var%20control%20%3D%20applet.GetControls()%5B%24target.attr(%22data-control%22)%5D%3B%0A%09%09%09const%20method%20%3D%20%24target.text()%3B%0A%09%09%09SiebelApp.S_App.uiStatus.Busy()%3B%0A%09%09%09try%20%7B%0A%09%09%09applet.GetPModel().OnControlEvent(SiebelApp.Constants.get(%22PHYEVENT_INVOKE_CONTROL%22)%2C%20control.GetMethodName()%2C%20control.GetMethodPropSet()%2C%20%7B%0A%09%09%09%09async%3A%20true%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20cb%3A%20()%3D%3ESiebelApp.S_App.uiStatus.Free()%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D)%3B%0A%09%09%09%7D%20catch(e)%20%7B%0A%09%09%09%09console.error(e.toString())%3B%0A%09%09%09%09SiebelApp.S_App.uiStatus.Free()%3B%0A%09%09%09%7D%0A%09%09%09e.stopPropagation%3F.call(this)%3B%0A%20%20%20%20%09%20%20%20%20return%20false%3B%0A%09%09%7D%2C%0A%09%09%22Focus%22%3A%20(e)%20%3D%3E%20%7B%0A%09%09%09var%20%24target%20%3D%20%24(e.target)%3B%0A%09%09%09const%20%24el%20%3D%20%24(%24target.attr(%22data-focus%22))%3B%0A%09%09%09%24d.dialog('close')%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24el.focus()%3B%0A%09%09%09e.stopPropagation%3F.call(this)%3B%0A%20%20%20%20%09%20%20%20%20return%20false%3B%0A%09%09%7D%2C%0A%09%09%22Expand%20related%22%3A%20(e)%20%3D%3E%20%7B%0A%09%09%09var%20%24target%20%3D%20%24(e.target)%3B%0A%09%09%09var%20sel%20%3D%20%24target.attr(%22data-selector%22)%3B%0A%09%09%09%24d.find(%60ul.ul_show%3Anot(.keep_open)%60).removeClass(%22ul_show%22).addClass(%22ul_hide%22)%3B%0A%09%09%09%24d.find(sel).toggleClass(%5B'ul_show'%2C'ul_hide'%5D)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20e.stopPropagation%3F.call(this)%3B%0A%20%20%20%20%09%20%20%20%20return%20false%3B%0A%09%09%7D%0A%0A%09%7D%3B%0A%0A%20%20%20%20%2F%2F%20handle%20double%20click%0A%09if%20(%24d.length)%20%7B%0A%20%20%20%20%20%20%20%20var%20o%20%3D%20options%5B%22bmk_dbl%22%5D%3B%0A%20%20%20%20%20%20%20%20handlers%5Bo%5D()%3B%0A%20%20%20%20%20%20%20%20return%3B%0A%20%20%20%20%7D%0A%0A%09%2F%2F%20render%20the%20dialog%0A%09let%20guid%20%3D%200%3B%0A%09const%20css%20%3D%20%5B%60%3Cstyle%3E%60%2C%20...%5B%0A%09%09%60ul%20%7Bmargin-left%3A20px%7D%60%2C%0A%09%09%60a.x_active%20%7Btext-decoration%3Aunderline%7D%60%2C%0A%09%09%60a.x_hidden%20%7Bfont-style%3Aitalic%7D%60%2C%0A%09%09%60select%20%7Bdisplay%3Ablock%3B%20margin-bottom%3A15px%7D%60%2C%0A%09%09%60.options%20%7Bbackground-color%3Alightgray%3B%20padding%3A15px%3B%20margin%3A10px%7D%60%2C%0A%09%09%60.ul_hide%20%7Bdisplay%3Anone%7D%60%2C%0A%09%09%60.ul_show%20%7Bborder-bottom%3A%201px%20solid%3B%20border-top%3A%201px%20solid%3B%20margin%3A%205px%3B%20padding%3A%205px%3B%20border-color%3A%20lightgray%3B%7D%60%2C%0A%09%09options%5B%22focus_feature%22%5D%20%3D%3D%20%22true%22%20%3F%20%60ul%3Ahas(.ul_show%3Anot(.keep_open))%20%3Ais(label%2Ca)%20%7Bcolor%3Adarkgray!important%7D%60%3A%60%60%2C%0A%20%20%20%09%09options%5B%22focus_feature%22%5D%20%3D%3D%20%22true%22%20%3F%20%60ul.ul_show%3Anot(%3Ahas(.ul_show%3Anot(.keep_open)))%20%3Ais(label%2Ca)%20%7Bcolor%3Ablack!important%7D%60%3A%60%60%2C%0A%09%09%60a%5Bdata-ul%5D%20%7Bfont-weight%3Abold%7D%60%2C%0A%09%09%60a%5Bdata-ul%5D%3Abefore%20%7Bcontent%3A%22%3E%20%22%3B%20opacity%3A0.5%3B%20font-style%3Anormal%7D%60%2C%0A%09%09%60a%5Bdata-handler%5D%3Abefore%2C%20a%5Bdata-focus%5D%3Abefore%20%7Bcontent%3A%22%3C%5B%22%3B%20opacity%3A0.5%3B%20font-style%3Anormal%7D%60%2C%0A%09%09%60a%5Bdata-handler%5D%3Aafter%2C%20a%5Bdata-focus%5D%3Aafter%20%7Bcontent%3A%22%5D%3E%22%3B%20opacity%3A0.5%3B%20font-style%3Anormal%7D%60%2C%0A%09%09%60label%20%7Bfont-size%3A1rem%3B%20margin%3A0px%3B%20font-weight%3Abold%3B%7D%60%2C%0A%09%09%60table%20%7Bdisplay%3Ablock%3B%20overflow-x%3Aauto%3B%20whitespace%3A%20nowrap%7D%60%2C%0A%09%09%60td%20%7Bborder%3Asolid%201px%7D%60%2C%0A%09%09%60.options%20select%20%7Bwidth%3A250px%7D%60%2C%0A%09%5D.map((i)%3D%3Ei%20%3F%20%60.%24%7BId%7D%20%24%7Bi%7D%60%3A%60%60)%2C%60%3C%2Fstyle%3E%60%5D.join(%22%5Cn%22)%3B%0A%0A%09%24d%20%3D%20%24(%60%3Cdiv%20class%3D%22container%22%20title%3D%22About%20View%202.0%22%3E%24%7BrApplication()%7D%24%7Bcss%7D%3C%2Fdiv%3E%60).dialog(%7B%0A%09%09dragStop%3A%20()%20%3D%3E%20%24d.dialog(%7Bheight%3A'auto'%7D)%2C%20%0A%09%09classes%3A%20%7B%22ui-dialog%22%3A%20Id%7D%2C%0A%09%09modal%3A%20true%2C%0A%09%09width%3A%20options%5B%22width%22%5D%2C%0A%09%09close%3A%20()%20%3D%3E%20%24d.dialog('destroy').remove()%2C%0A%09%09buttons%3A%20%5B%0A%09%09%09%7B%0A%09%09%09%20%20%20text%3A'Help'%2C%0A%09%09%09%20%20%20click%3A%20()%20%3D%3E%20window.open('http%3A%2F%2Fxapuk.com%2Findex.php%3Ftopic%3D145'%2C'_blank')%0A%09%09%09%7D%2C%7B%0A%09%09%09%20%20%20text%3A'Feedback'%2C%0A%09%09%09%20%20%20click%3A%20()%20%3D%3E%20window.location.href%3D%22mailto%3Avbabkin%40gmail.com%3Fsubject%3DAboutView2%22%0A%09%09%09%7D%2C%7B%0A%09%09%09%20%20%20text%3A'Settings'%2C%0A%09%09%09%20%20%20click%3A%20rOptions%2C%0A%09%09%09%7D%2C%7B%0A%09%09%09%20%20%20text%3A'Reset%20Settings'%2C%0A%09%09%09%20%20%20click%3A%20resetOptions%0A%09%09%09%7D%2C%7B%0A%09%09%09%20%20%20text%3A'Close%20(esc)'%2C%0A%09%09%09%20%20%20click%3A%20()%20%3D%3E%20%24d.dialog('close')%0A%09%09%09%7D%0A%09%09%5D%0A%09%7D)%3B%0A%09%0A%09function%20dispatchEvent(e%2C%20cb)%20%7B%0A%09%09var%20%24target%20%3D%20%24(e.target)%3B%0A%09%09if%20(cb.name%3F.indexOf(%22Special%22)%20%3E%200)%20%7B%0A%09%09%09if%20(%24target.attr(%22data-handler%22)%20%3D%3D%20%22applet%20method%22)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20handlers%5B%22Invoke%20applet%20method%22%5D(e)%3B%0A%09%09%09%7D%20else%20if%20(%24target.attr(%22data-handler%22)%20%3D%3D%20%22control%20method%22)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20handlers%5B%22Invoke%20control%20method%22%5D(e)%3B%0A%09%09%09%7D%20else%20if%20(%24target.attr(%22data-selector%22))%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20handlers%5B%22Expand%20related%22%5D(e)%3B%0A%09%09%09%7D%0A%09%09%7D%0A%09%09if%20(%24target.attr(%22data-focus%22))%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20handlers%5B%22Focus%22%5D(e)%3B%0A%09%09%7D%0A%20%20%20%20%20%20%20%20return%20cb(e)%3B%0A%09%7D%0A%0A%20%20%20%20%24d.contextmenu(handlers%5Boptions%5B%22ws_right%22%5D%5D)%3B%0A%20%20%20%20%24d.click(handlers%5Boptions%5B%22ws_click%22%5D%5D)%3B%0A%20%20%20%20%24d.find(%22a%22).off(%22click%22).click((e)%3D%3EdispatchEvent(e%2C%20handlers%5Boptions%5B%22link_click%22%5D%5D))%3B%0A%20%20%20%20%24d.find(%22a%22).off(%22contextmenu%22).contextmenu((e)%3D%3EdispatchEvent(e%2C%20handlers%5Boptions%5B%22link_right%22%5D%5D))%3B%0A%20%20%20%20%24(%22.ui-widget-overlay%22).click(handlers%5Boptions%5B%22out_click%22%5D%5D)%3B%0A%20%20%20%20%24(%22.ui-widget-overlay%22).contextmenu(handlers%5Boptions%5B%22out_right%22%5D%5D)%3B%0A%0A%20%20%20%20function%20resetOptions(e)%20%7B%0A%09%09options%20%3D%20%7B%0A%09%09%09%22bmk_dbl%22%3A%20%22Expand%20active%20context%22%2C%0A%09%09%09%22ws_click%22%3A%20%22None%22%2C%0A%09%09%09%22ws_right%22%3A%20%22Collapse%20item%22%2C%0A%09%09%09%22link_click%22%3A%20%22Copy%20value%22%2C%0A%09%09%09%22link_right%22%3A%20%22Expand%20%2F%20Special%22%2C%0A%09%09%09%22out_click%22%3A%20%22Close%20dialog%22%2C%0A%09%09%09%22out_right%22%3A%20%22Close%20dialog%22%2C%0A%09%09%09%22adv%22%3A%20%22true%22%2C%0A%09%09%09%22width%22%3A%20%221000%22%2C%0A%09%09%09%22ctrl_list_by%22%3A%20%22name%22%2C%0A%09%09%09%22applet_list%22%3A%20%22applet%20%2F%20bc%22%2C%0A%09%09%09%22applet_list_by%22%3A%20%22name%22%2C%0A%09%09%09%22focus_feature%22%3A%22false%22%0A%09%09%7D%3B%0A%09%09localStorage.setItem(Id%2C%20JSON.stringify(options))%3B%0A%09%7D%0A%0A%20%20%20%20%2F%2F%20render%20functions%0A%20%20%20%20function%20rDropdown(caption%2C%20field%2C%20list)%20%7B%0A%20%20%20%20%09const%20id%20%3D%20field%3B%0A%20%20%20%20%09const%20value%20%3D%20options%5Bfield%5D%3B%0A%20%20%20%20%20%20%20%20return%20%5B%60%3Cli%3E%60%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%60%3Clabel%20for%3D%22%24%7Bid%7D%22%3E%24%7Bcaption%7D%3C%2Flabel%3E%60%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%60%3Cselect%20id%3D%22%24%7Bid%7D%22%3E%60%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20list.map((i)%3D%3E%60%3Coption%20value%3D%22%24%7Bi%7D%22%20%24%7Bi%3D%3Dvalue%3F'selected'%3A''%7D%3E%24%7Bi%7D%3C%2Foption%3E%60)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%60%3C%2Fselect%3E%60%2C%0A%20%20%20%20%20%20%20%20%60%3Cli%3E%60%5D.join(%22%22)%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20function%20rOptions()%20%7B%0A%20%20%20%20%20%20%20%20if%20(%24d.find(%22.options%22).length)%20%7B%0A%20%20%20%20%20%20%20%20%09%24d.find(%22.options%22).remove()%3B%0A%20%20%20%20%20%20%20%20%7D%20else%20%7B%0A%20%20%20%20%20%20%20%20%09let%20html%20%3D%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%60%3Cdiv%20class%3D%22options%22%3E%60%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%60%3Ch4%3ESETTINGS%3C%2Fh4%3E%60%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20rDropdown(%60Advanced%20properties%60%2C%20%60adv%60%2C%20%5B%60false%60%2C%20%60true%60%5D)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20rDropdown(%60Dialog%20width%60%2C%20%60width%60%2C%20%5B%60600%60%2C%20%60800%60%2C%20%601000%60%5D)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20rDropdown(%60Show%20in%20main%20list%60%2C%20%60applet_list%60%2C%20%5B%60applet%60%2C%20%60applet%20%2F%20bc%60%2C%20%60applet%20%2F%20bc%20%2F%20rowid%60%5D)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20rDropdown(%60List%20applets%20by%60%2C%20%60applet_list_by%60%2C%20%5B%60name%60%2C%20%60title%60%5D)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20rDropdown(%60List%20controls%20by%60%2C%20%60ctrl_list_by%60%2C%20%5B%60name%60%2C%20%60caption%60%5D)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20rDropdown(%60Link%20click%60%2C%20%60link_click%60%2C%20%5B%60Copy%20value%60%2C%20%60Copy%20value%20and%20close%60%2C%20%60Expand%20%2F%20Special%60%2C%20%60None%60%5D)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20rDropdown(%60Link%20right%20click%60%2C%20%60link_right%60%2C%20%5B%60Copy%20value%60%2C%20%60Copy%20value%20and%20close%60%2C%20%60Expand%20%2F%20Special%60%2C%20%60None%60%5D)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20rDropdown(%60Bookmarklet%20double%20click%60%2C%20%60bmk_dbl%60%2C%20%5B%60Expand%20active%20context%60%2C%20%60Copy%20active%20applet%60%5D)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20rDropdown(%60Whitespace%20click%60%2C%20%60ws_click%60%2C%20%5B%60None%60%2C%20%60Close%20dialog%60%2C%20%60Options%60%2C%20%60Expand%20active%20context%60%2C%20%60Collapse%20item%60%2C%20%60Collapse%20all%60%5D)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20rDropdown(%60Whitespace%20right%20click%60%2C%20%60ws_right%60%2C%20%5B%60None%60%2C%20%60Close%20dialog%60%2C%20%60Options%60%2C%20%60Expand%20active%20context%60%2C%20%60Collapse%20item%60%2C%20%60Collapse%20all%60%5D)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20rDropdown(%60Outside%20click%60%2C%20%60out_click%60%2C%20%5B%60Close%20dialog%60%2C%20%60Expand%20active%20context%60%2C%20%60Collapse%20item%60%2C%20%60Collapse%20all%60%5D)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20rDropdown(%60Outside%20right%20click%60%2C%20%60out_right%60%2C%20%5B%60Close%20dialog%60%2C%20%60Expand%20active%20context%60%2C%20%60Collapse%20item%60%2C%20%60Collapse%20all%60%5D)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20rDropdown(%60Focus%20feature%60%2C%20%60focus_feature%60%2C%20%5B%60false%60%2C%20%60true%60%5D)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%60%3C%5Cdiv%3E%60%0A%20%20%20%20%20%20%20%20%09%5D.join(%22%5Cn%22)%3B%0A%20%20%20%20%20%20%20%20%09%24d.append(html)%3B%0A%20%20%20%20%20%20%20%20%09%24d.find(%22select%22).change((e)%3D%3E%7B%0A%09%09%09%09const%20c%20%3D%20e%3F.target%3B%0A%09%09%09%09if%20(c)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20options%5Bc.id%5D%20%3D%20c.value%3B%0A%09%09%09%09%20%20%20%20localStorage.setItem(Id%2C%20JSON.stringify(options))%3B%0A%09%09%09%09%7D%0A%09%09%09%7D)%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%0A%20%20%20%20function%20rPS(prop)%20%7B%0A%20%20%20%20%09return%20rItem(%60%3Ca%20href%3D'%23'%3E%24%7Bprop%5B0%5D%7D%3C%2Fa%3E%60%2C%20prop%5B1%5D)%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20function%20rHierarchy(caption%2C%20value%2C%20advanced)%20%7B%0A%20%20%20%20%09var%20a%20%3D%20%5B%5D%3B%0A%20%20%20%20%20%20%20%20while%20(%22object%22%20%3D%3D%3D%20typeof%20value%20%26%26%20value%3F.constructor%3F.name%3F.length%20%3E%201)%20%7B%0A%20%20%20%20%20%20%20%20%09a.push(value.constructor.name)%3B%0A%20%20%20%20%20%20%20%20%09value%20%3D%20value.constructor.superclass%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%09return%20rItem(caption%2C%20a%2C%20advanced)%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20function%20rItem(caption%2C%20value%2C%20advanced%2C%20attribs%3D%7B%7D)%20%7B%0A%20%20%20%20%20%20%20%20if%20(value%20%26%26%20(!Array.isArray(value)%20%7C%7C%20value.length)%20%7C%7C%20%22boolean%22%20%3D%3D%3D%20typeof%20value)%20%7B%0A%20%20%20%20%20%20%20%20%09if%20(!advanced%20%7C%7C%20options%5B%22adv%22%5D%20%3D%3D%3D%20%60true%60)%20%7B%0A%09%09%09%09guid%2B%2B%3B%0A%09%09%09%09let%20id%20%3D%20Id%20%2B%20guid%3B%0A%09%09%09%09let%20sAttr%20%3D%20Object.entries(attribs).map((%5Bp%2Cv%5D)%3D%3E%60%24%7Bp%7D%3D%22%24%7Bv%7D%22%60).join(%22%20%22)%3B%0A%09%09%09%09return%20(Array.isArray(value)%20%3F%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%60%3Cli%3E%60%2C%0A%09%09%09%09%09%60%3Clabel%20for%3D%22%24%7Bid%7D_0%22%3E%60%2Ccaption%2C%60%3A%3C%2Flabel%3E%20%60%2C%0A%09%09%09%09%09value.map((e%2C%20i)%20%3D%3E%60%3Ca%20href%3D%22%23%22%20id%3D%22%24%7Bid%7D_%24%7Bi%7D%22%20%24%7BsAttr%7D%3E%24%7Be%7D%3C%2Fa%3E%60).join(%22%20%3E%20%22)%2C%0A%09%09%09%09%09%60%3C%2Fli%3E%60%0A%09%09%09%09%5D%20%3A%20%5B%0A%09%09%09%09%09%60%3Cli%3E%60%2C%0A%09%09%09%09%09%60%3Clabel%20for%3D%22%24%7Bid%7D%22%3E%60%2Ccaption%2C%60%3A%3C%2Flabel%3E%20%60%2C%0A%09%09%09%09%09%60%3Ca%20href%3D%22%23%22%20id%3D%22%24%7Bid%7D%22%20%24%7BsAttr%7D%3E%60%2CescapeHtml(value)%2C%60%3C%2Fa%3E%60%2C%0A%09%09%09%09%09%60%3C%2Fli%3E%60%0A%09%09%09%09%5D).join(%22%22)%3B%0A%20%20%20%20%20%20%20%20%09%7D%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20return%20%22%22%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20function%20rControl(control)%20%7B%0A%20%20%20%20%09const%20id%20%3D%20control.GetInputName()%3B%0A%20%20%20%20%09const%20applet%20%3D%20control.GetApplet()%3B%0A%20%20%20%20%09const%20pr%20%3D%20SiebelAppFacade.ComponentMgr.FindComponent(applet.GetName())%3F.GetPR()%3B%0A%20%20%20%20%09const%20bc%20%3D%20applet.GetBusComp()%3B%0A%20%20%20%20%09const%20ps%20%3D%20control.GetMethodPropSet()%3B%0A%20%20%20%20%09const%20up%20%3D%20control.GetPMPropSet()%3B%0A%20%20%20%20%09const%20con%20%3D%20SiebelApp.Constants%3B%0A%20%20%20%20%09let%20sel%20%3D%20%60%23%24%7Bapplet.GetFullId()%7D%20%5Bname%3D%24%7Bcontrol.GetInputName()%7D%5D%60%3B%0A%20%20%20%20%09if%20(con.get(%22SWE_CTRL_RTCEMBEDDED%22)%20%3D%3D%3D%20control.GetUIType())%20%7B%0A%20%20%20%20%09%09sel%20%3D%20%60%23%24%7Bapplet.GetFullId()%7D%20%23cke_%24%7Bcontrol.GetInputName()%7D%60%3B%0A%20%20%20%20%09%7D%0A%20%20%20%20%09const%20cls%20%3D%20control%20%3D%3D%3D%20applet.GetActiveControl()%20%3F%20'x_active'%20%3A%20%24(sel).length%20%3D%3D%200%20%7C%7C%20%24(sel).is(%22%3Avisible%22)%20%3F%20''%20%3A%20'x_hidden'%0A%20%20%20%20%20%20%20%20return%20%5B%60%3Cli%3E%60%2C%0A%09%09%20%20%20%20%60%3Ca%20href%3D%22%23%22%20data-ul%3D%22%24%7Bid%7D_c%22%20class%3D%22%24%7Bcls%7D%22%3E%60%2C%20%0A%09%09%09%20%20%20%20options%5B%22ctrl_list_by%22%5D%20%3D%3D%20'caption'%20%26%26%20control.GetDisplayName()%20%3F%20control.GetDisplayName()%20%3A%20control.GetName()%20%2C%20%0A%09%09%09%60%3C%2Fa%3E%60%2C%0A%09%09%09%60%3Cul%20id%3D%22%24%7Bid%7D_c%22%20class%3D%22ul_hide%22%3E%60%2C%0A%09%09%09rItem(control.GetControlType()%20%3D%3D%20con.get(%22SWE_PST_COL%22)%3F%22List%20column%22%3Acontrol.GetControlType()%20%3D%3D%20con.get(%22SWE_PST_CNTRL%22)%3F%22Control%22%3Acontrol.GetControlType()%2C%20control.GetName())%2C%0A%09%09%09rItem(%22Display%20name%22%2C%20control.GetDisplayName())%2C%0A%09%09%09rItem(%22Field%22%2C%20control.GetFieldName()%2C%20false%2C%20%7B%22data-handler%22%3A%22field%22%2C%22data-selector%22%3A%60ul%23%24%7Bapplet.GetFullId()%7D_bc%2Cul%23%24%7Bapplet.GetFullId()%7D_bc_%24%7Bapplet.GetBusComp().GetFieldMap()%5Bcontrol.GetFieldName()%5D%3F.index%7D%60%7D)%2C%0A%09%09%09rItem(%22Value%22%2C%20bc%3F.GetFieldValue(control.GetFieldName()))%2C%0A%09%09%09rItem(%22Message%22%2C%20control.GetControlMsg())%2C%0A%09%09%09...(control.GetMessageVariableMap()%20%26%26%20Object.keys(control.GetMessageVariableMap()).length%20%3F%20%0A%09%09%09%09%5B%60%3Cli%3E%3Ca%20href%3D%22%23%22%20data-ul%3D%22%24%7Bid%7D_var%22%3EMessage%20variables%20(%24%7BObject.keys(control.GetMessageVariableMap()).length%7D)%3A%3C%2Fa%3E%3C%2Fli%3E%60%2C%0A%09%09%09%09%60%3Cul%20id%3D%22%24%7Bid%7D_var%22%20class%3D%22ul_hide%22%3E%60%2C%0A%09%09%09%09Object.entries(control.GetMessageVariableMap()).map(rPS).join(%22%5Cn%22)%2C%0A%09%09%09%09%60%3C%2Ful%3E%60%5D%20%3A%20%5B%5D)%2C%09%0A%09%09%09rItem(%22Type%22%2C%20control.GetUIType())%2C%0A%09%09%09rItem(%22LOV%22%2C%20control.GetLovType())%2C%0A%09%09%09rItem(%22MVG%22%2C%20control.IsMultiValue())%2C%0A%09%09%09rItem(%22Method%22%2C%20control.GetMethodName()%2C%20false%2C%20%7B%22data-handler%22%3A%22control%20method%22%2C%20%22data-applet%22%3Aapplet.GetName()%2C%22data-control%22%3Acontrol.GetName()%7D)%2C%0A%09%09%09...(options%5B%22adv%22%5D%20%3D%3D%3D%20%60true%60%20%26%26%20ps%20%26%26%20ps.propArrayLen%20%3F%20%0A%09%09%09%09%5B%60%3Cli%3E%3Ca%20href%3D%22%23%22%20data-ul%3D%22%24%7Bid%7D_method%22%3EMethod%20properties%20(%24%7Bps.propArrayLen%7D)%3A%3C%2Fa%3E%3C%2Fli%3E%60%2C%0A%09%09%09%09%60%3Cul%20id%3D%22%24%7Bid%7D_method%22%20class%3D%22ul_hide%22%3E%60%2C%0A%09%09%09%09Object.entries(ps.propArray).map(rPS).join(%22%5Cn%22)%2C%0A%09%09%09%09%60%3C%2Ful%3E%60%5D%20%3A%20%5B%5D)%2C%0A%09%09%09%2F%2FrItem(%22Id%22%2C%20id)%2C%0A%09%09%09rItem(%22Id%22%2C%20options%5B%22adv%22%5D%20%3D%3D%3D%20%60true%60%20%26%26%20%24(sel).is(%22%3Afocusable%22)%20%3F%20%5Bid%2C%20%60%3Ca%20href%3D%22%23%22%20data-focus%3D'%24%7Bsel%7D'%3EFocus%3C%2Fa%3E%60%5D%3Aid)%2C%09%0A%09%09%09rItem(%22Immidiate%20post%20changes%22%2C%20control.GetPostChanges())%2C%0A%09%09%09rItem(%22Display%20format%22%2C%20control.GetDisplayFormat())%2C%0A%09%09%09rItem(%22HTML%20attribute%22%2C%20control.GetHTMLAttr()%2C%20true)%2C%0A%09%09%09rItem(%22Display%20mode%22%2C%20control.GetDispMode())%2C%0A%09%09%09rItem(%22Popup%22%2C%20control.GetPopupType()%20%26%26%20%5Bcontrol.GetPopupType()%2C%20control.GetPopupWidth()%2C%20control.GetPopupHeight()%5D.join(%22%20%2F%20%22))%2C%0A%09%09%09rHierarchy(%22Plugin%20wrapper%22%2C%20SiebelApp.S_App.PluginBuilder.GetPwByControl(pr%2C%20control)%2C%20true)%2C%0A%09%09%09rItem(%22Length%22%2C%20control.GetFieldName()%20%3F%20control.GetMaxSize()%20%3A%20%22%22)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20...(options%5B%22adv%22%5D%20%3D%3D%3D%20%60true%60%20%26%26%20up%20%26%26%20up.propArrayLen%20%3F%20%0A%09%09%09%09%5B%60%3Cli%3E%3Ca%20href%3D%22%23%22%20data-ul%3D%22%24%7Bid%7D_up%22%3EUser%20properties%20(%24%7Bup.propArrayLen%7D)%3A%3C%2Fa%3E%3C%2Fli%3E%60%2C%0A%09%09%09%09%60%3Cul%20id%3D%22%24%7Bid%7D_up%22%20class%3D%22ul_hide%22%3E%60%2C%0A%09%09%09%09Object.entries(up.propArray).map(rPS).join(%22%5Cn%22)%2C%0A%09%09%09%09%60%3C%2Ful%3E%60%5D%20%3A%20%5B%5D)%2C%09%0A%09%09%09rItem(%22Object%22%2C%20%60SiebelApp.S_App.GetActiveView().GetAppletMap()%5B%22%24%7Bapplet.GetName()%7D%22%5D.GetControls()%5B%22%24%7Bcontrol.GetName()%7D%22%5D%60%2C%20true)%2C%0A%09%09%09%24(sel).length%20%3E%200%20%3F%20rItem(%22Node%22%2C%20%60%24(%22%24%7Bsel%7D%22)%60%2C%20true)%20%3A%20%60%60%2C%0A%09%09%60%3C%2Ful%3E%60%2C%60%3C%2Fli%3E%60%5D.join(%22%22)%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20function%20rApplet(applet)%20%7B%0A%20%20%20%20%09const%20cm%20%3D%20Object.values(applet.GetControls())%3B%0A%20%20%20%20%09const%20mm%20%3D%20applet.GetCanInvokeArray()%3B%0A%20%20%20%20%09const%20id%20%3D%20applet.GetFullId()%3B%0A%20%20%20%20%20%20%20%20return%20%5B%60%3Cul%20id%3D%22%24%7Bid%7D%22%20class%3D%22ul_hide%22%3E%60%2C%0A%09%09%09rItem(%22Applet%22%2C%20applet.GetName())%2C%0A%09%09%09rItem(%22BusComp%22%2C%20applet.GetBusComp()%3F.GetName()%2C%20false%2C%20%7B%22data-handler%22%3A%22buscomp%22%2C%20%22data-selector%22%3A%60ul%23%24%7Bapplet.GetFullId()%7D_bc%60%7D)%2C%0A%09%09%09rItem(%22Title%22%2C%20SiebelApp.S_App.LookupStringCache(applet.GetTitle()))%2C%0A%09%09%09rItem(%22Mode%22%2C%20applet.GetMode())%2C%0A%09%09%09rItem(%22Record%20counter%22%2C%20applet.GetPModel().GetStateUIMap().GetRowCounter%2C%20true)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20rHierarchy(%22PModel%22%2C%20applet.GetPModel()%2C%20true)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20rHierarchy(%22PRender%22%2C%20SiebelAppFacade.ComponentMgr.FindComponent(applet.GetName())%3F.GetPR()%2C%20true)%2C%0A%09%09%09rItem(%22Object%22%2C%20%60SiebelApp.S_App.GetActiveView().GetAppletMap()%5B%22%24%7Bapplet.GetName()%7D%22%5D%60%2C%20true)%2C%0A%09%09%09rItem(%22Node%22%2C%20%60%24(%22%23%24%7Bapplet.GetFullId()%7D%22)%60%2C%20true)%2C%0A%09%09%09%60%3Cli%3E%3Ca%20href%3D%22%23%22%20data-ul%3D%22%24%7Bid%7D_methods%22%3EMethods%20(%24%7Bmm.length%7D)%3A%3C%2Fa%3E%3C%2Fli%3E%60%2C%0A%09%09%09%60%3Cul%20id%3D%22%24%7Bid%7D_methods%22%20class%3D%22ul_hide%22%3E%60%2C%0A%09%09%09mm.map(m%3D%3E%5B%60%3Cli%3E%60%2C%20%60%3Ca%20href%3D%22%23%22%20data-handler%3D%22applet%20method%22%20data-applet%3D%22%24%7Bapplet.GetName()%7D%22%3E%60%2C%20m%2C%20%60%3C%2Fa%3E%60%2C%20%60%3C%2Fli%3E%60%5D.join(%22%22)).join(%22%5Cn%22)%2C%0A%09%09%09%60%3C%2Ful%3E%60%2C%0A%09%09%09%60%3Cli%3E%3Ca%20href%3D%22%23%22%20data-ul%3D%22%24%7Bid%7D_controls%22%3EControls%20(%24%7Bcm.length%7D)%3A%3C%2Fa%3E%3C%2Fli%3E%60%2C%0A%09%09%09%60%3Cul%20id%3D%22%24%7Bid%7D_controls%22%20class%3D%22ul_show%20keep_open%22%3E%60%2C%20%2F%2F%3Cul%20id%3D%22%24%7Bid%7D_controls%22%20class%3D%22ul_show%22%3E%0A%09%09%09...cm.map(rControl)%2C%0A%09%09%09%60%3C%2Ful%3E%60%2C%0A%09%09%60%3C%2Ful%3E%60%5D.join(%22%5Cn%22)%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20function%20rField(field%2C%20id)%20%7B%0A%20%20%20%20%09const%20bc%20%3D%20field.GetBusComp()%3B%0A%20%20%20%20%09const%20name%20%3D%20SiebelApp.S_App.LookupStringCache(field.GetName())%3B%0A%20%20%20%20%09return%20%5B%60%3Cli%3E%60%2C%0A%09%09%09%60%3Ca%20href%3D%22%23%22%20data-ul%3D%22%24%7Bid%7D%22%3E%60%2C%20name%2C%20%60%3C%2Fa%3E%60%2C%0A%09%09%09%60%3Cul%20id%3D%22%24%7Bid%7D%22%20class%3D%22ul_hide%22%3E%60%2C%0A%09%09%09rItem(%22Field%22%2C%20name)%2C%0A%09%09%09rItem(%22Value%22%2C%20field.GetBusComp().GetFieldValue(name))%2C%0A%09%09%09rItem(%22Type%22%2C%20field.GetDataType())%2C%0A%09%09%09rItem(%22Length%22%2C%20field.GetLength())%2C%0A%09%09%09rItem(%22Search%20spec%22%2C%20field.GetSearchSpec())%2C%0A%09%09%09rItem(%22Calculated%22%2C%20!!field.IsCalc())%2C%0A%09%09%09rItem(%22Bounded%20picklist%22%2C%20!!field.IsBoundedPick())%2C%0A%09%09%09rItem(%22Read%20only%22%2C%20!!field.IsReadOnly())%2C%0A%09%09%09rItem(%22Immediate%20post%20changes%22%2C%20!!field.IsPostChanges())%2C%0A%09%09%09rItem(%22Object%22%2C%20%60SiebelApp.S_App.GetBusObj().GetBusCompByName(%22%24%7Bbc.GetName()%7D%22).GetFieldMap()%5B%22%24%7Bname%7D%22%5D%60%2C%20true)%2C%0A%09%09%60%3C%2Ful%3E%60%2C%20%60%3C%2Fli%3E%60%5D.join(%22%5Cn%22)%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20function%20rBC(a%2C%20id)%20%7B%0A%20%20%20%20%09var%20bc%20%3D%20a.GetBusComp()%3B%0A%20%20%20%20%09const%20fields%20%3D%20Object.values(bc.GetFieldMap())%3B%0A%20%20%20%20%20%20%20%20return%20%5B%60%3Cul%20id%3D%22%24%7Bid%7D%22%20class%3D%22ul_hide%22%3E%60%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20rItem(%22BusComp%22%2C%20bc.GetName())%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20rItem(%22Commit%20pending%22%2C%20!!bc.commitPending%2C%20true)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20rItem(%22Can%20update%22%2C%20!!bc.canUpdate)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20rItem(%22Search%20spec%22%2C%20bc.GetSearchSpec())%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20rItem(%22Sort%20spec%22%2C%20bc.GetSortSpec())%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20rItem(%22Current%20row%20id%22%2C%20bc.GetIdValue())%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20rItem(%22Object%22%2C%20%60SiebelApp.S_App.GetBusObj().GetBusComp(%22%24%7Ba.GetBCId()%7D%22)%60%2C%20true)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%60%3Cli%3E%3Clabel%3E%3Ca%20href%3D%22%23%22%20data-ul%3D%22%24%7Bid%7D_rec%22%3ERecords%3A%20%24%7BMath.abs(bc.GetCurRowNum())%7D%20of%20%24%7Bbc.GetNumRows()%7D%24%7Bbc.IsNumRowsKnown()%3F''%3A'%2B'%7D%3C%2Fa%3E%3C%2Flabel%3E%3C%2Fli%3E%60%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%60%3Cul%20id%3D%22%24%7Bid%7D_rec%22%20class%3D%22ul_hide%22%3E%60%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%60%3Ctable%3E%60%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%60%3Ctr%3E%60%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20...Object.keys(bc.GetFieldMap()).map((i)%3D%3E%60%3Cth%3E%24%7Bi%7D%3C%2Fth%3E%60)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%60%3C%2Ftr%3E%60%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20...bc.GetRecordSet().map((r%2C%20i)%3D%3E%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%60%3Ctr%3E%60%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20...Object.values(r).map(v%3D%3E%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%60%3Ctd%3E%3Ca%20href%3D%22%23%22%20%24%7Bbc.GetSelection()%20%3D%3D%20i%20%3F%20%60%20class%3D%22x_active%22%60%20%3A%20%60%60%7D%3E%60%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20v%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%60%3C%2Fa%3E%3C%2Ftd%3E%60%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5D.join(%22%22))%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%60%3C%2Ftr%3E%60%0A%20%20%20%20%20%20%20%20%20%20%20%20%5D.join(%22%22))%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%60%3C%2Ftable%3E%60%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%60%3C%2Ful%3E%60%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%60%3Cli%3E%3Clabel%3E%3Ca%20href%3D%22%23%22%20data-ul%3D%22%24%7Bid%7D_fields%22%3EFields(%24%7Bbc.GetFieldList()%3F.length%7D)%3A%3C%2Fa%3E%3C%2Flabel%3E%3C%2Fli%3E%60%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%60%3Cul%20id%3D%22%24%7Bid%7D_fields%22%20class%3D%22ul_show%20keep_open%22%3E%60%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20...fields.map((field%2C%20i)%20%3D%3E%20rField(field%2C%20id%20%2B%20%22_%22%20%2B%20field.index))%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%60%3C%2Ful%3E%60%2C%0A%20%20%20%20%20%20%20%20%60%3C%2Ful%3E%60%5D.join(%22%5Cn%22)%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20function%20rApplication()%20%7B%0A%20%20%20%20%20%20%20%20const%20app%20%3D%20SiebelApp.S_App%3B%0A%20%20%20%20%20%20%20%20const%20view%20%3D%20app.GetActiveView()%3B%0A%20%20%20%20%20%20%20%20const%20bo%20%3D%20app%3F.GetBusObj()%3B%0A%20%20%20%20%20%20%20%20const%20bm%20%3D%20bo%3F.GetBCArray()%3B%0A%20%20%20%20%20%20%20%20const%20scrPM%20%3D%20SiebelApp.S_App.NavCtrlMngr()%3F.GetscreenNavigationPM()%3B%0A%20%20%20%20%20%20%20%20let%20am%20%3D%20Object.values(view%3F.GetAppletMap())%3B%0A%20%20%20%20%20%20%20%20var%20ws%20%3D%20SiebelApp.S_App.GetWSInfo().split(%22_%22)%3B%0A%20%20%20%20%20%20%20%20var%20wsver%20%3D%20ws.pop()%3B%0A%0A%20%20%20%20%20%20%20%20var%20amCache%20%3D%20%7B%7D%3B%0A%20%20%20%20%20%20%20%20Object.assign(amCache%2C%20view%3F.GetAppletMap())%3B%0A%0A%09%09%2F%2F%20Identifying%20a%20primary%20BC%0A%09%09var%20paa%20%3D%20Object.values(SiebelApp.S_App.GetActiveView().GetAppletMap()).filter((a)%20%3D%3E%20!a.GetParentApplet()%20%26%26%20(!a.GetBusComp()%20%7C%7C%20!a.GetBusComp().GetParentBusComp()))%3B%0A%09%09if%20(!paa.length)%20%7B%0A%09%09%09alert(%22Failed%20to%20identify%20a%20primary%20BusComp!%22)%0A%09%09%7D%0A%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20return%20%5B%60%3Cul%3E%60%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20rItem(%22Application%22%2C%20app.GetName())%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20rItem(%22Screen%22%2C%20scrPM%3F.Get(%22GetTabInfo%22)%5BscrPM%3F.Get(%22GetSelectedTabKey%22)%5D%3F.screenName)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20rItem(%22View%22%2C%20view.GetName())%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20rItem(%22Task%22%2C%20view.GetActiveTask())%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20rHierarchy(%22PModel%22%2C%20SiebelAppFacade.ComponentMgr.FindComponent(view.GetName())%3F.GetPM()%2C%20true)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20rHierarchy(%22PRender%22%2C%20SiebelAppFacade.ComponentMgr.FindComponent(view.GetName())%3F.GetPR()%2C%20true)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20rItem(%22BusObject%22%2C%20bo%3F.GetName())%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20rItem(%22Workspace%22%2C%20%5Bws.join(%22_%22)%2C%20wsver%5D)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%60%3Clabel%3EApplets%20(%24%7Bam.length%7D)%20%2F%20BusComps%20(%24%7Bbm.length%7D)%3A%3C%2Flabel%3E%60%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%60%3Cul%3E%60%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20hierBC(paa%5B0%5D.GetBusComp()%2C%200%2C%20amCache)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20...Object.values(amCache).map((a)%3D%3ErAppletName(a%2C%200%2C%20amCache))%2C%0A%20%20%20%20%20%20%20%20%60%3C%2Ful%3E%3C%2Ful%3E%60%5D.join(%22%5Cn%22)%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20%2F%2F%20prints%20applet%20name%0A%20%20%20%20function%20rAppletName(a%2C%20l%2C%20amCache)%20%7B%0A%20%20%20%20%09delete%20amCache%5Ba.GetName()%5D%3B%0A%20%20%20%20%09return%20%5B%60%3Cli%3E%60%2C%0A%09%09%09%09%20%20%20%20%60%3Cul%3E%60.repeat(l)%2C%0A%09%09%09%09%09%60%3Ca%20href%3D%22%23%22%20data-ul%3D%22%24%7Ba.GetFullId()%7D%22%20class%3D%22%24%7Ba%20%3D%3D%3D%20SiebelApp.S_App.GetActiveView().GetActiveApplet()%20%3F%20'x_active'%20%3A%20%24(%60%23%24%7Ba.GetFullId()%7D%60).is(%22%3Avisible%22)%20%3F%20''%20%3A%20'x_hidden'%7D%22%3E%60%2C%0A%09%09%09%09%09%09options%5B'applet_list_by'%5D%20%3D%3D%20'title'%20%26%26%20SiebelApp.S_App.LookupStringCache(a.GetTitle())%20%3F%20SiebelApp.S_App.LookupStringCache(a.GetTitle())%20%3A%20a.GetName()%2C%0A%09%09%09%09%09%60%3C%2Fa%3E%60%2C%0A%09%09%09%09%09a.GetBusComp()%20%26%26%20options%5B%22applet_list%22%5D.indexOf(%22bc%22)%20%3E%20-1%20%3F%20%60%20%2F%20%3Ca%20href%3D%22%23%22%20data-ul%3D%22%24%7Ba.GetFullId()%7D_bc%22%3E%24%7Ba.GetBusComp().GetName()%7D%3C%2Fa%3E%60%20%3A%20%60%60%2C%0A%09%09%09%09%09a.GetBusComp()%20%26%26%20a.GetBusComp().GetIdValue()%20%26%26%20options%5B%22applet_list%22%5D.indexOf(%22rowid%22)%20%3E%20-1%20%3F%20%60%20%2F%20%3Ca%20href%3D%22%23%22%3E%24%7Ba.GetBusComp().GetIdValue()%7D%3C%2Fa%3E%60%20%3A%20%60%60%2C%0A%09%09%09%09%09rApplet(a)%2C%0A%09%09%09%09%09a.GetBusComp()%20%26%26%20rBC(a%2C%20a.GetFullId()%20%2B%20%22_bc%22)%2C%0A%09%09%09%09%09%60%3C%2Ful%3E%60.repeat(l)%2C%0A%09%09%09%09%60%3C%2Fli%3E%60%5D.join(%22%22)%3B%0A%20%20%20%20%7D%0A%0A%09%2F%2F%20prints%20applets%20based%20on%20bc%20or%20parent%20applet%20(rec)%0A%09function%20hierApplet(bc%2C%20pa%2C%20l%2C%20amCache)%20%7B%0A%09%09return%20Object.values(amCache).filter((a)%20%3D%3E%20bc%20%26%26%20a.GetBusComp()%20%3D%3D%3D%20bc%20%7C%7C%20pa%20%26%26%20a.GetParentApplet()%20%3D%3D%3D%20pa).map((a)%20%3D%3E%20!(a.GetName()%20in%20amCache)%20%3F%20%22%22%20%3A%20%5B%0A%09%09%09rAppletName(a%2C%20l%2C%20amCache)%2C%0A%09%09%09hierApplet(null%2C%20a%2C%20l%20%2B%201%2C%20amCache)%20%2F%2F%20look%20for%20child%20applets%0A%09%09%5D.join(%22%5Cn%22))%3B%0A%09%7D%0A%0A%09%2F%2F%20prints%20applets%20based%20on%20BC%20hierarchy%20(rec)%0A%09function%20hierBC(bc%2C%20l%2C%20amCache)%20%7B%0A%09%09return%20%5B%0A%09%09%09hierApplet(bc%2C%20null%2C%20l%2C%20amCache)%3F.join(%22%5Cn%22)%2C%0A%09%09%09...SiebelApp.S_App.GetActiveBusObj().GetBCArray().filter((e)%20%3D%3E%20e.GetParentBusComp()%20%3D%3D%3D%20bc).map((b)%20%3D%3E%20hierBC(b%2C%20l%20%2B%201%2C%20amCache))%0A%09%09%5D.join(%22%5Cn%22)%3B%0A%09%7D%0A%0A%09%2F%2F%20utilities%0A%09function%20escapeHtml(html)%20%7B%0A%09%09return%20html.toString()%0A%09%09%20%20%20%20.replace(%2F%26%2Fg%2C%20%22%26amp%3B%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20.replace(%2F%3C%2Fg%2C%20%22%26lt%3B%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20.replace(%2F%3E%2Fg%2C%20%22%26gt%3B%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20.replace(%2F%22%2Fg%2C%20%22%26quot%3B%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20.replace(%2F'%2Fg%2C%20%22%26%23039%3B%22)%3B%0A%09%7D%0A%7D)()%7D)()%3B 
  • About View 2.0 bookmarklet link for drag and drop install.

I used Caio's tool to convert source code into a bookmarklet.

Previous version can be found here and on GitHub.

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).


Back-end


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.


Front-end


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.


My implementation


I've advanced the initial version of Inspect Workspace UI with quite a few features, so check out my latest implementation below.

Features:

  • The text field accepts multiple formats:
    • an exact workspace name: vbabkin_20200924_d419_1
    • a search pattern of workspace name: *d419*
    • a full search specification for the Repository Workspace BC: [Parent Name] = "Release 21" AND [Created By] = LoginId()
    • leave it empty to search or inspect most recent undelivered workspaces created by the active user
  • Hit Enter or click [Search button] to search for 10 most recent workspaces matching the provided name/pattern/spec.
  • Click one of the workspaces link in the search result list to inspect the workspace.
  • Hit Ctrl+Enter or click [Inspect] button to inspect the most recent workspaces matching the provided name/pattern/spec.
  • Run the snippet twice or double-click the bookmark to inspect your latest undelivered workspace.
  • The text field remembers a history of 5 recent calls.
  • It highlights searching pattern in result list.
  • It prints calls, errors and success messages with a timestamp.
  • Click [Help] button to toggle help instructions.
  • To close the dialog:
    • click the [X] or [Close] button
    • hit the [Escape] key
    • click outside the dialog
  • Right-click a workspace link to copy it.

Source code (v1.5):

  • Business service: File
  • Front-end: Snippet | File
  • /* 
    @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>"));
        }
    })();
  • Bookmarklet code: Snippet
  • 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()}})})();
  • Bookmarklet link: InspectWS
  • Version history is available in GitHub

Thanks to Manan for his contribution!

If "Siebel Workflow Process" (WF) is your primary tool for business process automation as they are for me, it might be a useful topic for you.

Familiar with the error "The value entered in field Process Business Object of buscomp Workflow Process Deployment does not match any value in the bounded pick list FOW SRF Business Object." (SBL-DAT-00225) when activating a custom BO-based workflow process in Siebel Tools? The usual way to solve it is to activate a process through [Administration - Business Process] screen. It is still annoying, as you will lose your current context, and extremely annoying when you don't have BP screen or access to it in the test application. In this topic, I offer you another, effortless way to activate a WF process. On the same view/session where you are testing it, and with just a couple clicks.

Ok, let's get down to implementation.

1. Publishing the service.

As you might have guessed, we will be using the "Workflow Admin Service". Out of many ways to run the BS, I'll cover OUI scripts.

So, let's make the BS accessible from a browser script. Here, I recommend using a service runner - straight forward concept when you publish one to run any other service in the system. If you are not comfortable with granting access to all services in production, use a client-side business service for service runner. So, it wouldn't be automatically migrated to higher environments along with a repository.

Here is how your service runner method can look like:

function InvokeServiceMethod(Inputs, Outputs) {
    var bs;
    var sBS = Inputs.GetProperty("Service");
    Inputs.RemoveProperty("Service");
    var sMethod = Inputs.GetProperty("Method");
    Inputs.RemoveProperty("Method");
    
    try {
        bs = TheApplication().GetService(sBS);
        bs.InvokeMethod(sMethod, Inputs, Outputs);
    } catch(e) {
        throw e;
    } finally {
        bs = null;
    }
}

Here is a full version of my client-side Business Service - FWK Runtime. Simply, download and import it on Administration - Business Service screen.

And finally, granting access to run a service runner from browser scripts:

2. JS code with UI and service call.

We will need a UI to enter a WF process name and a button to run the BS. We will be using JQuery dialog with just a couple of elements. It is going to look like this:

Source code and installation links are below.

  • Front-end: Snippet
  • /* 
    @desc bookmarklet UI to activate Siebel Workflow Process
    @author VB (xapuk.com)
    @version 1
    */
    if ("undefined" == typeof SiebelApp) {
        alert("It works only in Siebel OUI session!");
    } else {
    
        // snippet id
        var id = "SiebelWFDeploy";
    
        // localStorage to store the history
        var aHist = window.localStorage[id]?JSON.parse(window.localStorage[id]):[];
    
        // just in case (experimental)
        $("#" + id).parent().remove();
    
        // constructing dialog content
        var s = '<div title="Activate workflow">';
        s += '<input id = "' + id + '" type="text" list="' + id + 'List" style="width:100%" value="' + (aHist.length?aHist[0]:"") + '">'; // most recent
        s += '<label class="pt-3">Recent workflows:</label><ul>';
        for (var i =0; i < aHist.length && i < 5; i++){ // five recent values as links
            s += '<li><a href="#">' + aHist[i] + '</a></li>';
        }
        s += '</ul></div>';
    
        // open dialog
        var $d = $(s).dialog({
            modal: true,
            width: 640,
            open: function() {
                $('#' + id).autocomplete({source: aHist});
                $('#' + id).focus().select(); // autofocus
            },
            close: function() {
                $(this).dialog('destroy').remove();
            },
            buttons: [{
                text: 'Activate (Enter/Click)',
                click: function(){
                    go($d.find('#' + id).val());
                }
            }, {
                text: 'Close (Esc)',
                click: function() {
                    $(this).dialog('close');
                }
            }]
        });
    
        // key bindings
        $d.keyup(function(event) {
            // enter
            if (event.keyCode === 13) { 
                go($d.find('#' + id).val());
            }
        });
    
        // running function on anchor link click
        $d.find("a").click(function(event) {
            go($(this).html());
        });
    }
    
    // Activate
    function go(name) {
        if (name){
            // moving recent view to the top
            if (aHist.indexOf(name) > -1){
               aHist.splice(aHist.indexOf(name),1);
            }
            aHist.unshift(name);
            window.localStorage[id] = JSON.stringify(aHist); 
            $d.dialog('close');
    
            // invoke BS
            var service = SiebelApp.S_App.GetService("FWK Runtime");
            var ps = SiebelApp.S_App.NewPropertySet();
            ps.SetProperty("Service", "Workflow Admin Service");
            ps.SetProperty("Method", "Activate");
            ps.SetProperty("FlowSearchSpec", "[Process Name] = '" + name + "'");
            var outputSet = service.InvokeMethod("InvokeServiceMethod", ps);
            if (console){
                console.log(outputSet);
            }
            if (outputSet.GetProperty("Status") == "Error"){
                alert(outputSet.GetChildByType("Errors").GetChild(0).GetProperty("ErrMsg"));    
            }else{
                if (outputSet.GetChildByType("ResultSet").GetProperty("NumFlowActivated") === "0"){
                    alert("Process definition [" + name + "] not found");
                }else{
                    alert("Done!");
                }
            }
        }
    }
  • Bookmarklet code: Snippet
  • javascript:function go(e){if(e){aHist.indexOf(e)>-1&&aHist.splice(aHist.indexOf(e),1),aHist.unshift(e),window.localStorage[id]=JSON.stringify(aHist),$d.dialog("close");var i=SiebelApp.S_App.GetService("FWK Runtime"),t=SiebelApp.S_App.NewPropertySet();t.SetProperty("Service","Workflow Admin Service"),t.SetProperty("Method","Activate"),t.SetProperty("FlowSearchSpec","[Process Name] = '"+e+"'");var o=i.InvokeMethod("InvokeServiceMethod",t);console&&console.log(o),alert("Error"==o.GetProperty("Status")?o.GetChildByType("Errors").GetChild(0).GetProperty("ErrMsg"):"0"===o.GetChildByType("ResultSet").GetProperty("NumFlowActivated")?"Process definition ["+e+"] not found":"Done!")}}if("undefined"==typeof SiebelApp)alert("It works only in Siebel OUI session!");else{var id="SiebelWFDeploy",aHist=window.localStorage[id]?JSON.parse(window.localStorage[id]):[];$("#"+id).parent().remove();var s='<div title="Activate workflow">';s+='<input id = "'+id+'" type="text" list="'+id+'List" style="width:100%" value="'+(aHist.length?aHist[0]:"")+'">',s+='<label class="pt-3">Recent workflows:</label><ul>';for(var i=0;i<aHist.length&&5>i;i++)s+='<li><a href="#">'+aHist[i]+"</a></li>";s+="</ul></div>";var $d=$(s).dialog({modal:!0,width:640,open:function(){$("#"+id).autocomplete({source:aHist}),$("#"+id).focus().select()},close:function(){$(this).dialog("destroy").remove()},buttons:[{text:"Activate (Enter/Click)",click:function(){go($d.find("#"+id).val())}},{text:"Close (Esc)",click:function(){$(this).dialog("close")}}]});$d.keyup(function(e){13===e.keyCode&&go($d.find("#"+id).val())}),$d.find("a").click(function(e){go($(this).html())})}
  • Bookmarklet link: Activate WF

3. Disable cache.

Workflow Process cache is controlled by VerCheckTime (Workflow Version Checking Interval) server parameter .

If the parameter is on, a new WF version wouldn't take in action in existing session right away after activating it. It makes sense to have it on production, but I recommend to turn it off on DEV environment.

Also, turn it off on your dedicated client in .cfg file:

...
[Workflow]
VerCheckTime = -1

Bonus

Same story with TBUI. Simply run [Task Activation Automation] service from JavaScript. Service runner is paying off already, by the way.

  • Bookmarklet code: Snippet
  • javascript:function go(e){if(e){aHist.indexOf(e)>-1&&aHist.splice(aHist.indexOf(e),1),aHist.unshift(e),window.localStorage[id]=JSON.stringify(aHist),$d.dialog("close");var t=SiebelApp.S_App.GetService("FWK Runtime"),i=SiebelApp.S_App.NewPropertySet();i.SetProperty("Service","Task Activation Automation"),i.SetProperty("Method","Activate Task"),i.SetProperty("TaskName",e);var o=t.InvokeMethod("InvokeServiceMethod",i);console&&console.log(o),alert("Error"==o.GetProperty("Status")?o.GetChildByType("Errors").GetChild(0).GetProperty("ErrMsg"):"0"===o.GetChildByType("ResultSet").GetProperty("NumFlowActivated")?"Process definition ["+e+"] not found":"Done!")}}if("undefined"==typeof SiebelApp)alert("It works only in Siebel OUI session!");else{var id="SiebelTaskDeploy",aHist=window.localStorage[id]?JSON.parse(window.localStorage[id]):[];$("#"+id).parent().remove();var s='<div title="Activate task">';s+='<input id = "'+id+'" type="text" list="'+id+'List" style="width:100%" value="'+(aHist.length?aHist[0]:"")+'">',s+='<label class="pt-3">Recent tasks:</label><ul>';for(var i=0;i<aHist.length&&5>i;i++)s+='<li><a href="#">'+aHist[i]+"</a></li>";s+="</ul></div>";var $d=$(s).dialog({modal:!0,width:640,open:function(){$("#"+id).autocomplete({source:aHist}),$("#"+id).focus().select()},close:function(){$(this).dialog("destroy").remove()},buttons:[{text:"Activate (Enter/Click)",click:function(){go($d.find("#"+id).val())}},{text:"Close (Esc)",click:function(){$(this).dialog("close")}}]});$d.keyup(function(e){13===e.keyCode&&go($d.find("#"+id).val())}),$d.find("a").click(function(e){go($(this).html())})}
  • Bookmarklet: Activate Task

You know how calc expressions can become a nightmare when growing in size. Nested IIFs, long chains of logical operators, service calls and dozen of brackets. And all these in a single line. It can take minutes before one can understand what is going on. Sometimes I copy the expression into a text editor and manually format it - with tabs and newlines, the same way modern IDE formats the code. Once done, it is much easier to read a logic from the rule.

Inspired by the idea to automate the routine, I did a research and then spent some time on a "grammar playground". Check out the result below or in a new window.

Siebel Expressions online beautifier

Current formatting logic is to simply print function parameters (when more than 2) on new lines as well as logical expressions enclosed in parenthesis. I plan to make it a bit smarter in future.

Source code

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 f = o.arguments.length > 2; // split params when more then 2
			var s = (f ? "\n" + t : "");
			var st = (f ? s + "\t" : "");
			r = o.name + "(";
			for (var i in o.arguments) {
				r += st + trav(o.arguments[i], t + "\t") + (i < o.arguments.length - 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.quote + o.value + o.quote;
		}

		if (p) {
			r = "(" + r + ")";
		}
		if (n) {
			r = "NOT " + r;
		}

	} else {
		r = o.toString();
	}
    return r;
}

$(document).ready(function(){
	$("#ExpParser").on("click", function() {
		var s = $("#ExpParserInput").val();
		console.log(s);
		if (s) {
			try {
				var o = SiebelQueryLang.parse(s);
				s = trav(o.expression, "");
			} catch(e) {
				s = e.toString();
			}
		} else {
			s = "Please, insert a Siebel expression first";
		}
		$("#ExpParserOutput").val(s);
	}).click();
})

The main outcome of the effort is, of course, to make it a part of Expression Playground. Check out an updated version here.

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

 

I have this idea since the release of OpenUI - to pull all useful info from SiebelApp object (BO, BCs, View, Applets, Controls etc.) into a dialog which can be opened with a hit of a button. And maybe also to built in some features for a better user experience.

Check out my version of "About View" bookmarklet:

Source code & Installation
  • Drag&drop it over the browser favourites toolbar: About View
  • Or replace a bookmark URL with this code: Snippet
  • javascript:{var id="SiebelAboutView",$d,tmp='<div title="About View" id="<%=id%>"><%var v=SiebelApp.S_App.GetActiveView(),am=v.GetAppletMap()%><b>Application:</b> <a><%=SiebelApp.S_App.GetName()%></a><br><b>View:</b> <a><%=v.GetName()%></a><br><b>BusObject:</b> <a><%=SiebelApp.S_App.GetActiveBusObj().GetName()%></a><br><%if(v.GetActiveTask()){%><b>Task:</b> <a><%=v.GetActiveTask()%></a><br><%}%><b>Applets(<%=Object.keys(am).length%>) / BusComps(<%=Object.keys(SiebelApp.S_App.GetActiveBusObj().GetBCMap()).length%>):</b><br><ul style="padding-left:20px"><%for(sa in am){var a=am[sa];var bc=a.GetBusComp();var r = bc.GetSelection() < bc.GetRecordSet().length?bc.GetRecordSet()[bc.GetSelection()]:{};var os="SiebelApp.S_App.GetActiveView().GetAppletMap()[\'"+sa+"\']";var $ds=$("#"+a.GetFullId());%><li><a data-target="controls"><b style="<%if($ds.is(":hidden")){%>font-style:italic;<%}if(a===v.GetActiveApplet()){%>text-decoration:underline<%}%>"><%=sa%></b></a> / <a data-target="fields"><b><%=bc.GetName()%></b></a><ul id="controls" style="display:none"><hr><b>Applet:</b> <a><%=a.GetName()%></a><br/><b>BusComp:</b> <a><%=bc.GetName()%></a><br/><b>Mode:</b> <a><%=a.GetMode()%></a><br/><b>Title:</b> <a><%=a.GetAppletLabel()%></a><br/><%var at=a.GetToggleApplet();if(at){%><b>Toggle:</b> <a><%=at%></a><br/><%}%><b>Object Selector:</b> <a><%=os%></a><br><b>DOM Selector:</b> <a>$("<%=$ds.selector%>")</a><br><b>Controls (<%=Object.keys(a.GetControls()).length%>): </b><ul><%for(control in a.GetControls()){var c=a.GetControls()[control];var $cds=$ds.find("[name=\'"+c.GetInputName()+"\']")%><li><a data-target="control"><b style="<%if($cds.is(":hidden")){%>font-style:italic;<%}if(c===a.GetActiveControl()){%>text-decoration:underline<%}%>"><%=c.GetDisplayName()||control%></b></a><ul id="control"><hr><%if($cds.is(":visible")&&$cds.is(":focusable")){%><button data-eval="$(\'<%=$cds.selector%>\').focus()">Focus</button><br><%}%><b>Control:</b> <a><%=control%></a><br><%if(c.GetFieldName()){%><b>Field:</b> <a><%=c.GetFieldName()%></a><br><%if(r){%><b>Value:</b> <a><%=r[c.GetFieldName()]%></a><br><%}%><b>Immediate post changes:</b> <a><%=c.IsPostChanges()%></a><br><%}%><b>Type:</b> <a><%=c.GetUIType()%></a> <br><b>Input:</b> <a><%=c.GetInputName()%></a><br><b>Object Selector:</b> <a><%=os+".GetControls()[\'"+control+"\']"%></a><br><b>DOM Selector:</b> <a>$("<%=$cds.selector%>")</a><br><%if(c.GetMethodName()){%><b>Method:</b> <a><%=c.GetMethodName()%></a><br><%}%><%if(c.GetPMPropSet()&&c.GetPMPropSet().propArrayLen > 0){%><b>User Props (<%=Object.keys(c.GetPMPropSet().propArray).length%>):</b><br><ul><%for(p in c.GetPMPropSet().propArray){%><%if("string"===typeof c.GetPMPropSet().propArray[p]){%><li><a><%=p%></a>=<a><%=c.GetPMPropSet().propArray[p]%> </a></li><%}%><%}%></ul><%}%><%if(c.GetMethodPropSet()&&c.GetMethodPropSet().propArrayLen > 0){%><b>Method PS (<%=Object.keys(c.GetMethodPropSet().propArray).length%>):</b><ul><%for(p in c.GetMethodPropSet().propArray){%><%if("string"===typeof c.GetMethodPropSet().propArray[p]){%><li><a><%=p%></a>=<a><%=c.GetMethodPropSet().propArray[p]%> </a></li><%}%><%}%></ul><%}%><hr></ul></li><%}%></ul><hr></ul><ul id="fields" style="display:none"><hr><b>BusComp:</b> <%=bc.GetName()%><br/><b>Commit pending:</b> <%=bc.commitPending%><br/><b>Fields:</b> <%=Object.keys(bc.GetFieldList()).length%><br/><b>Row:</b> <%=bc.GetCurRowNum()==-1?0:bc.GetCurRowNum()%> of <%=bc.GetNumRows()%><%=bc.IsNumRowsKnown()?"":"+"%><br/><ul><%for(var f in r){%><li><a><%=f%></a>=<a><%=r[f]%></a></li><%}%></ul><hr></ul></li><%}%></ul></div>';function AV(){var html=new EJS({text:tmp}).render(SiebelApp.S_App);$d=$(html).dialog({modal:!0,width:1024,open:function(){$(this).find("li").find("ul[id]").hide(),$(this).find("a").click(function(){copy(this)}),$(this).find("a").contextmenu(function(){return $(this).siblings("#"+$(this).attr("data-target")).toggle(),$(this).siblings("ul[id]:not([id='"+$(this).attr("data-target")+"'])").hide(),!1}),$(this).find("button").click(function(){var s=$(this).attr("data-eval");$d.dialog("close"),eval(s)})},close:function(){$(this).dialog("destroy").remove()},buttons:[{text:"Help",click:function(){window.open("http://xapuk.com/index.php?topic=80","_blank")}},{text:"Close (esc)",click:function(){$(this).dialog("close")}}]}),$d.css("padding-left","20px").find("ul").css("padding-left","20px"),$d.find("hr").css("margin","5px"),$d.find("a").hover(function(e){$(this).css({"text-decoration":"mouseenter"==e.type?"underline":"none"})})}function copy(e){var t=$(e).text();$(e).hide().after("<input id='"+id+"i'>"),$d.find("#"+id+"i").val(t).select(),document.execCommand("copy")?($d.find("#"+id+"i").attr("disabled","disabled").css("color","red").val("Copied!"),setTimeout(function(){$d.find("#"+id+"i").remove(),$(e).show()},700)):$d.find("#"+id+"i").blur(function(){$(this).remove(),$d.find("a").show()})}$("#"+id).parent().remove(),"undefined"==typeof SiebelApp?alert("It works only in Siebel OUI session!"):"undefined"==typeof EJS?requirejs(["3rdParty/ejs/ejs_production"],AV,function(){alert("Failed to load EJS")}):AV();};void(0) 
  • You are also free to fork it. Here is a source code: Snippet
/* 
@desc advanced AboutView plugin
@author VB(xapuk.com)
@version 1.3 2018/07/10
*/

var id = "SiebelAboutView";

// template
var $d;
var tmp = ''+
'<div title="About View" id = "<%= id%>">'+
  '<% var v = SiebelApp.S_App.GetActiveView() %>'+
  '<b>Application:</b> <a><%= SiebelApp.S_App.GetName() %></a><br>'+
  '<b>View:</b> <a><%= v.GetName() %></a><br>'+
  '<b>BusObject:</b> <a><%= SiebelApp.S_App.GetActiveBusObj().GetName() %></a><br>'+
  '<% if(v.GetActiveTask()) { %>'+
    '<b>Task:</b> <a><%= v.GetActiveTask() %></a><br>'+
  '<% } %>'+
  '<b>Applets(<%= Object.keys(v.GetAppletMap()).length %>) / BusComps(<%= Object.keys(SiebelApp.S_App.GetActiveBusObj().GetBCMap()).length %>):</b><br>'+
  '<ul style="padding-left:20px">'+
    '<% for(applet in v.GetAppletMap()) { var a = v.GetAppletMap()[applet]; var bc = a.GetBusComp(); var r = bc.GetSelection() < bc.GetRecordSet().length?bc.GetRecordSet()[bc.GetSelection()]:{}; var os = "SiebelApp.S_App.GetActiveView().GetAppletMap()[\'" + applet + "\']"; var $ds = $("#" + a.GetFullId()); %>'+
      '<li>'+
        '<a data-target="controls"><b style="<% if($ds.is(":hidden")){ %>font-style:italic;<% } if(a===v.GetActiveApplet()){ %>text-decoration:underline<% } %>"><%= applet %></b></a> / '+ 
        '<a data-target="fields"><b><%= bc.GetName() %></b></a>'+
        '<ul id="controls" style="display:none">'+
          '<hr>'+
          '<b>Applet:</b> <a><%= a.GetName() %></a><br/>'+
          '<b>BusComp:</b> <a><%= bc.GetName() %></a><br/>'+
          '<b>Mode:</b> <a><%= a.GetMode() %></a><br/>'+
          '<b>Title:</b> <a><%= a.GetAppletLabel() %></a><br/>'+
          '<% if(a.GetToggleApplet()){ %>'+
            '<b>Toggle:</b> <a><%= a.GetToggleApplet() %></a><br/>'+
          '<% } %>'+
          '<b>Object Selector:</b> <a><%= os %></a><br>'+
          '<b>DOM Selector:</b> <a>$(\"<%= $ds.selector %>\")</a><br>'+
          '<b>Controls (<%= Object.keys(a.GetControls()).length %>): </b>'+
          '<ul>'+
          '<% for(control in a.GetControls()) { var c = a.GetControls()[control]; var $cds = $ds.find("[name=\'" + c.GetInputName() + "\']") %>'+
            '<li>'+
              '<a data-target="control"><b style="<% if($cds.is(":hidden")){ %>font-style:italic;<% } if(c===a.GetActiveControl()){ %>text-decoration:underline<% } %>"><%= c.GetDisplayName()||control %></b></a>'+
              '<ul id="control">'+
                '<hr>'+
                '<% if($cds.is(":visible") && $cds.is(":focusable")){ %>'+
                  '<button data-eval="$(\'<%= $cds.selector %>\').focus()">Focus</button><br>'+
                '<% } %>'+
                '<b>Control:</b> <a><%= control %></a><br>'+
                '<% if(c.GetFieldName()){ %>'+
                  '<b>Field:</b> <a><%= c.GetFieldName() %></a><br>'+
                  '<% if(r){ %>'+
                    '<b>Value:</b> <a><%= r[c.GetFieldName()] %></a><br>'+
                  '<% } %>'+
                  '<b>Immediate post changes:</b> <a><%= c.IsPostChanges() %></a><br>'+
                '<% } %>'+
                '<b>Type:</b> <a><%= c.GetUIType() %></a> <br>'+ // to decode value trhough SiebelJS.Dependency("SiebelApp.Constants");
                '<b>Input:</b> <a><%= c.GetInputName() %></a><br>'+
                '<b>Object Selector:</b> <a><%= os+".GetControls()[\'" + control + "\']" %></a><br>'+
                '<b>DOM Selector:</b> <a>$(\"<%= $cds.selector %>\")</a><br>'+
                '<% if(c.GetMethodName()){ %>'+
                  '<b>Method:</b> <a><%= c.GetMethodName() %></a><br>'+
                '<% } %>'+
                '<% if(c.GetPMPropSet() && c.GetPMPropSet().propArrayLen > 0){ %>'+
                  '<b>User Props (<%= Object.keys(c.GetPMPropSet().propArray).length %>):</b><br>'+
                  '<ul>'+
                    '<% for(p in c.GetPMPropSet().propArray){ %>'+
                      '<% if("string" === typeof c.GetPMPropSet().propArray[p]){ %>'+
                        '<li><a><%= p %></a> = <a><%= c.GetPMPropSet().propArray[p] %> </a></li>'+
                      '<% } %>'+
                    '<% } %>'+
                  '</ul>'+
                '<% } %>'+
                '<% if(c.GetMethodPropSet() && c.GetMethodPropSet().propArrayLen > 0){ %>'+
                  '<b>Method PS (<%= Object.keys(c.GetMethodPropSet().propArray).length %>):</b>'+
                    '<ul>'+
                      '<% for(p in c.GetMethodPropSet().propArray){ %>'+
                        '<% if("string" === typeof c.GetMethodPropSet().propArray[p]){ %>'+
                          '<li><a><%= p %></a> = <a><%= c.GetMethodPropSet().propArray[p] %> </a></li>'+
                        '<% } %>'+
                      '<% } %>'+
                    '</ul>'+
                  '<% } %>'+
                '<hr>'+
              '</ul>'+
            '</li>'+
          '<% } %>'+
          '</ul>'+
          '<hr>'+
        '</ul>'+
        '<ul id="fields" style="display:none">'+
          '<hr>'+
          '<b>BusComp:</b> <%= bc.GetName() %><br/>'+
          '<% if(r && r.hasOwnProperty("Id")){ %>'+
            '<b>Row Id:</b> <a><%= r.Id %></a><br/>'+
          '<% } %>'+
          '<% if(r && r.hasOwnProperty("Created")){ %>'+
            '<b>Created:</b> <a><%= r.Created %></a><br/>'+
          '<% } %>'+
          '<% if(r && r.hasOwnProperty("Updated")){ %>'+
            '<b>Updated:</b> <a><%= r.Updated %></a><br/>'+
          '<% } %>'+
          '<b>Commit pending:</b> <%= bc.commitPending %><br/>'+
          '<b>Fields:</b> <%= Object.keys(bc.GetFieldList()).length %><br/>'+
          '<b>Row:</b> <%= bc.GetCurRowNum()==-1?0:bc.GetCurRowNum() %> of <%= bc.GetNumRows() %><%= bc.IsNumRowsKnown()?"":"+" %><br/>'+
          '<ul>'+
            '<% for(var f in r){ %>'+
              '<li><a><%= f %></a> = <a><%= r[f] %></a></li>'+
            '<% } %>'+
          '</ul>'+
          '<hr>'+
        '</ul>'+
      '</li>'+
    '<% } %>'+
'</ul>'+
'</div>';

// to support single session
$("#" + id).parent().remove();

// show the dialog
function SiebelAboutView(){

    var html = new EJS({text: tmp}).render(SiebelApp.S_App);

    $d = $(html).dialog({
        modal: true,
        width: "1024",
        open:function(){
            // hide all expandable ULs by default
            $(this).find("li").find("ul[id]").hide();
            // attempt to copy span content (click)
            $(this).find("a").click(function(){
                copy(this);
            });
            // expand (right click)
            $(this).find("a").contextmenu(function(){
                $(this).siblings("#"+$(this).attr("data-target")).toggle();
                $(this).siblings("ul[id]:not([id='"+$(this).attr("data-target")+"'])").hide();
                return false;
            });
            // focus on control
			$(this).find("button").click(function(){
				var str = $(this).attr("data-eval");
				$d.dialog('close');
				eval(str);
			});
        },
        close: function(){
            $(this).dialog('destroy').remove();
        },
        buttons: [
            {
               text:'Help',
               click: function(){
                   window.open("http://xapuk.com/index.php?topic=80", "_blank");
               }
            },
            {
               text:'Copy (left click)',
               disabled: true
            },
            {
               text:'Expand (right click)',
               disabled: true
            },
            {
               text:'Close (esc)',
               click: function() {
				 $(this).dialog('close');
               }
            }
        ]
    });
	
	// styling
	$d.css("padding-left","20px");
	$d.find("ul").css("padding-left","20px");
	$d.find("hr").css("margin","5px");
	$d.find("a").hover(function(e){
		$(this).css({"text-decoration":e.type=="mouseenter"?"underline":"none"});
	});
}

// copy value
function copy(scope){
	// replacing link with intput and select the value
	var val = $(scope).text();
    $(scope).hide().after("<input id='" + id + "i'>");
    $d.find("#" + id + "i").val(val).select();
    // attempt to copy value
    if (document.execCommand("copy", false, null)){
    	// if copied, display a message for a second
        $d.find("#" + id + "i").attr("disabled", "disabled").css("color","red").val("Copied!");
        setTimeout(function(){
        	$d.find("#" + id + "i").remove();
        	$(scope).show();
        }, 700);
    }else{
    	// if failed to copy, leave input until blur, so it can be copied manually
		$d.find("#" + id + "i").blur(function(){
			$(this).remove();
			$d.find("a").show();
		});    	
    }
}

if ("undefined" === typeof SiebelApp || "undefined" === typeof SiebelApp.S_App){
	alert("Please launch Siebel application first.");
}else if ("undefined" === typeof EJS){
	var src = "3rdParty/ejs/ejs_production";
	requirejs([src], SiebelAboutView, function(){alert("Failed to load EJS library ! \n" + src);});
}else{
	SiebelAboutView();
}
  

Quick tips
  • All values can be copied with a mouse click.
  • Values in bold can be expanded with right click.
  • Underlined style indicates active items.
  • Italic style indicates hidden items.
  • Unlike the original About View (server side) it works with popup applets and in expired session.

A new version is available here.

Since OpenUI release, browser console became a powerful tool in hands of confident Siebel developer. In this topic I'll give you an idea how to organise your browser code snippets and will share couple of examples how to take an advantage of SiebelApp object.


Browser console


Traditional way of injecting browser code.

How to run: F12 => Console tab => type the command or use up key to flip through history => enter


SiebelApp.S_App.GetActiveView().GetActiveApplet().GetName();

Chrome code snippets


Useful if you need to store more than oneliners.

How to run: F12 => Source tab => Snippets => right click => run

Here is an example why you might need it.


console.log(SiebelApp.S_App.GetActiveView().GetName());
var am = SiebelApp.S_App.GetActiveView().GetAppletMap();
for (sAppletName in am) {
  console.log('\t' + sAppletName + '(' + am[sAppletName].GetBusComp().GetName() + ')');
}

Bookmarklets


Easiest way to run a code snippet. It is not only takes one click to run your code but it also allows 'endless' features (fancy UI, working with clipboard, history of queries). I see bookmarklets as an open source, cross-browser, cross-environment Siebel plugin. If you manage to use Chrome code snippets to build and debug snippets and then compile them into bookmarklets.

I usually use Chrome snippets as a source code vault and as GUI to build and debug my code. Once the code is stable, I make it a bookmarklet and enjoy it.

How to create: Linearise your JS code, remove comments or simply minify it (for example, JSCompress or JS/CSS Minifier, thanks Emma!), escape quotes and finally wrap it with "javascript:{...};void(0)". Here is how the code from above example will look like as a bookmarklet:


javascript:{var s=SiebelApp.S_App.GetActiveView().GetName();var am=SiebelApp.S_App.GetActiveView().GetAppletMap();for(sAppletName in am){s+='\n\t'+sAppletName+'('+am[sAppletName].GetBusComp().GetName()+')';}alert(s);};void(0)

You can always use online bookmarklet converters. Here are couple examples:

How to install: Easiest way is to drag & drop a link on your favourites toolbar. I'm a bookmarklet Drag & Drop me. Here is a full article on how to install bookmarklets for different browsers.

How to run: Click

Check out how powerful and convenient bookmarklets could be:

/* 
@desc fancy UI wrapper for GetProfileAttr Siebel function
@author VB(xapuk.com)
@version 1.1 2018/06/08
*/
if ("undefined" == typeof SiebelApp) {
    alert("It works only in Siebel OUI session!");
}

// snippet id
var id = "SiebelProfileAttr";

// localStorage to store the history
var aHist = window.localStorage[id] ? JSON.parse(window.localStorage[id]) : [];

// just in case (experimental)
$("#" + id).parent().remove();

// constructing dialog content
var s = '<div title="Get Profile Attribute">';
s += '<input id = "' + id + '" type="text" list="' + id + 'List" style="width:100%" value="' + (aHist.length ? aHist[0] : "") + '">';
s += '<datalist id="' + id + 'List">';
for (var i = 0; i < aHist.length; i++){
    s += '<option>' + aHist[i] + '</option>';
}
s += '</datalist>';
s += '<input id="' + id + 'Out" type ="text" style="display:none">';
s += '<ul></ul></div>';

// open dialog
var $d = $(s).dialog({
    modal: true,
    width: 640,
    open: function() {
        $('#' + id).focus().select(); // autofocus
    },
    close: function() {
        $(this).dialog('destroy').remove();
    },
    buttons: [{
        text: 'Get (Enter)',
        click: go
    }, {
        text: 'Close (Esc)',
        click: function() {
            $(this).dialog('close');
        }
    }]
});

listHistory();

function go() {
    var name = $d.find('#' + id).val();
    if (name) {
        // moving recent query to the top
        if (aHist.indexOf(name) > -1) {
            aHist.splice(aHist.indexOf(name), 1);
        }
        aHist.unshift(name);
        window.localStorage[id] = JSON.stringify(aHist);

        //rerender history
        listHistory();
    }
    return name;
}

// print a list of recent queries
function listHistory() {
    var $ul = $d.find("ul").empty();
    for (var i = 0; i < aHist.length && i < 5; i++) {
        // five recent values
        $ul.append('<li><b>' + aHist[i] + '</b> = <a href="#">' + SiebelApp.S_App.GetProfileAttr(aHist[i]) + '</a></li>');
    }
    // copy value on click
    $d.find("a").click(function(event) {
        var val = $(this).html();
        $("#" + id + "Out").show().val(val).select();
        var r = document.execCommand("copy");
        $("#" + id + "Out").hide();
        if(r){
            $(this).hide().after('<span id="tmp" style="color:red">Copied!</span>');
            setTimeout(function(){
                $d.find("a").show();
                $d.find("#tmp").remove();
            }, 500);
        }
    });
}

// key bindings
$d.keyup(function(event) {
    // enter
    if (event.keyCode === 13) {
        go();
    }
});
/* 
@desc fancy wrapper for GotoView Siebel function
@author VB(xapuk.com)
@version 1.0 12/06/2018
*/
if ("undefined" == typeof SiebelApp){
    alert("It works only in Siebel OUI session!");
}

// snippet id
var id = "SiebelGotoView";

// localStorage to store the history
var aHist = window.localStorage[id]?JSON.parse(window.localStorage[id]):[];

// just in case (experimental)
$("#" + id).parent().remove();

// constructing dialog content
var s = '<div title="Goto View">';
s += '<input id = "' + id + '" type="text" list="' + id + 'List" style="width:100%" value="' + (aHist.length?aHist[0]:"") + '">'; // most recent
s += '<datalist id="' + id + 'List">';
for (var i =0; i < aHist.length; i++){ // full history into unbounded picklist
    s += '<option>' + aHist[i] + '</option>';
}
s += '</datalist><ul>';
for (var i =0; i < aHist.length && i < 5; i++){ // five recent values as links
    s += '<li><a href="#">' + aHist[i] + '</a></li>';
}
s += '</ul></div>';

// open dialog
var $d = $(s).dialog({
    modal: true,
    width: 640,
    open: function() {
        $('#' + id).focus().select(); // autofocus
    },
    close: function() {
        $(this).dialog('destroy').remove();
    },
    buttons: [{
        text: 'Go (Enter)',
        click: function(){
            go($d.find('#' + id).val());
        }
    }, {
        text: 'Close (Esc)',
        click: function() {
            $(this).dialog('close');
        }
    }]
});

// GotoView
function go(name) {
    if (name){
        // moving recent view to the top
        if (aHist.indexOf(name) > -1){
           aHist.splice(aHist.indexOf(name),1);
        }
        aHist.unshift(name);
        window.localStorage[id] = JSON.stringify(aHist); 
        $d.dialog('close');
        SiebelApp.S_App.GotoView(name); //running GotoView command
    }
}

// running GotoView on Enter
$d.keyup(function(event) {
    // enter
    if (event.keyCode === 13) { 
        go($d.find('#' + id).val());
    }
});

// running GotoView on link click
$d.find("a").click(function(event) {
    go($(this).html());
});
Stay tuned - best snippets are yet to come :)

 Since last versions of Siebel don't allow logging in through GET requests (credentials in URL address), it is now a bit more challenging to have a "log into Siebel" browser bookmarks. However, we can acheive the same by sending a POST request. Check it out in the below example.

Quick description:

A single-file page to orginise all Siebel links which allow you to log into Siebel thin client in one click.

Setup instruction:

  1. Download a Sample page (right click - Save link as ... / Save target as ...).
  2. Open the page in text editor, and update it with your structure and credentials following inline comment tips:
    • each level of grid can have url, usr, pwd attributes defined
      • url is concatinated from top to bottom
      • usr and pwd give a priority to the deepest level
      • usr = '*' when you want to specify credentials manually
  3. Open the page in browser and make it your home page.

Copy below script in browser console (F12) and run it once. It will keep Siebel client alive during the day.


function keepAlive(iHour, iNum)
{
	var iStep = 60; // will send the request every minnute
	iNum = iNum>0?iNum:0;

	if (iHour*60*60 > iNum*iStep){
		setTimeout(function(){
			if (typeof(SiebelApp.S_App.GetProfileAttr("ActiveViewName")) != "undefined"){
				SiebelJS.Log("keep alive / " + iNum + " / " + (new Date()));
				keepAlive(iHour, ++iNum);
			}
		}, iStep * 1000);
	}
}
keepAlive(8); // 8 = number of hours you want Siebel to live

It is helpful when you are working with Siebel client all day long and you don't want to re-login after a short pause. Very handy during Siebel frontend development.


Updates

A fancy version with a pulsing indicator and on / off toggle.

  • Bookmarklet link: About View
  • Bookmarklet source: Snippet
  • javascript:{function keepAlive(e,i){i=0<i?i:0,console&&console.log("keep alive / "+i+" / "+new Date),60*i<60*e*60?(0===$("#SiebelKeepAlive").length?$("body").append("<div id='SiebelKeepAlive' style='position:fixed;top:5px;left:5px;background-color:#ff5e00;border:solid 2px;border-radius:5px;padding:5px'><b>KA</b></div>"):$("#SiebelKeepAlive").fadeTo(100,1),$("#SiebelKeepAlive").fadeTo(54e3,.2),window.timerId=setTimeout(function(){void 0!==SiebelApp.S_App.GetProfileAttr("ActiveViewName")&&keepAlive(e,++i)},6e4)):keepAlive_stop()}function keepAlive_stop(){clearTimeout(window.timerId),delete window.timerId,$("#SiebelKeepAlive").remove(),console.log("keep alive time stopped")}"undefined"==typeof SiebelApp?alert("It works only in Siebel OUI session!"):void 0===window.timerId?keepAlive(4):keepAlive_stop();};void(0) 
  • Source code: Snippet
/* 
@desc Keeps Siebel session alive for a certain amount of time
@author VB(xapuk.com)
@version 2.0 2018/07/20
*/


if ("undefined" == typeof SiebelApp){
    alert("It works only in Siebel OUI session!");
}else{
	if ("undefined" === typeof window.timerId){
		keepAlive(4); // number of hours you want Siebel to live
	}else{
        keepAlive_stop();
	}
}

function keepAlive(iHour, iNum)
{
	var iStep = 60; // frequency in secs
	iNum = iNum>0?iNum:0;
	if (console) console.log("keep alive / " + iNum + " / " + (new Date()));
	if (iHour*60*60 > iNum*iStep){
	    if($("#SiebelKeepAlive").length === 0){
	        $("body").append("<div id='SiebelKeepAlive' style='position:fixed;top:5px;left:5px;background-color:#ff5e00;border:solid 2px;border-radius:5px;padding:5px'><b>KA</b></div>");
	    }else{
	        $("#SiebelKeepAlive").fadeTo(100, 1);
	    }
	    $("#SiebelKeepAlive").fadeTo(iStep * 900, 0.2);
		window.timerId = setTimeout(function(){
			if (typeof(SiebelApp.S_App.GetProfileAttr("ActiveViewName")) != "undefined"){
				keepAlive(iHour, ++iNum);
			}
		}, iStep * 1000);
	}else{
		keepAlive_stop();
	}
}

function keepAlive_stop(){
    clearTimeout(window.timerId);
    delete window.timerId;
    $("#SiebelKeepAlive").remove();
    console.log("keep alive time stopped");
}
 

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.


1. Download AceJS

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\".


2. Create PM/PR files

Create Presentation Model and Physical Renderer files:

siebel/custom/CSBS_PM.js


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";
		})
}

siebel/custom/CSBS_PR.js


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";
		})
}

3. Manifest administration

Don't forget to attach PM/PR files to applet (Business Service Script Editor Applet2) on Application -> Manifest Administration view.


What's next ...

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!