/*********************************************************************
 **
 **   server/appx-client-screen.js - Client Screen processing
 **
 **   This module contains code to process client resources.
 **
 *********************************************************************/

// what_str =  "@(#)Appx $Header$";

"use strict";
var screenflipped = false;
var blocked = false;
var inProgressBox = null;

function getInputElement($tag) {
    if ($($tag).hasClass("appxdatefield") && $($tag).find(".appxdatevalue")) {
        $tag = $($tag).find(".appxdatevalue");
    }

    return($tag);
}

function setInputFocus($tag) {
    if ($($tag).hasClass("appxdatefield") && $($tag).find(".appxdatevalue")) {
        $tag = $($tag).find(".appxdatevalue");
    }
    if ($($tag).hasClass("appxcolorpickerwrapper") && $($tag).find(".appxcolorpicker")) {
        $tag = $($tag).find(".appxcolorpicker");
    }

    $tag.focus();
}

function appxmsgshandler(x) {
    appx_session.msgs = [];
    var i = 0;
    var $msgshtml = "";
    while (x.data.length > 0) {
        var msg = x.data.shift();
		// Bug # 4689 - The prompt message "Press User Option" is sent by the engine but is done after the user selects an option
		//  thus making the message worthless.  Given that, we'll ignore the message so it isn't displayed.
		if (msg.txtval != null && msg.txtlen > 0 && msg.txtval.includes("Press User Option")) { continue; }
		
        if (msg.txtlen > 0 && msg.txtval.indexOf("Building Scan List") < 0) {
            var $msghtml = $("<span>").html(msg.txtval);
            $msghtml.addClass("status-msg");
            switch (msg.severity) {
                case 0:
                    $msghtml.addClass("status-msg-info");
					if (appx_session.getProp("silenceSystemMessageSounds") == false) {
						appxloadurlhandler( {'data':'$messagebeep:'});   // Bug#4447 - no sound on errors, warnings
					} 
                    break;
                case 1:
                    $msghtml.addClass("status-msg-warning");
					if (appx_session.getProp("silenceSystemMessageSounds") == false) {
						appxloadurlhandler( {'data':'$warningbeep:'});   // Bug#4447 - no sound on errors, warnings
					} 
                    break;
                case 2:
                    $msghtml.addClass("status-msg-error");
					if (appx_session.getProp("silenceSystemMessageSounds") == false) {
						appxloadurlhandler( {'data':'$errorbeep:'});     // Bug#4447 - no sound on errors, warnings
					} 
                    break;
                case 3:
                    $msghtml.addClass("status-msg-cancel");
					if (appx_session.getProp("silenceSystemMessageSounds") == false) {
						appxloadurlhandler( {'data':'$cancelbeep:'});    // Bug#4447 - no sound on errors, warnings
					} 
                    break;
            }
            appx_session.msgs.push($msghtml);
        }
    }
}

function applymessages() {
	var msgcount = appx_session.msgs.length;
    var $newhtml = ((msgcount > 1) ? $("<div id=dropdown-container class=appx-dropdown-content>") : $("<div id=dropdown-container>"));
	var $firstMsg = ((msgcount > 1) ? appx_session.msgs[0] : '');
	var i = ((msgcount > 1) ? 1:0);
	
    for (; i < appx_session.msgs.length; i++) {
        var $msghtml = appx_session.msgs[i];
        if (i > 1)
            $newhtml.append("<br>");
        $newhtml.append($msghtml);
    }
	
    $("#appx-status-msg").html($newhtml);
	if (msgcount > 1) { 
		$firstMsg[0].innerHTML += ' -- more --'; 
		$firstMsg.append("<i class=\"appx-solid-arrow arrow-solid-down\"></i>");
		$("#appx-status-msg").prepend($firstMsg);
		$("#appx-status-msg").addClass("appx-dropdown");
	}
}

/*
**Function to find the box id of that belongs to the coordinates provided
**
**@param pos_row: starting row position of item to find box for
**@param pos_col: starting column position of item to find box for
**@param size_rows: row size of item to find box for
**@param size_cols: column size of item to find box for
**@param includeScroll: whether to include scroll boxes in box search
**
**@return ret: box id if found or 0 if box wasn't found
**
*/
function appxFindBoxIdx(pos_row, pos_col, size_rows, size_cols, includeScroll) {
    var ret = 0;
    var boxes = appx_session.current_show.boxes;
    for (var boxIdx = 0; boxIdx < boxes.length; boxIdx++) {
        var box = boxes[boxIdx];
        if (pos_col >= box.begin_column && pos_col <= box.end_column && pos_row >= box.begin_row && pos_row <= box.end_row) {
            if (pos_row + size_rows - 1 <= box.end_row && pos_col + size_cols - 1 <= box.end_column) {
                if (includeScroll == true || (appxIsScroll(box) == false && appxIsScrollReg(box) == false))
                    ret = boxIdx;
            }
        }
    }
    return ret;
}


function appxfindbox(pos_row, pos_col, size_rows, size_cols, includeScroll) {
    var ret = null;
    var boxes = appx_session.current_show.boxes;
    for (var boxIdx = 0; boxIdx < boxes.length; boxIdx++) {
        var box = boxes[boxIdx];
        if (pos_col >= box.begin_column && pos_col <= box.end_column && pos_row >= box.begin_row && pos_row <= box.end_row) {
            if (pos_row + size_rows - 1 <= box.end_row && pos_col + size_cols - 1 <= box.end_column) {
                if (includeScroll == true || (appxIsScroll(box) == false && appxIsScrollReg(box) == false))
                    ret = box;
            }
        }
    }
    return ret;
}

function applyimage(key) {
    if (key != null) {
        var url = appx_session.image_cache[key].url;
        var ctx = appx_session.image_cache[key].ctx;

        switch (ctx) {
            case 4:
            case 5:
                //feed fake attributes for mouse hovers
                $("#screenBuf ." + key + "_imgRO").attr("srcRO", url);
                break;
            case 7:
                // feed background image css
                $("#screenBuf ." + key).css({ "background-image": "url('" + url + "')" });
                break;
            default:
                //feed img tags instead of background-image
                var lookup = "#screenBuf .";

                if ($("#screenBuf").children().length === 0) {
                    lookup = "#appx_main_container .";
                }

                $(lookup + key + "_pic").css({
                    "background-image": "url('" + url + "')"
                });
                $(lookup + key + "_ico").css({
                    "background-image": "url('" + url + "')"
                });
                $(lookup + key + "_img").attr("src", url);
                if (url.indexOf("./images/missing.png") != -1) {
                    $("." + key + "_img").addClass("appx-image-missing");
                }
                $(lookup + "context-menu-icon-" + key).css({ "background-image": "url('" + url + "')", "background-repeat": "no-repeat", "background-size": "auto 90%", "margin-left": "10px" });

                $("#topMenu ." + key).css({ "background-image": "url('" + url + "')" });
                $("#appx-toolbar ." + key).css({ "background-image": "url('" + url + "')" });
        }
    }
    if (appx_session.pendingResources.length == 0 && appx_session.pendingTables === 0 ) {
        appxSetStatusStateText(APPX_STATE_READY);
        if (!screenflipped) {
            appxshowscreen();
        }
    }
    else {
        appxSetStatusStateText(APPX_STATE_IMAGES);
    }
}

function applystyles() {
    try {
        appx_session.applyStylesCount++;
        if (appx_session.image_cache) {
            for (var i = 0; i < appx_session.image_cache.keys.length; i++) {
                var key = appx_session.image_cache.keys[i];
                applyimage(key);
            }
        }
        if (appx_session.applyStylesCount > 1)
            return;
        appxApplyStylesCheckbox();
        appxApplyStylesColorPicker();
        appxApplyStylesDate();
        if (appx_session.pendingResources.length === 0) {
            appxApplyStylesEditor();
        }
        appxApplyStylesHtmlViewer();
        appxApplyStylesTable();
        appxApplyStylesTitleButtons();
        appxApplyStylesSlider();
        appxApplyStylesSignature();

    }
    catch (ex) {
        console.log("applystyles: " + ex);
        console.log(ex.stack);
    }
}

/**
 **Function to turn file chooser widgets marked as signature widgets
 **into signature blocks
 */
function appxApplyStylesSignature() {
    /*
    **This code is duplicated in appx-client-localos.js function appxreceivefilehandler due
    **to it being possible to request a signature directly from the engine instead of using
    **a widget or button.
    */
    try {
        $(".signature").click(function signature_click() {
            appx_session.signaturePadID = $(this).attr("id");
            /*On click create pop up dialog that contains the signature pad*/
            var fd = $("<div>").addClass("appxsignaturedialog").css({
                "z-index": 200000
            });
            var $canvas = $("<canvas>").addClass("signaturepad");
            $(fd).append($canvas);
            $("body").append(fd);
            $.each($(".appxsignaturedialog"), function $_each(i, el) {
                $(el).dialog({
                    open: function $_dialog_open(event, ui) {
                        $(this).parent().addClass("appx-signature-dialog-parent").position({
                            my: "center",
                            at: "center",
                            of: "#box_0"
                        });
                        $(this).addClass("appx-signature-dialog");
                        /*Creating global so button clicks have access to signature pad*/
                        appx_session.signaturePad = new SignaturePad($(".signaturepad")[0]);
                        //const data = appx_session.signaturePad.toData();
                        setTimeout(function () {
                            var ratio = Math.max(window.devicePixelRatio || 1, 1);
                            $(".signaturepad")[0].width = $(".signaturepad")[0].offsetWidth * ratio;
                            $(".signaturepad")[0].height = $(".signaturepad")[0].offsetHeight * ratio;
                            $(".signaturepad")[0].getContext("2d").scale(ratio, ratio);
                            appx_session.signaturePad.clear();
                        }, 0);
                    },
                    close: function $_dialog_close() {
                        appx_session.signaturePadID = null;
                        this.remove();
                    }
                });
                $(el).dialog("open");
            });

            $(fd).append($("<input id='submitsignature' type='button' value='" + appx_session.language.buttons.submit + "'>").click(function $_click() {
                if (!appx_session.signaturePad.isEmpty()) {
                    /*Block the screen with message while uploading files*/
                    $("#main").block({
                        message: "<h1>Uploading file to temporary storage, please wait...</h1>",
                        baseZ: 999999,
                        fadeIn: 0,
                    });

                    function createBlob(callback) {
                        if (HTMLCanvasElement.prototype.toBlob !== undefined) {
                            /*toBlob for all browsers except IE/Edge... Microsoft likes to create their own standards.*/
                            $(".signaturepad")[0].toBlob(function (blob) {
                                appx_session.sigBlob = blob;
                                callback(blob);
                            });
                        } else {
                            /*IE/Edge version*/
                            callback($(".signaturepad")[0].msToBlob());
                        }

                    }
                    createBlob(function (fileBlob) {
                        /*Need slight delay to let blob get built.*/
                        var fileName = "signature.png" + Date.now();
                        uploadFileToMongo(fileBlob, fileName, function () {
                            $("#" + appx_session.signaturePadID).val("$(sendFile)\\" + fileName);
                            $("#" + appx_session.signaturePadID).addClass("appxitem dirty");
                            appx_session.signaturePad.off();
                            $(".appxsignaturedialog").dialog("close");
                        });
                    });
                }

            }));

            /*Clear signature pad*/
            $(fd).append($("<input id='clearsignature' type='button' value='" + appx_session.language.buttons.clearSignature + "'>").click(function $_click() {
                appx_session.signaturePad.clear();
            }));

            $(fd).append($("<input id='cancelsignature' type='button' value='" + appx_session.language.buttons.cancel + "'>").click(function $_click() {
                appx_session.signaturePad.off();
                $(".appxsignaturedialog").dialog("close");
            }));
        });
    } catch (ex) {
        console.log("Signature Pad Error: " + ex);
        console.log(ex.stack);
    }
}

function appxApplyStylesTitleButtons() {
    /*add click event to all help buttons on the screen and toolbar*/
    $.each($("#screenBuf .appx-title-button-help, #appx-toolbar .appx-title-button-help"), function $_each(k, el0) {
        if(ilfEditorMode == false && $(el0).parents(".appx-active-box").length === 0) {
            $(el0).prop("disabled",true);
		} else if(ilfEditorMode == true && appx_session.current_show.boxes.length == 23 && $(el0).parents(".appx-active-box").length != 0) {
            $(el0).prop("disabled",true);
			$(el0).prop("hidden", true);
        }
        $(el0).click(function $_click(event) {
            $(".appx-active-box").addClass("appx-help-cursor");
            event.preventDefault();
            appx_session.processhelp = true;
			// Bug # 4689 - Show the same help prompt text that the ADC shows
			appxSetStatusText("Click the item you would like help on", 0);
			
            $.each($(".appx-active-box .button"), function $_each(k, e10) {
                $(e10).addClass("appx-help-cursor");
            });
            $.each($(".appx-active-box .appxfield"), function $_each(k, e10) {
                $(e10).addClass("appx-help-cursor");
                $(e10).click(function $_click(event) {
                    var col = $(this).data("col");
                    var row = $(this).data("row");
                    if (!col || !row) {
                        return;
                    }
                    appxPutCursor(col, row);
                    appx_session.processhelp = false;
                    appxwidgetcallback(OPT_HELP_ITM);

                });
            });
        });
    });
    $.each($("#screenBuf .appx-title-button-ok"), function $_each(k, el0) {
        if(ilfEditorMode == false && $(el0).parents(".appx-active-box").length === 0) {
            $(el0).prop("disabled",true);
		} else if(ilfEditorMode == true && appx_session.current_show.boxes.length == 23 && $(el0).parents(".appx-active-box").length != 0) {
            $(el0).prop("disabled",true);
			$(el0).prop("hidden", true);
        }
        $(el0).click(function $_click(event) {
            event.preventDefault();
            appxwidgetcallback(OPT_ENTER);
        });
    });
    $.each($("#screenBuf .appx-title-button-close"), function $_each(k, el0) {
        if(ilfEditorMode == false && $(el0).parents(".appx-active-box").length === 0) {
            $(el0).prop("disabled",true);
		} else if(ilfEditorMode == true && appx_session.current_show.boxes.length == 23 && $(el0).parents(".appx-active-box").length != 0) {
            $(el0).prop("disabled",true);
			$(el0).prop("hidden", true);
        }
        $(el0).click(function $_click(event) {
            event.preventDefault();
            appxwidgetcallback(OPT_END);
        });
    });
}

function appxApplyStylesCheckbox() {
    try {
        var browser = checkBrowser();
        $.each($(".checkbox-label input[type=checkbox]"), function $_each(k, el0) {
            var parent = $(el0).parent();
            if ($(el0).attr('disabled')) {
                if (browser == "Edge" || browser == "IE") {
                    // Do nothing for these browsers, their disabled checkboxes look ok.
                }
                else {
                    $(el0).addClass('disabled')
                        .removeAttr('disabled')
                        .keypress(function $_keypress(event) {
                            event.preventDefault();
                        })
                        .click(function $_click(event) {
                            event.preventDefault();
                        });
                }
                /*Bug #4690 disable click on custom checkbox-label as well*/
                if($(el0).parent().hasClass("checkbox-label")){
                    $(el0).parent()
                        .keypress(function $_keypress(event) {
                            event.preventDefault();
                        })
                        .click(function $_click(event) {
                            event.preventDefault();
                        });
                }
            }
            else {
                // Bug#4415 - checkbox attributes not previously assigned
                var el = $(el0);
                switch (el.attr('value')) {
                    case 'Y':
                    case '1':
                        el.data('checked', 2);
                        el.prop('indeterminate', false);
                        el.prop('checked', true);
                        el.val("Y");
                        break;
                    case 'N':
                    case '0':
                        el.data('checked', 0);
                        el.prop('indeterminate', false);
                        el.prop('checked', false);
                        el.val("N");
                        break;
                    default:
                        el.data('checked', 1);
                        el.prop('indeterminate', true);
                        el.prop('checked', false);
                        el.val(" ");
                }

                $(el0).data('checked', $(el0).checked)      
                    .click(function $_click(e) {
                        var el = $(this);
                        var parent = $(this).parent();
                        if (el.data('checked') == 0 && parent.hasClass('appx-nullok') == false) {
                            el.data('checked', 1);
                        }
                        switch (el.data('checked')) {
                            // unchecked, going indeterminate
                            case 0:
                                el.data('checked', 1);
                                el.prop('indeterminate', true);
                                el.val(" ");
                               // el.addClass("dirty");
                                el.change();
                                break;
                            // indeterminate, going checked
                            case 1:
                                el.data('checked', 2);
                                el.prop('indeterminate', false);
                                el.prop('checked', true);
                                el.val("Y");
                                //el.addClass("dirty");
                                break;
                            // checked, going unchecked
                            default:
                                el.data('checked', 0);
                                el.prop('indeterminate', false);
                                el.prop('checked', false);
                                el.val("N");
                                //el.addClass("dirty");
                        }
                        /*maintain the value in parent - to keep checkboxes the same style*/
                        parent.data('checked', el.data('checked'));
                        parent.prop('indeterminate', el.prop('indeterminate'));
                        parent.prop('checked', el.prop('checked'));
                        parent.val(el.val());
                        parent.addClass("dirty");
                        if(el.prop('indeterminate') == true)
                            el.change();
                    });
                /*specify the keypress even on parent so it could be triggered id parent label is on focuse*/
                parent.keypress(function $_keypress(event) {
                    var el = $(this).children("input[type=checkbox]");
                    var originalValue = el.val();
                    var parent = $(this);
                    if (event.keyCode == 110 || event.keyCode == 78) { // n or N
                        el.data('checked', 0);
                        el.prop('indeterminate', false);
                        el.prop('checked', false);
                        el.val("N");
                    }
                    else if (event.keyCode == 121 || event.keyCode == 89) { // y or Y
                        el.data('checked', 2);
                        el.prop('indeterminate', false);
                        el.prop('checked', true);
                        el.val("Y");
                    }
                    /*maintain the value in parent - to keep checkboxes the same style*/
                    parent.data('checked', el.data('checked'));
                    parent.prop('indeterminate', el.prop('indeterminate'));
                    parent.prop('checked', el.prop('checked'));
                    parent.val(el.val());
                    parent.addClass("dirty");
                    if(el.val() !== originalValue)
                        el.change();
                });
            }
        });
    }
    catch (ex) {
        console.log("appxApplyStylesCheckbox: " + ex);
        console.log(ex.stack);
    }
}

function appxApplyStylesColorPicker() {
    try {
        //No need to do any of thses if we are on image editor
        var topboxid = appx_session.topboxid;
        var box = appx_session.current_show.boxes[topboxid];
        if( box && box.widget.wWidgetType == WIDGET_TYPE_IMG_EDITOR_FRAME){
            return;
        }
        //may need to loop and get ids here;
        $.each($("#screenBuf .appxcolorpicker"), function $_each(i, el) {
            $(el).parent().width($(el).parent().width() + WIDGET_COLOR_CHOOSER_EXTRA_WIDTH - WIDGET_RAW_TEXT_EXTRA_WIDTH);
            //set initial bg color base on the value if exists
            var color = el.value.trim();
            if(color.length >= 6){
                if(color.substring(0,1) != '#'){
                    color = "#"+color;
                } 
                $(this).css({
                    'border-color': color,
                    'background-color': color
                });
            }
            //change the background color when the color is changed, also set the new color to colorpicker
            $(el).change(function $_change() {
                $(this).parent().find("img").colpickSetColor(this.value,this.value);
                var color = this.value.trim();
                if(color.length >= 6){
                    if(color.substring(0,1) != '#'){
                        color = "#"+color;
                    } 
                    $(this).css({
                        'border-color': color,
                        'background-color': color
                    });
                }
            });
            $(el).focus(function(){
                if(appx_session.getProp("autoSelect")){
                    $(this).select();
                }
            });
            //icon click opens colorpicker
            $(el).parent().find("img").colpick({
                layout: 'hex',
                submit: true,
                colorScheme: 'light',
                color: el.value.trim().length == 0 ? 'ffffff':el.value.trim(),
                /*Callback function triggered when the color is changed. This is the function that allows you to get the color picked by the user whenever 
                  it changes, whithout the user pressing the OK button. Should receive:
                    HSB object: (eg. {h:0, s:100, b:100})
                    HEX string: (with no #)
                    RGB object: (eg. {r:255, g:0, b:0})
                    el element: the parent element on which colorpicker() was called. Use it to modify this parent element on change (see first example below).
                    bySetColor flag: if true, the onChange callback was fired by the colpickSetColor function and not by the user changing the color directly 
                                     in the picker. There are some cases in which you'll want a different behaviour in this case (see last example).
                */
                onChange: function $_colpick_onChange(hsb, hex, rgb, elc, bySetColor) {
                    $(elc).parent().find("input").css({
                        'border-color': '#' + hex,
                        'background-color': '#' +hex
                    });
                    // Fill the text box just if the color was set using the picker, and not the colpickSetColor function.
                    if (!bySetColor){
                         $(elc).parent().find("input").val("#"+hex);
                         $(elc).parent().addClass("dirty");
                    }
                },
                onSubmit: function $_colpick_onSubmit(hsb, hex, rgb, elc, bySetColor) {
                    $(elc).parent().find("input").css({
                        'border-color': '#' + hex,
                        'background-color': '#' +hex
                    });
                    if (!bySetColor){ 
                        $(elc).parent().find("input").val("#"+hex);
                        $(elc).parent().addClass("dirty");
                    }
                    $(elc).colpickHide();
                },
                onBeforeShow: function $_colpickOnBoforeShow(elem){
                    //set the current color value
                    var currentValue = $(this).parent().find("input").val();
                    currentValue = currentValue.trim().length == 0? "ffffff":currentValue;
                    $(this).colpickSetColor(currentValue,currentValue);
                },
                onShow: function $_colpick_onShow(elem){
                    //this is to prevent sending keys to appx while colpick is open
                    var $elc = $(this);
                    $(elem).keydown(function $_colpick_onKeyDown(e){
                        //F8 and Escape to close the color chooser 
                        if( e.key === "Escape" || e.key === "F8"){
                            $elc.colpickHide();
                        }
                        //Enter will close the color chooser
                        if(e.key === "Enter" ){
                            $elc.colpickHide();
                        }
                        e.stopPropagation();
                    });
                    //set the focuse to the hex feild on the popup. This is needed to intercept all the keydown events
                    setTimeout( function(){
                        //we have to do it this way, because the element is not added to the screen yet
                        var $hexTextField = $("#"+elem.id +" > .colpick_hex_field > input");
                        $hexTextField.focus();
                        $hexTextField.select();
                        }
                        ,1);
                }
            });
        });
    }
    catch (ex) {
        console.log('appxApplyStylesColorPicker: ' + ex);
        console.log(ex.stack);
    }
}

function appxApplyStylesDate() {
    try {
        $.each($("#screenBuf .appxdatefield"), function $_each(i, el) {
            $(el).width($(el).width() + (WIDGET_DATE_CHOOSER_EXTRA_WIDTH - WIDGET_RAW_TEXT_EXTRA_WIDTH));
            var datevalue = $(el).find(".appxdatevalue").first();
            datevalue.css({
                "font-size": appx_session.basefontsize + "px"
            });
            var datepicker = $(el).find(".appxdatepicker").first();
            if(datepicker.length > 0){
                var parts = JSON.parse(datepicker.val());
                var opt = {
                    beforeShow: function opt_beforeShow(inp, inst) {
                        appx_session.activeDatepicker = $(this);
                    },
                    buttonImage: "" + appxClientRoot + "/images/calendar.gif",
                    buttonImageOnly: true,
                    changeMonth: true,
                    changeYear: true,
                    buttonText: "",
                    dateFormat: parts.datemsk,
                    onClose: function $opt_onClose(date, obj) {
                        //appx_session.activeDatepicker = null;
                        var ad = APPX.DateFormatter2(
                            date, obj.settings.dateFormat, obj.settings.timeFormat
                        );
                        var ar = $(this).closest(".appxitem").attr('id').split('_');
                        sendappxdate(parseInt(ar[2]), parseInt(ar[1]), ad);
                    },
                    showButtonPanel: true,
                    showOn: "button",
                    timeFormat: parts.timemsk,
                    yearRange: "1000:3000"
                };//end opt
                datepicker.datepicker(opt);
                datepicker.val(parts.value);
            }//end if
            $(el).find("img").css({
                "position": "absolute",
                "right": "2px",
                "top": "2px",
                "z-index": 100000
            }).click(function $_click() {
                appx_session.activeDatepicker = $(this);
            });
        });
    }
    catch (ex) {
        console.log('appxApplyStylesDate: ' + ex);
        console.log(ex.stack);
    }
}

function appxApplyStyleEditor(cacheIDorEl, cacheID) {
    try {
        var el = [];
        if (cacheID) {
            $(":data(editorConfig)").each(function data_each() {
                if ($(this).data().editorConfig === cacheIDorEl) {
                    el = $(this);
                }
            });
            if (el.length === 0) {
                return;
            }
        } else {
            el[0] = cacheIDorEl;
        }
        for (var els = 0; els < el.length; els++) {
            var $ckElement = $(el[els]);
            if (!$ckElement.attr("id") || $ckElement.attr("id").indexOf("stale") !== -1) {
                return;
            }

            $ckElement.removeClass("ckeditor");
            var tLeft = $ckElement.css("left");
            var tTop = $ckElement.css("top");
            var tWidth = $ckElement.css("width");
            var tHeight = $ckElement.css("height");
            tLeft = parseInt(tLeft.substring(0, (tLeft.length - 2)));
            tTop = parseInt(tTop.substring(0, (tTop.length - 2)));
            tWidth = parseInt(tWidth.substring(0, (tWidth.length - 2)));
            tHeight = parseInt(tHeight.substring(0, (tHeight.length - 2)));

            /*
            ** If element is not long enough to hold CKEDITOR element correctly or
            ** show decoration is set to no then
            ** we display text area and when entering change mode, put a button next
            ** element that allows user to popup dialog box with CKEDITOR inside
            */
            if (tHeight <= (appx_session.rowHeightPx * 5) || $ckElement.attr("decoration") == "no") {

                /*if a custom file is pushed down from appx we use that, otherwise we use the
                **config.js file that is in the ckeditor directory.  */
                var config = {};
                config.customConfig = AppxResource.cache[$ckElement.data("editorConfig")];
                if (config.customConfig === undefined) {
                    config.customConfig = '../../custom/appx-ckeditor-config.js';
                }
                config.wordcount = {
                    // Whether or not you want to show the Paragraphs Count
                    showParagraphs: false,
                    // Whether or not you want to show the Word Count
                    showWordCount: false,
                    // Whether or not you want to show the Char Count
                    showCharCount: true,
                    // Whether or not you want to count Spaces as Chars
                    countSpacesAsChars: true,
                    // Whether or not to include Html chars in the Char Count
                    countHTML: true,
                    // Whether or not to include Line Breaks in the Char Count
                    countLineBreaks: true,
                    // Maximum allowed Word Count, -1 is default for unlimited
                    maxWordCount: -1,
                    // Maximum allowed Char Count, -1 is default for unlimited
                    maxCharCount: $ckElement.attr("maxlength"),
                    // Maximum allowed Paragraphs Count, -1 is default for unlimited
                    maxParagraphs: -1,
                    // How long to show the 'paste' warning, 0 is default for not auto-closing the notification
                    pasteWarningDuration: 0
                };
                config.width = tWidth;
                config.height = tHeight;
                
                //now initialize the ckeditor with no toolbar for appxscreen
                config.removePlugins = ["toolbar", "clipboard", "pastetext", "pastetools", 
                                            "tableselection", "widget", "uploadwidget", "uploadimage",
                                            "pastefromword", "pastefromgdocs"];
                
                if ($ckElement.attr("disabled") == "disabled") {
                    config.removePlugins.push("elementspath");
                    config.removePlugins.push("wordcount");
                    config.resize_enabled = false;
                }
                var mainEditor = CKEDITOR.replace($ckElement.attr("id"), config);
                $ckElement.attr("name", mainEditor.name);
                var funcVal = els;
                mainEditor.on('instanceReady', function editor_onInstanceReady() {
					var visibility = "visible";
					if ($(el[funcVal]).hasClass("hideeditor")) {
						visibility = "hidden";
					}
                    this.ui.contentsElement.clientHeight = tHeight;
                    $("#cke_" + $(el[funcVal]).attr("id")).find("iframe").attr("title", "");
                    $("#cke_" + $(el[funcVal]).attr("id")).css({
						"visibility": visibility,
                        "position": "absolute",
                        "top": tTop + "px",
                        "left": tLeft + "px",
						"border-width": Math.round(parseFloat($(el[funcVal]).css("border-width"))),
						"border-style": $(el[funcVal]).css("border-style"),
						"border-radius": $(el[funcVal]).css("border-radius"),
						"border-color": $(el[funcVal]).css("border-color"),
                        "z-index": $(el[funcVal]).css("z-index")
                    });
                    var footerbar  =  $("#cke_" + $(el[funcVal]).attr("id")+" .cke_bottom");

                    /*hide footerbar instead of removing it so we can still enforce character limit*/
                    if(footerbar){
                        footerbar.css({"display":"none"});
                    }
                    
                    this.resize(tWidth, tHeight, true);
                });
                mainEditor.on('paste', function (event) {
                    validateInputText(event.data.dataValue, $ckElement.data("unicode"));
                });
                // when user closes the notification this event happens
                //Fixing the issue with 'close' tooltip stays visible
                mainEditor.on('notificationHide', function (event){
                    var tooltipDiv = document.querySelector("div.ui-tooltip");
                    if(tooltipDiv != null){
                        tooltipDiv.remove();
                    }
                });

                /*
                ** Event for when user presses a key inside of html editor
                **
                */
                mainEditor.on( 'key', function(event){ 
                    var domEvent = event.data.domEvent.$;
                    //don't send arrow key events to sendkey function
                    if(domEvent.which >= 37 && domEvent.which <= 40){
                        event.data.domEvent.stopPropagation();
                    }
                    //don't send return key to sendkey function if htmleditor is not in rtead only mode
                    else if( (domEvent.which == 10 || domEvent.which == 13) && (event.editor.element.$.getAttribute("disabled") == null)){
                        event.data.domEvent.stopPropagation();
                    }
                    else{
                        /* 
                        ** event is a ckeditor event. It also has the dom event
                        ** we want to pass the dom event (event.data.domEvent.$) to sendkey function to
                        ** intercept appx special shortcuts 
                        */
                        var ret = sendkey(domEvent);
                        //if appx intercepted the key event don't pass it to html editor
                        if(ret == false){
                            event.cancel();
                        }
                    }
                });
                mainEditor.resetDirty();

                $ckElement.addClass("button_cke");
                var $ckeButton = $('<button title="Launch a Full Featured HTML Editor">');
                $ckeButton.attr("id", "CKE_Button");
                $ckeButton.val("a");
                $ckeButton.addClass("cke_fullscreen_button");
                //$ckeButton.text(". ");
                $ckeButton.click(function ckeButton_click() {
                    var $element = $ckElement;
                    var popup_height = tHeight;
                    var popup_width = tWidth;
                    //dont let the width to be less than 600px on the popup
                    if(popup_width < 600){
                        popup_width = 600;
                    }
                    // Minus 3 to not overlap the status bar and footer bar
                    if((popup_height * 5) > ((appx_session.screenrows - 3)  * appx_session.rowHeightPx)){
                        popup_height = (appx_session.screenrows - 3) * appx_session.rowHeightPx;
                    }
                    else{
                        popup_height = popup_height * 5;
                    }
                    /*If in inquiry mode we do not display ckeditor toolbar. Else if a custom
                    **file is pushed down from appx we use that, otherwise we use the
                    **config.js file that is in the ckeditor directory for toolbar items.*/
                    var config = {};
                    config.customConfig = AppxResource.cache[$element.data("editorConfig")];
                    if (config.customConfig === undefined) {
                        config.customConfig = '../../custom/appx-ckeditor-config.js';
                    }
                    /* 
                    ** Add plugins to limit the number of characters user can add to html client 
                    */
                    config.wordcount = {
                        // Whether or not you want to show the Paragraphs Count
                        showParagraphs: false,
                        // Whether or not you want to show the Word Count
                        showWordCount: false,
                        // Whether or not you want to show the Char Count
                        showCharCount: true,
                        // Whether or not you want to count Spaces as Chars
                        countSpacesAsChars: true,
                        // Whether or not to include Html chars in the Char Count
                        countHTML: true,
                        // Whether or not to include Line Breaks in the Char Count
                        countLineBreaks: true,
                        // Maximum allowed Word Count, -1 is default for unlimited
                        maxWordCount: -1,
                        // Maximum allowed Char Count, -1 is default for unlimited
                        maxCharCount: $element.attr("maxlength"),
                        // Maximum allowed Paragraphs Count, -1 is default for unlimited
                        maxParagraphs: -1,
                        // How long to show the 'paste' warning, 0 is default for not auto-closing the notification
                        pasteWarningDuration: 0,
                        // Add filter to add or remove element before counting (see CKEDITOR.htmlParser.filter), Default value : null (no filter)
                        /*filter: new CKEDITOR.htmlParser.filter({
                            elements: {
                                div: function( element ) {
                                    if(element.attributes.class == 'mediaembed') {
                                        return false;
                                    }
                                }
                            }
                        })*/
                    };
                    config.width = popup_width;
                    // because the popup is in modal mode, we need this so the floating (menu lists) 
                    // frames open on top of the modal not under it
                    config.baseFloatZIndex = 1000001;
                    var $elementClone = $element.clone();
                    var $dialogDiv = $('<div>');
                    $dialogDiv.hide().appendTo($element.parent())
                    $elementClone.hide().appendTo($dialogDiv);
                    $elementClone.css("height", (popup_height)+"px");
                    $elementClone.attr("id", "clone_" + $element.attr("id"));
                    var editor = CKEDITOR.replace($elementClone.attr("id"), config);
                    $elementClone.attr("name", editor.name);
                    editor.on('instanceReady', function editor_onInstanceReady() {
                        // Bug #4728: Move data from the Main Editor to the popup editor
                        this.setData(mainEditor.getData());
                        $("#cke_" + $elementClone.attr("id")).find("iframe").attr("title", "");
                        $("#cke_" + $elementClone.attr("id")).css({
                            "position": "absolute",
                            //"height": $elementClone.css("height"),
                            "z-index": $elementClone.css("z-index")
                        });
                        setTimeout(function setTimeoutCallback() {
                            editor.resize( popup_width, popup_height, false);
                        }, 0);
                    });
                    editor.on('paste', function (event) {
                        validateInputText(event.data.dataValue, $element.data("unicode"));
                    });

                    editor.resetDirty();
                    $element.hide();
                    /*Create dialog popup for CKEDITOR*/
                    $dialogDiv.dialog({
                        title: "HTML EDITOR",
                        position: { "of": "#appx_main_container" },
                        minWidth: ( popup_width + 30),
                        height: (popup_height + 130),
                        modal: true,
                        closeOnEscape: false,
                        buttons: {
                            Ok: function () {
                                $element.val(editor.getData());
                                mainEditor.setData(editor.getData());
                                $(this).dialog("close");
                            },
                            Cancel: function () {
                                $(this).dialog("close");
                            }
                        },
                        close: function () {
                            $elementClone.remove();
                            $element.show();
                            editor.destroy();
                        },
                        //This is to prevent keystrokes to go back to appx process
                        open: function(){
                            $(this).dialog("widget").on("keydown", function(event){ 
                                                                                    event.stopPropagation();
                                                                                    });
                        }
                    })
                });
				var buttonvisibility = "visible";
				if ($ckElement.hasClass("hideeditor")) {
					buttonvisibility = "hidden";
				}
                $ckeButton.css({
					"visibility": buttonvisibility,
                    "position": "absolute",
                    "top": tTop + "px",
                    "left": (tLeft + tWidth + 3) + "px",
                    "z-index": $ckElement.css("z-index")
                });
                if ($ckElement.hasClass("button_cke") && $ckElement.hasClass("appx-modifiable")){
                    $ckeButton.appendTo($ckElement.parent());
                }

            } else {
                /*If in inquiry mode we do not display ckeditor toolbar. Else if a custom
                **file is pushed down from appx we use that, otherwise we use the
                **config.js file that is in the ckeditor directory for toolbar items.  */
                var config = {};
                var editor = {};
                config.customConfig = AppxResource.cache[$ckElement.data("editorConfig")];
                if (config.customConfig === undefined) {
                    config.customConfig = '../../custom/appx-ckeditor-config.js';
                }
                config.wordcount = {
                    // Whether or not you want to show the Paragraphs Count
                    showParagraphs: false,
                    // Whether or not you want to show the Word Count
                    showWordCount: false,
                    // Whether or not you want to show the Char Count
                    showCharCount: true,
                    // Whether or not you want to count Spaces as Chars
                    countSpacesAsChars: true,
                    // Whether or not to include Html chars in the Char Count
                    countHTML: true,
                    // Whether or not to include Line Breaks in the Char Count
                    countLineBreaks: true,
                    // Maximum allowed Word Count, -1 is default for unlimited
                    maxWordCount: -1,
                    // Maximum allowed Char Count, -1 is default for unlimited
                    maxCharCount: $ckElement.attr("maxlength"),
                    // Maximum allowed Paragraphs Count, -1 is default for unlimited
                    maxParagraphs: -1,
                    // How long to show the 'paste' warning, 0 is default for not auto-closing the notification
                    pasteWarningDuration: 0
                };
                config.width = tWidth;
                config.height = tHeight;
                //Hide toolbar and footerbar by removing their plugins
                //Note that you also need to remove all dependent plugings as well
                if ($ckElement.attr("disabled") == "disabled") {
                    config.removePlugins = ["toolbar", "elementspath", "wordcount", "clipboard", "pastetext", "pastetools", 
                                            "tableselection", "widget", "uploadwidget", "uploadimage",
                                            "pastefromword", "pastefromgdocs"];
                    config.resize_enabled = false;
                } 
                
                editor = CKEDITOR.replace($ckElement.attr("id"), config);
                $ckElement.attr("name", editor.name);
                var funcVal = els;
                editor.on('instanceReady', function editor_onInstanceReady() {
                    this.ui.contentsElement.clientHeight = tHeight;
                    $("#cke_" + $(el[funcVal]).attr("id")).find("iframe").attr("title", "");
                    $("#cke_" + $(el[funcVal]).attr("id")).css({
                        "position": "absolute",
                        "top": tTop + "px",
                        "left": tLeft + "px",
						"border-width": Math.round(parseFloat($(el[funcVal]).css("border-width"))),
						"border-style": $(el[funcVal]).css("border-style"),
						"border-radius": $(el[funcVal]).css("border-radius"),
						"border-color": $(el[funcVal]).css("border-color"),
                        "z-index": $(el[funcVal]).css("z-index")
                    });
                    this.resize(tWidth, tHeight, false);
                });
                editor.on('paste', function (event) {
                    validateInputText(event.data.dataValue, $ckElement.data("unicode"));
                });
                // when user closes the notification this event happens
                //Fixing the issue with 'close' tooltip stays visible
                 editor.on('notificationHide', function (event){
                    var tooltipDiv = document.querySelector("div.ui-tooltip");
                    if(tooltipDiv != null){
                        tooltipDiv.remove();
                    }
                 });

                 /*
                 ** Event for when user presses a key inside of html editor
                 **
                 */
                 editor.on( 'key', function(event){ 
                    var domEvent = event.data.domEvent.$;
                    //don't send arrow key events to sendkey function
                    if(domEvent.which >= 37 && domEvent.which <= 40){
                        event.data.domEvent.stopPropagation();
                    }
                    //don't send return key to sendkey function if htmleditor is not in rtead only mode
                    else if( (domEvent.which == 10 || domEvent.which == 13) && (event.editor.element.$.getAttribute("disabled") == null)){
                        event.data.domEvent.stopPropagation();
                    }
                    else{
                        /* 
                        ** event is a ckeditor event. It also has the dom event
                        ** we want to pass the dom event (event.data.domEvent.$) to sendkey function to
                        ** intercept appx special shortcuts 
                        */
                        var ret = sendkey(domEvent);
                        //if appx intercepted the key event don't pass it to html editor
                        if(ret == false){
                            event.cancel();
                        }
                    }
                 });

                editor.resetDirty();
            }
        }
    }
    catch (ex) {
        console.log('appxApplyStyleEditor: ' + ex);
        console.log(ex.stack);
    }
}

function appxApplyStylesEditor() {
    try {
        for (name in CKEDITOR.instances) {
            CKEDITOR.instances[name].destroy(true);
        }

        //may need to loop and get ids here;

        for (var key in appx_session.image_cache) {
            if (appx_session.image_cache[key].ctx == 10) {
                appxApplyStyleEditor(key, true);
            }
        }
        $.each($("#screenBuf .ckeditor"), function $_each(i, el) {
            if (!CKEDITOR.instances.hasOwnProperty(el.id)){
                appxApplyStyleEditor(el, false);
            }
        });
    }
    catch (ex) {
        console.log('appxApplyStylesEditor: ' + ex);
        console.log(ex.stack);
    }
}

function appxApplyStylesHtmlViewer() {
    try {
        $.each($("#screenBuf .appx-html-viewer a"), function $_each(k, el0) {
            var opt = $(el0).attr("href");
            if (!isNaN(opt)) {
                $(el0).attr({
                    "href": "#"
                }).click(function $_click(event) {
                    event.preventDefault();
                    appxwidgetcallback(parseInt(opt));
                    return false;
                });
            }
        });
    }
    catch (ex) {
        console.log('appxApplyStylesHtmlViewer: ' + ex);
        console.log(ex.stack);
    }
}

function appxApplyStylesTable() {
    try {
        $.each($("#screenBuf .appxtablewidget"), function $_each(i, el) {
            var appx_table = AppxTable.getAppxTable($(el).data("tableHashKey"));
            // create grid if we have received the userPrefs for this table,
            // if not, wait 250ms and and try again. Try this 4 times and if we still didn't get the preferences then use the default
            if(appx_table._prefsDataReceived == true){
                createGridMongo( {}, $(el).attr("id") );
            }
            else{
                setTimeout(function(){
                    if(appx_table._prefsDataReceived == true){
                        createGridMongo( {}, $(el).attr("id") );
                    }
                    else{
                        setTimeout(function(){
                            if(appx_table._prefsDataReceived == true){
                                createGridMongo( {}, $(el).attr("id") );
                            }
                            else{
                                setTimeout(function(){
                                    if(appx_table._prefsDataReceived == true){
                                        createGridMongo( {}, $(el).attr("id") );
                                    }
                                    else{
                                        setTimeout(function(){
                                            createGridMongo( {}, $(el).attr("id") );
                                        }, 250);
                                    }
                                }, 250);
                            }
                        }, 250);
                    }
                }, 250);
            }
        });
    }
    catch (ex) {
        console.log('appxApplyStylesTable: ' + ex);
        console.log(ex.stack);
    }
}

/*
**Function to turn any slider divs created into slider elements.
**
**@Library noUiSlider: http://refreshless.com/nouislider/
*/

function appxApplyStylesSlider() {
    try {
        $.each($("#screenBuf .slider"), function $_each(i, el) {
            var sStep = 1;
            var dDensity = 1;
            var dDirection = "ltr";
            var ticMajor, ticMinor, min, max;
            var sliderRange = {};
            var percent, stringPercent, count;
            var createPips = false;
            var oOrientation = "horizontal";


            min = parseInt($(el).attr("data-min"));
            max = parseInt($(el).attr("data-max"));
            sliderRange["min"] = [min];
            sliderRange["max"] = [max];

            if ($(el).attr("data-orientation") === "vertical") {
                oOrientation = "vertical";
            } else {
                $(el).closest(".appx--box").css("top", "10px");
            }

            if ($(el).attr("data-tickMajor") != null) {
                ticMajor = parseInt($(el).attr("data-tickMajor"));
                dDensity = ticMajor;
            }
            if ($(el).attr("data-tickMinor") != null) {
                ticMinor = parseInt($(el).attr("data-tickMinor"));
                dDensity = ticMinor;
            }

            if ($(el).attr("data-invert") != null &&
                $(el).attr("data-invert") == "true") {
                dDirection = "rtl";
            }

            /*Create range of values from what the user defined with tick marks.
            **If user chose snap to ticks then there is no extra processing required.*/
            if ($(el).attr("data-tickSnap") != null &&
                $(el).attr("data-tickSnap") == "true") {
                createPips = true;
                percent = 0;
                if (ticMinor) {
                    sStep = ticMinor;
                    count = (max / ticMinor + 1);
                } else if (ticMajor) {
                    sStep = ticMajor;
                    count = (max / ticMajor + 1);
                }
            } else {
                if (($(el).attr("data-tickShow") != null &&
                    $(el).attr("data-tickShow") == "true") &&
                    (ticMinor || ticMajor)) {
                    createPips = true;
                    if (ticMinor) {
                        count = (max / ticMinor + 1);
                        percent = ((ticMinor / max) * 100);
                        for (var i = 1; i <= max / percent; i++) {
                            var value = (ticMinor * i);
                            stringPercent = (percent * i).toString() + "%";
                            sliderRange[stringPercent] = [value];
                        }
                    } else if (ticMajor) {
                        count = (max / ticMajor + 1);
                        percent = ((ticMajor / max) * 100);
                        for (var i = 1; i <= max / percent; i++) {
                            var value = (ticMajor * i);
                            stringPercent = (percent * i).toString() + "%";
                            sliderRange[stringPercent] = [value];
                        }
                    }
                }
            }

            /*
            **Function to test tick marks so we can add labels to only the ones we want
            **
            **@param value: value of tick mark
            **
            **@return value: 0 - no label, 1 - large label, 2 - small label (not used)
            **
            */
            function filterTics(value) {
                var ticMajor;
                if ($(".slider").attr("data-tickMajor") != null) {
                    ticMajor = parseInt($(".slider").attr("data-tickMajor"));
                }
                if (ticMajor) {
                    value = value % ticMajor ? 0 : 1;
                } else {
                    value = 0;
                }

                return value;
            }

            /*Need to create slider differently based on if we want tick marks or not*/
            if (createPips) {
                noUiSlider.create(el, {
                    start: [parseInt($(el).attr("data-value"))],
                    step: sStep,
                    range: sliderRange,
                    direction: dDirection,
                    orientation: oOrientation,
                    pips: {
                        mode: 'count',
                        values: count,
                        density: dDensity,
                        filter: filterTics,
                        stepped: true
                    }
                });
            } else {
                noUiSlider.create(el, {
                    start: [parseInt($(el).attr("data-value"))],
                    step: sStep,
                    range: sliderRange,
                    direction: dDirection,
                    orientation: oOrientation
                });
            }

            /*Disable slider if it is not modifiable*/
            if (($(el).attr("data-modifiable") != null &&
                $(el).attr("data-modifiable") != "true")) {
                $(el).attr("disabled", true);
            }

            if ($(el).attr("data-orientation") === "vertical") {
                var tHeight = ($(el).closest(".noUiSlider").height() - 13);
                $(el).height(tHeight);
            }
            /*Upon changing slider value mark appxitem tag as dirty & update its value
            **to current slider value*/
            el.noUiSlider.on('change', function noUiSlider_onChange(values, handle) {
                var slider = $(el).closest(".noUiSlider");
                if (parseInt($(el).attr("data-value")) !== parseInt(el.noUiSlider.get())) {
                    slider.addClass("dirty");
                }
                slider.val(parseInt(values[0]));
            });

            /*Remove value divs if user has chosen not to show labels*/
            if ($(el).attr("data-tickLabels") != null &&
                $(el).attr("data-tickLabels") == "false") {
                $("div").remove(".noUi-value");
            }

        });
    }
    catch (ex) {
        console.log('appxApplyStylesSlider: ' + ex);
        console.log(ex.stack);
    }
}

//Attributes Message Handler
function appxattributeshandler(x) { appx_session.attributes = new Array(x.data); }

//Extra Attributes Message Handler
function appxextraattributeshandler(x) { appx_session.attributes_extra = new Array(x.data); }

function attachMouseListener(obj) {
    obj.mousedown(function $_mousedown(e) {
        appx_session.mouseX = (e.pageX - parseInt($('#appx_main_container').position().left));
        appx_session.mouseY = (e.pageY - parseInt($('#appx_main_container').position().top));
    });
}

//Screen Message Handler
function appxscreenhandler(x) {
    var screendatastr = "";
    if ($_screenBuf == null) {
        var $screen = $("#appx_main_container").empty();
        $_screenBuf = $screen.clone();
        $_screenBuf.attr("id", "screenBuf");
        $_screenBuf.appendTo($screen.parent());
        $_screenBuf.css("visibility","hidden"); //hide();
        attachMouseListener($screen.parent());
   }
    $_screenBuf.html("<div id='appx_main_background' style='float:left;width:70%;'>" + screendatastr + "</div>");
    //the menu may have multiple blocks and has to be cleared as a function of the current show/screen
    initializeMenu();
}

//raw screen data not used at the moment
function appxscreenhandlerdata(x) {
    var screendata = [];
    var screendatastr = "";
    var datalength = 0;
    while (x.length > 0) { //process compressed screen
        var mask = x.shift();
        logactivity("mask:  " + mask);
        if (mask > 255) {
            screendata = new Array(mask);
            var rptchar = x.shift();
            for (var i = 0; i < screendata.length; i++) {
                screendata[i] = rptchar;
            }
            x = x.slice(screendata.length, x.length);
        }
        else {
            x = x.slice(1, x.length);
            datalength = x.shift() + 1;
            screendata = x.slice(0, datalength);
            screendatastr += ab4str(screendata);
            x = x.slice(datalength, x.length);
        }
    }
    return screendatastr;
}

function appxMergeBoxes(oldbox, newbox) {
    var topAdj = oldbox.begin_row - newbox.begin_row;
    var leftAdj = oldbox.begin_column - newbox.begin_column;
    var topAdjPx = topAdj * appx_session.rowHeightPx;
    var leftAdjPx = leftAdj * appx_session.colWidthPx;
    while (oldbox.items.length) {
        var boxitem = oldbox.items.shift();
        var item = boxitem[0];
        var $tag = boxitem[1];
        item.pos_row += topAdj;
        item.pos_col += leftAdj;
        if ($tag) {
            $tag.css({
                "top": (parseInt($tag.css("top")) + topAdjPx) + "px",
                "left": (parseInt($tag.css("left")) + leftAdjPx) + "px"
            });
        }
        newbox.items.push(boxitem);
    }
    while (oldbox.widgets.length) {
        var boxwdgt = oldbox.widgets.shift();
        var wx = boxwdgt[0];
        var $tag = boxwdgt[1];
        wx.wPositionY += topAdj;
        wx.wPositionX += leftAdj;
        if ($tag) {
            $tag.css({
                "top": (parseInt($tag.css("top")) + topAdjPx) + "px",
                "left": (parseInt($tag.css("left")) + leftAdjPx) + "px"
            });
        }
        newbox.widgets.push(boxwdgt);
    }
    while (oldbox.rowtext.length) {
        var rowtext = oldbox.rowtext.shift();
        rowtext.pos_row += topAdj;
        rowtext.pos_col += leftAdj;
        newbox.rowtext.push(rowtext);
    }
}

var _boxcur = null; //appx_session?

function appxHandleEmBox() {
    for (var i = 0; i < appx_session.current_show.boxes.length; i++) {
        var box = appx_session.current_show.boxes[i];
        if (appxIsEmBuild(box)) {
            $("#appx_status_date").html(box.widget.wLabel);
            return true;
        }
    }
    return false;
}

function appxshowboxes() {
    try {
        var box1 = null,
            box2 = null,
            box3 = null;
        var boxcnt = 0;
        var $boxcurhtml = null;
        var cel = appxGetCellSize();
        var cur = appxGetCursorPos();
        var bPrevScrollAct = false;
        var topBoxIdx = 0;
        var topMerged = null;
        var isOldILFDebugger = isIlfDebugger(appx_session.current_show.boxes[0]);
        appx_session.topbox = null;
        appx_session.tablist = null;
		ilfIsLastPage = false;
        for (var i = 0; i < appx_session.current_show.boxes.length; i++) {
            inProgressBox = null;
            var box = appx_session.current_show.boxes[i];
			// The "[END]" marker indicates that this is the last page of the statemnets	
			if (ilfEditorMode && box.widgets.length == 1 && box.widgets[0].length == 2 && box.widgets[0][0].wLabel == "[END]") { ilfIsLastPage = true;	}
			// When looking for the Ilf editor auto-Gui buttons, we're only interested in the boxes that start on the row number before and the row number of the maximum number of display rows along with the audit box
			if (box.data.includes("VIEW AUDIT INFO")) { box.isAuditBox = true; };
			if (ilfEditorMode && ((box.rowtext.length > 0 && box.begin_row >= parseInt(appxLoginFormRows, 10) - 1 && box.begin_row <= parseInt(appxLoginFormRows, 10)) || (box.isAuditBox !== undefined))) {
				appxGenerateAutoGuiButtons(box, i, cel);
			}

            //console.log("box %d(%d)(%d) begin: %d/%d end %d/%d mask 0x%s, label=%s",i,(box.widget == null ? -1 : box.widget.wBoxNumber),box.newbox,box.begin_row,box.begin_column,box.end_row,box.end_column,box.bit_mask.toString(16),box.widget.wLabel);
            if (appxIsInProgress(box))
                inProgressBox = box;
/*
            if (appxIsEmBuild(box)) {
                $("#appx_status_date").html(box.widget.wLabel);
                continue;
            }
*/
            if (box.begin_row == 0 && box.end_row == 0) {
                continue;
            }
            if (box.end_row > (appx_session.screenrows - 3))
                box.end_row = appx_session.screenrows - 3;
            if (box.end_column > appx_session.screencols)
                box.end_column = appx_session.screencols;
            // Full window scrolling inputs, not really scrolling but used to get Prev/Next Record with PageUp/Down keys
            if (box1 != null && appxIsScrollReg(box) && appxIsScrollAct(box) && appxIsScroll(box1) && box.begin_row == 1 && box.begin_column == 1 && box.begin_row == box1.begin_row && box.end_row == box1.end_row && box.begin_column == box1.begin_column && box.end_column == box1.end_column) {
                box.newbox = i - 1;
                box.bit_mask &= ~SCROLL_REG;
                box.bit_mask &= ~SCROLL_ACT;
                box1.bit_mask &= ~SCROLL;
                appxMergeBoxes(box, box1);
                box3 = null;
                box2 = null;
                box1 = null;
            }
            //we don't want to do any merging id it is scrolling area or in oldILFDebugger. We do specialized merging if in ILF Debugger
            if (appxIsScrollReg(box) || isOldILFDebugger) {
                box3 = null;
                box2 = null;
                box1 = null;
            }
            else {
                box3 = box2;
                box2 = box1;
                box1 = box;
            }
            if (box3 != null &&
                box3.begin_column == box2.begin_column && 
                box2.begin_column == box1.begin_column &&
                box3.end_column == box2.end_column && 
                box2.end_column == box1.end_column &&
                box1.end_row >= (box2.begin_row - 1) && 
                box3.end_row >= (box1.begin_row - 1) &&
                appxIsScroll(box1)) {
                    /*If outer boxes are exactly the same as inner boxes, but the inner boxes have
                    **more attributes (or has title) then we get rid of the outer box & keep the inner box*/
                    if ( box3.data_length < box2.data_length && appx_box_hasTitleText(box3) == false){
                        box3.newbox = i - 2;
                        box2.scrollrow = box1.begin_row - (box2.begin_row - 1);
                    } else {
                        box3.end_row = box2.end_row;
                        box2.newbox = i - 2;
                        appxMergeBoxes(box2, box3);
                        box3.scrollrow = box1.begin_row - (box3.begin_row - 1);
                    }
            }
            if (box2 != null && 
                box2.begin_column <= box1.begin_column && 
                box2.data.indexOf("SWBC=") === -1 &&
                box2.end_column >= box1.end_column && 
                (box2.begin_row - 1) <= box1.end_row && 
                box2.end_row >= (box1.begin_row - 1)) {
                    if (appxIsScroll(box1)) {
                        box2.scrollrow = box1.begin_row - (box2.begin_row - 1);
                    }
                    else if (Math.abs(box1.bit_mask & BORD_AROUND) == 0 && 
                            box1.widget.wIconWallpaper == null && 
                            box1.widget.wColorBgWallpaper == null && 
                            box1.widget.wBorder == null && 
                            appx_box_hasTitleText(box1) == false && 
                            box1.widget.wBorder == null) {
                                if (box2.end_row < box1.end_row) {
                                    box2.end_row = box1.end_row;
                                }
                                box1.newbox = i - 1;
                                /*If there are 3 boxes then top box should be
                                **2 less than i, otherwise 1 less*/
                                if (box3 === null) {
                                    topMerged = box1.newbox;
                                } else {
                                    topMerged = box1.newbox - 1;
                                }
                                appxMergeBoxes(box1, box2);
                                box1 = box2;
                                box2 = box3;
                    }
            }
            if (!(appxIsScroll(box) || appxIsScrollReg(box))) {
                /*If boxes were merged then the top box is not i*/
                if (topMerged !== null) {
                    topBoxIdx = topMerged;
                } else {
                    topBoxIdx = i;
                }
            }
        }
        if (appx_session.current_show.boxes[topBoxIdx] && appx_session.current_show.boxes[topBoxIdx].hasOwnProperty("newbox"))
            topBoxIdx = appx_session.current_show.boxes[topBoxIdx].newbox;

        appx_session.topboxid = topBoxIdx;
        if(isOldILFDebugger){
            ilfDebugger_mergeBoxes();
        }
        var scrollBoxNo = 0;
        var boxLayer = 0;
        var firstBoxIndex = 0;
		
		// When in ilf editor mode, we'll check now to see if any of the boxes have a mismatch in the amount of items and the amount of row text
		//  Any mismatch in any box indicates that we're either in add or change mode
		ilfAddorChangeMode = false;
		if (ilfEditorMode) {
			for (var idx = 0; idx < appx_session.current_show.boxes.length; idx++) {
				var currentbox = appx_session.current_show.boxes[idx];
				if (currentbox.rowtext.length != currentbox.items.length) {
					ilfAddorChangeMode = true;
					break;
				}
			}
		} 
		
        for (var i = 0; i < appx_session.current_show.boxes.length; i++) {
            var box = appx_session.current_show.boxes[i];
            //If we are receiving empty box do not paint to screen
            if ((box.bit_mask == 0x40040000 && box.items[0] == null &&
                box.widgets[0] == null && box.rowtext[0] == null) &&
                (Math.abs(box.bit_mask & BORD_AROUND) == 0 &&
                    box.widget.wIconWallpaper == null &&
                    box.widget.wColorBgWallpaper == null && box.widget.wBorder == null &&
                    box.widget.wLabel == null)) {
                        /* This is an exception for image editor. Image editor sends an extra empty box which normally we don't display.
                        ** however if the actual imageeditor's frame has a first row title bar, html clinet displays that empty box, because
                        ** engine set the title on the empty box. Easiest way to fix this issue without breaking anything else is to add
                        ** an expection to image editor screen here. So if the only property that the box has is the title that is set by
                        ** the engine (screenTitleText) and one of the next boxes is image editor, we hide this box*/
                        if( box.widget.screenTextTitle != null ){
                            if( i == 0 && appx_session.current_show.boxes[i+1].widget.wWidgetType == WIDGET_TYPE_IMG_EDITOR_FRAME){
                                //if we don't paint this box, we don't consider this as first box. So, mark the next box as possible first box
                                if( i == firstBoxIndex){
                                    firstBoxIndex++;
                                }
                                continue;
                            }
                        }
                        //if screenTextTitle is also null there is nothing to display, so skip this box
                        else{
                            //if we don't paint this box, we don't consider this as first box. So, mark the next box as possible first box
                            if( i == firstBoxIndex){
                                firstBoxIndex++;
                            }
                            continue;
                        }
            }
            if (appxIsEmBuild(box))
                continue;
            if (box.begin_row == 0 && box.end_row == 0) {
                continue;
            }
            if (appxIsInProgress(box))
                continue;
            if (box.newbox >= 0) {
                continue;
            }
            if (appxIsScroll(box))
                scrollBoxNo = boxcnt;
            if (appxIsScrollReg(box))
                boxLayer = 1000 * (scrollBoxNo + 1);
            else
                boxLayer = 1000 * (boxcnt + 1);
            box.layer = boxLayer;
            //see if we can should use the first row as  the title bar. We don't want to add the first row as title if we are in image editor
            if (!(appxIsScroll(box) || appxIsScrollReg(box) || (box.widget && box.widget.wWidgetType == WIDGET_TYPE_IMG_EDITOR_FRAME))) {
                if (box.rowtext && box.rowtext.length > 0) {
                    var title = "";
                    var addTitle = false;
                    var title_rowtext = [];
                    //first find out if the first row is title
                    for (var j = 0; j < box.rowtext.length; j++) {
                        var rowtxt = box.rowtext[j];
                        //for backward compatibily, we don't check for isTitle if appx is version 5.x
                        if (rowtxt.pos_row == 1 && (!appx_session._isServerUnicode || rowtxt.isTitle) && rowtxt.string && rowtxt.string.trim().length > 0) {
                                addTitle = true;
                                break;
                        }
                    }
                    //if first row is title, go through all rowtexts on line one and create a title text, also delete the text so it doesn't show up 2 times
                    if(addTitle == true){
                        for (var j = 0; j < box.rowtext.length; j++) {
                            var rowtxt = box.rowtext[j];
                            if (rowtxt.pos_row == 1) {
                                //save all of the data in the first row. Row text maybe out of order. Store them by col number (they are all on line 1) 
                                title_rowtext.push({'col':rowtxt.pos_col, 'string': rowtxt.string.trim()});
                                rowtxt.string = "";
                            }
                        }
                    }

                    if (addTitle == true && title_rowtext.length > 0 && box.widget.wLabel == null && box.widget.screenTextTitle == null){
                        //sort title_rowtext and then concatinate them together to build the title
                        title_rowtext.sort( function(a,b){ return a.col - b.col});
                        box.widget.wLabel = "";
                        for(var ii = 0; ii < title_rowtext.length; ii++){
                            box.widget.wLabel += title_rowtext[ii].string + " ";
                        }
                    }
                }
                //set the screen text title to wLabel so it becomes the screen title
                if (box.widget.screenTextTitle && box.widget.wLabel == null && box.widget.screenTextTitle.length > 0 ){
                    box.widget.wLabel = box.widget.screenTextTitle;
                }
            }
            var boxwidth = ((box.end_column - box.begin_column) * cel.w) + (3 * cel.w) + 2;
            var boxheight = ((box.end_row - (box.begin_row - 1)) * cel.h) + (1 * cel.h);
            if (appxIsScroll(box)) {
            /*Scroll Boxes don't have an extra margin.  The - 4 is to leave the
                    **box 4 pixels larger for scroll boxes.  This allows room for the 2
                    **pixel top and bottom needed to render correctly */
                boxheight -= ((1 * cel.h) - 4);
            } else {
                boxheight += (1 * cel.h);
            }
            /*Firefox doesn't honor ending main content at footer element. This accounts for places (hopefully all of them)
            **where the box would overlay the footer bar.*/
            if (boxheight >= document.documentElement.clientHeight && (box.data.indexOf("SBN=0") > -1 && checkBrowser() === "Firefox")) {
                boxheight += appx_session.rowHeightPx;
            }

            // BUG #3712: Popup input screen is centered incorrectly
            if( i==0 ) {
                var backgroundBoxHeight = boxheight;
            }

            var boxtop = (box.begin_row * cel.h) - cel.h;
            // Fix for bug #5177 - An input process scrolling frame does not centering within the window when called from another input process as an automatic child
			var lastBoxLeft = 0;
			if (appxIsScroll(box) && boxleft !== undefined) {
				lastBoxLeft = parseInt(boxleft, 10);
			} else {
				lastBoxLeft = 0;
			}
            var boxleft = (box.begin_column * cel.w) - cel.w;
            if(box.widget && box.widget.wPositionX && box.widget.wPositionY){
                //Readjust the position based on widget data
                boxtop  = (box.widget.wPositionY * cel.h) - cel.h;
                boxleft = (box.widget.wPositionX * cel.w) - cel.w;
            }
            /*If engine was told to center the box, then we remove the horizontal
            **centering the engine did to allow client to handle horizontal centering.*/
            if (box.widget.specialHorizontalLocation == 0 && !appxIsScrollReg(box)) {
                    boxleft = 0;
            }
            /*We also remove vertical centering, but only apply our own vertical
            **centering if a smaller box should be positioned inside a larger box.*/
            if (!(appxIsScroll(box) || appxIsScrollReg(box))) {
                var tempVert = (($("#appx_main_container").height() - boxheight) / 2);
                if (i === 0 || tempVert > appx_session.centerVerticalOffset) {
                    appx_session.centerVerticalOffset = tempVert;
                }
                
                if (box.widget.specialVerticalLocation == 0) {
                    boxtop = 0;
                    if (i > 0) {
                        var tempBoxHeight = box.end_row - box.begin_row;
                        //var tempPrevBoxHeight = appx_session.current_show.boxes[i - 1].end_row - appx_session.current_show.boxes[i - 1].begin_row;
                        if (tempBoxHeight <= backgroundBoxHeight) {
                         // BUG #3712: Popup input screen is centered incorrectly
                         // JES: centers on 'main' rather than primary frame box. Chose to use 'backgroundBoxHeight' instead of last Box Height
                         //      to ensure popup-within-popup is properly centered. Formerly     boxtop = appx_session.centerVerticalOffset;
                            boxtop = ( backgroundBoxHeight - boxheight) / 2;
                        }
                    }
                }
            }

            /*The first box is the "main appx window" so we need the width to
            **properly calculate the offset for centering inside browser. It's
            **possible for overlay box to be larger than main window so we check
            **for that also and use the smallest offset*/
            var mainWidth = $("#appx_main_container").width();
            var tempOffset = ((mainWidth - boxwidth) / 2);
            if (tempOffset < 0) {
                tempOffset = 0;
            }
            if ((i === firstBoxIndex || (tempOffset < appx_session.centerHorizontalOffset)) &&
                appx_session.getProp("centerAppx")) {
                var curOffset = appx_session.centerHorizontalOffset;
                appx_session.centerHorizontalOffset = tempOffset;

                /*If we change the offset based on box that is not the first box
                **then we go back and change the left position for all previous boxes*/
                for (var j = 0; j < i; j++) {
                    var tempBox = appx_session.current_show.boxes[j];
                    var $tempBox = $("#box_" + tempBox.widget.wBoxNumber.toString());
                    if ($tempBox.length) {
                        var tempBoxLeft = ($tempBox.position().left - curOffset);
                        if (tempBoxLeft < 0) {
                            tempBoxLeft = 0;
                        }
                        $tempBox.css({
                            "left": ((tempBoxLeft + appx_session.centerHorizontalOffset) + "px")
                        });
                    }
                }
            }
            if (tempOffset > appx_session.centerHorizontalOffset &&
                !(appxIsScroll(box) || appxIsScrollReg(box)) &&
                box.widget.specialHorizontalLocation == 0) {
                tempOffset = tempOffset - appx_session.centerHorizontalOffset;
            } else {
                tempOffset = 0;
            }

            if (appxIsScroll(box)) {
                boxleft += !ilfEditorMode ? 3 : 1;
                boxwidth -= !ilfEditorMode ? 6 : 1;
            }
            if (appxIsScrollReg(box)) {
                boxleft += !ilfEditorMode ? 3 : 1;
                boxwidth -= !ilfEditorMode ? 6 : 1;
            }
            var $boxhtml2 = null;
            //don't draw the title bar if there is no title
            //also don't draw the title bar if we are in image editor and the process is output or the process is inquiry and the frame is key entry frame <-- this logic came from java client isPrinterAspect function
            if (box.widget.wLabel && box.widget.wLabel != "" &&
                !(  box.widget.wWidgetType == WIDGET_TYPE_IMG_EDITOR_FRAME && 
                    ( 
                        box.widget.wSetProcessType == PROCESS_TYPE_OUTPUT || 
                        ( 
                            box.widget.wSetProcessType == PROCESS_TYPE_INQUIRY && 
                            box.widget.wSetFrameClass != FRAME_CLASS_KEY_ENTRY 
                        )
                    )
                ))
            {
                var m = 2; //margin
                var t = m;
                var l = m;
                var w = (boxwidth - (m * 4)) + "px";
                var h = (cel.h) + "px";
				var wstype = !ilfEditorMode ? "normal" : "pre";
				
                if (appxFillWindow == "true" && box.end_column - box.begin_column + 1 == appx_session.screencols) {
                    w = "100%";
                    t = "0px";
                    l = "0px";
                }
                var $boxhtml2 = $("<div>").css({
					"white-space": wstype,
                    "border": "0px", //remove any border
                    "box-sizing": "border-box",
                    "display": "table",
                    "height": h,
                    "left": l,
                    "overflow": "hidden",
                    "position": "absolute",
                    "top": t,
                    "width": w,
                    "z-index": boxLayer + 1
                });
                $boxhtml2.addClass("appx-title");
                $boxhtml2.attr("id", "box_2_" + box.widget.wBoxNumber.toString());
                $boxhtml2.data("row", box.widget.wPositionY);
                $boxhtml2.data("col", box.widget.wPositionX);
                box.widget.isBox = false;
                appxwidgetshandlerpropsex(box.widget, $boxhtml2, true);
            }
            //</toolbar>

            /*Adjust box positioning to center in browser*/
            boxleft = ((boxleft + tempOffset + appx_session.centerHorizontalOffset) + "px");

            // Fix for bug #5177 - An input process scrolling frame does not centering within the window when called from another input process as an automatic child
			if (appxIsScroll(box) && lastBoxLeft > parseInt(boxleft, 10)) { 
				boxleft = (lastBoxLeft + (!ilfEditorMode ? 3 : 1) + "px");
			}

            if (appxFillWindow == "true" && box.begin_row == 1 && box.begin_column == 1 && box.end_row == (appx_session.screenrows - 3) && box.end_column == appx_session.screencols) {
                boxwidth = "100%";
            }
			
            /* Adjust the box height when the box height is within 4 rows of the max screen rows of this session and, when that box has */
			/* a button widget on the last row and, that button has a micro-offset height >= to the height of a row */
            /* This is to emulate the behavior of the ADC when apps are designed to use the extra space available in the ADC, primarily AXAEM */
			if (((appx_session.screenrows - 3) - box.end_row <= 4) &&
				(box.widgets.length > 0 &&
				parseInt(box.widgets[box.widgets.length - 1][0].wWidgetType) == WIDGET_TYPE_BUTTON) &&
				((box.widgets[box.widgets.length - 1][0].wOffsetH / cel.h >= 1) ||
				(box.widgets[box.widgets.length - 1][0].wOffsetY / cel.h >= 1))) {
				boxheight += (cel.h);
			}
			
            var $boxhtml = $("<div>").css({
                "box-sizing": "border-box",
                "height": boxheight,
                "left": boxleft,
                "overflow": "hidden",
                "position": "absolute",
                "top": boxtop,
                "width": boxwidth,
                "z-index": boxLayer
            });
            if ($boxhtml2) $boxhtml.append($boxhtml2);
            $boxhtml.addClass("appxbox");
            $boxhtml.attr("id", "box_" + box.widget.wBoxNumber.toString());
            $boxhtml.data("row", box.widget.wPositionY?box.widget.wPositionY:box.begin_row);
            $boxhtml.data("col", box.widget.wPositionX?box.widget.wPositionX:box.begin_column);
            $boxhtml.attr("data-role", "content");
            if (box.begin_row == 1 && box.begin_column == 1 && box.end_row == (appx_session.screenrows - 3) && box.end_column == appx_session.screencols) {
                $boxhtml.addClass("appxbox-full");
            }
            box.widget.isBox = true;
            //remove mouse location on statusbar if exists
            if($("#appx_mouse_loc").length >= 0 ){
                $("#appx_mouse_loc").html("");
            }
            /*is this an image editor*/
            if(box.widget.wWidgetType == WIDGET_TYPE_IMG_EDITOR_FRAME){
                $boxhtml.addClass("appxImageEditor");
                $boxhtml.attr("tabindex","0"); //set the tabindex so imageeditor screen can get the focus. Important for keyboard event overrides
                $boxhtml.data("widget", box.widget);
                //setup mouse events 
                imageEditor_event_setup($boxhtml);
            }
            else if(box.ilfDebuggerMain == true){
                $boxhtml.addClass("appxILFDebugger"); //the main box is the old ilf debugger
            }
            else if(box.ilfDebuggerStatements == true){
                $boxhtml.addClass("appxILFDebuggerStatements");//This is the scrolling section of the old ilf debugger
            }
            /*is this an ILF editor*/
            else if(box.widget.wWidgetType == WIDGET_TYPE_ILF_EDITOR_FRAME)
                $boxhtml.addClass("appxILFEditor");

            appxwidgetshandlerprops(box.widget, $boxhtml);

            //store box between items for tabindexing
            $_screenBuf.append($boxhtml);
            if ( //lets see if this is the box that contains the cursor
                cur.row >= box.begin_row && cur.row <= box.end_row &&
                cur.col >= box.begin_column && cur.col <= box.end_column) {
                if (_boxcur == null ||
                    (_boxcur.end_row - _boxcur.begin_row) >= (box.end_row - box.begin_row) ||
                    (_boxcur.end_column - _boxcur.begin_column) >= (box.end_column - box.begin_column)
                ) {
                    if (Math.abs(box.bit_mask & SCROLL) == 0) {
                        _boxcur = box;
                        $boxcurhtml = $boxhtml;
                    }
                }
            }

            //<scroll>
            //no scrolling classes if box size is full screen
            if (!((box.end_column - box.begin_column) + 1 >= appx_session.screencols &&
                (box.end_row - box.begin_row) + 1 >= (appx_session.screenrows - 3))) {
                if (appxIsScroll(box)) {
                    $boxhtml.addClass("appx-scroll");
                    $boxhtml.css("top", boxtop + cel.h); //remove the -1 offset row
                    if(box.widget.specialHorizontalLocation == 0){
                        $boxhtml.data("specialHorizontalLocation", 0);
                    }
                }
                else if (appxIsScrollReg(box)) {
                    /*We do not want scroll regions to be part of the main box, we
                    **want them merged into the scroll box*/
                    var scrollParentArray = $boxhtml.siblings(".appx-scroll");
                    var scrollParent;
                    /*Possible to have multiple scroll boxes on one screen. To get
                    **the correct one, we match the z-indexes.*/
                    for (var j = 0; j < scrollParentArray.length; j++) {
                        if ($(scrollParentArray[j]).css("z-index") === $boxhtml.css("z-index")) {
                            scrollParent = $(scrollParentArray[j]);
                        }
                    }
                    if (scrollParent) {
                        //current parent location
                        var parentTop = parseInt(scrollParent.css("top"));
                        var parentLeft = parseInt(scrollParent.css("left"));
                        var boxL = parseInt(boxleft);
                        //did we modify the scroll area to be center justified based on widget property "specialHorizontalLocattion?
                        if(parseInt(scrollParent.data("specialHorizontalLocation")) == 0){
                            //find original parent location then the difference between the original location and modified location
                            var parentOrigLeft = ((parseInt(scrollParent.data("col")) -1 )* appx_session.colWidthPx)+3; //we add 3 pixels to scroll regions/areas
                            //boxL is the adjusted left location based on the adjusted scroll area
                            boxL = Math.abs((boxL - appx_session.centerHorizontalOffset) + (parentLeft - parentOrigLeft));
                        }
                        //we need to adjust the location of scroll region when we add it to scroll area
                        var adjTop = Math.abs(parentTop - (boxtop + cel.h));
                        var adjLeft = Math.abs(boxL - parentLeft );                   /* Bug #4303: formerly:   var adjLeft = (parentLeft - boxL); */
                        var adjWidth = scrollParent.width();
                    }
                    if (appxIsScrollAct(box)) {
						if (!ilfEditorMode) {
							$boxhtml.addClass("appx-scroll-act");
						} else {
							$boxhtml.addClass("appx-ilf-scroll-act");
						}
                        $boxhtml.removeClass("appxbox");
                        bPrevScrollAct = true;
                    } else {
                        if (bPrevScrollAct) {
							if (!ilfEditorMode) {
								$boxhtml.addClass("appx-scroll-act-next");
							} else {
								$boxhtml.addClass("appx-ilf-scroll-act-next");
							}
                            $boxhtml.removeClass("appxbox");
                            bPrevScrollAct = false;
                        }
                        else {
							if (!ilfEditorMode) {
								$boxhtml.addClass("appx-scroll-reg");
							} else {
								$boxhtml.addClass("appx-ilf-scroll-reg");
								$boxhtml.on('dblclick', function ilf_ondblclick(event){
									event.stopPropagation();
									// Since a single click has already occurred, we're likely locked as the server sends us new data
									// If so, we'll wait for a bit for the server to finish and the lock to clear 		
									if (appxIsLocked()) {
										let delay = 200;
										setTimeout(function() {
											if (appxIsLocked()) { return; }
											appxwidgetcallback(94);
										}, delay);					
									} else {
										appxwidgetcallback(94);
									}	
								});
							}
                            $boxhtml.removeClass("appxbox");
                        }
                    }
                    /*Attach scroll regions to scroll box*/

                    $boxhtml.appendTo(scrollParent);
                    $boxhtml.data("col", box.begin_column);
                    $boxhtml.data("row", box.begin_row);
					
					// When in ilf add or change mode, we need to handle inadvertent clicks that occurred somewhere outside of the 
					// editable Ilf code area, otherwise a SCR_OPT_RTN {i.e. cancel, see: ie.h } will be sent to the engine
					if (ilfEditorMode && ilfAddorChangeMode) {
						$boxhtml.data("ilfchangemode", true);
					}
					
                    $boxhtml.click(function tag_onclick(event) {
                        appxScrollClick($(this), event);
                    });
                    $boxhtml.css({
                        "top": adjTop + "px",
                        "left": adjLeft + "px",
                        "width": adjWidth + "px"
                    });
                    $boxhtml.height(boxheight - (2 * cel.h)); //remove the default two extra lines
                }
                else if (!box.widget.wBorder) {
                    $boxhtml.addClass("appx-border-bevel-raised");
                }
            }
            //</scroll>
            /*Is box movable*/
            if (box.widget.wMovable) {
                $boxhtml.data({
                    "wMovableCommand": box.widget.wMovableCommand
                })
                $boxhtml.draggable({
                    cancel: ".appx-modifiable, .ui-pg-input, .ui-pg-selbox",
                    stop: function $_draggable_drop() {
                        var r = Math.floor(($(this).offset().top - $("#appx_main_container").offset().top) / appx_session.rowHeightPx) + 1;
                        var c = Math.floor(($(this).offset().left - $("#appx_main_container").offset().left - appx_session.centerHorizontalOffset) / appx_session.colWidthPx) + 1;
                        appxPutCursor(c, r);

                        if ($(this).data("wMovableCommand")) {
                            appxwidgetcallback($(this).data("wMovableCommand"));
                        }
                    }
                });
            }



            /*$boxhtml.dblclick(function $_dblclick() {   ##DELETEUSERPREFS##
                if (appx_session.getProp("appxDoubleClick") == "true")
                    appxwidgetcallback(OPT_ENTER);
            });*/
            $("a").each(function $_each() {
                $(this).attr("tabindex", -1);
            });
            $("button").each(function $_each() {
                $(this).attr("tabindex", -1);
            });
            if ((appxIsScroll(box) || appxIsScrollReg(box))) {
                var scrollIdx = appxFindBoxIdx(box.begin_row, box.begin_column, (box.end_row - box.begin_row + 1), (box.end_column - box.begin_column + 1), false);
            } else {
                scrollIdx = i;
                var boolTopBox = true;

                /*If every box after this box has a newbox property (box was merged)
                **or a scroll(scrolls can't be top box) then current box is the top box & the topBoxIdx should be equal
                **to i*/
                for (var j = i + 1; j < appx_session.current_show.boxes.length; j++) {
                    if (!appx_session.current_show.boxes[j].hasOwnProperty("newbox") && !appxIsScroll(appx_session.current_show.boxes[j]) &&
                        !appxIsScrollReg(appx_session.current_show.boxes[j])) {
                       boolTopBox = false;
                       break;
                    }
                }
                if (boolTopBox) {
                    topBoxIdx = i;
                }
            }
            appxshowrowtextforbox(box);
            appxshowitemsforbox(box, topBoxIdx == scrollIdx, $boxhtml);
            appxshowwidgetsforbox(box, topBoxIdx == scrollIdx);
            /*At this point all the items on the box has been added*/
            if(box.widget.wWidgetType == WIDGET_TYPE_IMG_EDITOR_FRAME){
                /*reselect imageEditor's selected Items*/
                appxImageEditor_reselect_items();
                //draw the guidebars if they exist
                appxImageEditor_displayGuideBars();
                //set showgrid based on user's preferences when grid is implamented
                imageEditor_update_gridlines(appx_session.getProp("showGridlines"));
                //show bound is session dependent and is not stored on disk
                imageEditor_update_showBounds(appx_session.imageEditor_show_bound);
            }
            else if(box.ilfDebuggerStatementLine == true){
                //all the widgets and items have been added at this point. Now we can do some extra processing specific to statements
                ilfDebugger_style_statements($boxhtml);
            }
            else if(box.ilfDebuggerMain == true){
                //if it is old appx ilf debugger, and footerbox exists, add the footer buttons to it
                //footerbox only exists if you set APPX_OLD_DBG env variable
                if(box.ilfDebuggerHasFooterBox == true){
                    ilfDebugger_create_footer_buttons();
                }
            }
            if (!(appxIsScroll(box) || appxIsScrollReg(box))) {
                if (topBoxIdx != i) {
                    $boxhtml.addClass("appx-not-modifiable");
                }
                else {
                    $boxhtml.addClass("appx-active-box");
                }
            }

			// Add a click event to any ilf editor auto gui buttons	in this box
			if (ilfEditorMode) {
				$(".ilf-autogui-button").on("click", appxIlfAutoGuiButtonClick);
			}

            boxcnt++;
        }

        if (inProgressBox && !blocked) {
            try {
                blocked = true;
				var progressLabel = (inProgressBox.widget.wLabel !== undefined && inProgressBox.widget.wLabel !== null) ?
									inProgressBox.widget.wLabel : "In Progress"; 

                $("#main").block({
                    message: progressLabel + "...",
                    centerY: false,
                    css: {
						// Uncomment those below will allow the css selector/class appx-in-progress-msg to take precedence 						
						//backgroundColor: null,
						//color: null,
                        opacity: 1,
						cursor: "wait",
                        "z-index": 10000
                    },
                    blockMsgClass: "appx-in-progress-msg"
                });
            }
            catch (e) { }
        }
    }
    catch (ex) {
        console.log("appxshowboxes: " + ex);
        console.log(ex.stack);
    }
}

/*
** this function returns true if box has title text based on widget or the row text. 
*/
function appx_box_hasTitleText(box){
    //if we have wLabel or screenTextTitle, we have a title text 
    if( box.widget &&  
        ((box.widget.wLabel && box.widget.wLabel.length > 0) ||  
         (box.widget.screenTextTitle && box.widget.screenTextTitle.length > 0)
        )){
        return true;
    }
    //see if we can should use the first row as  the title bar. We don't want to add the first row as title if we are in image editor
    else if (!(appxIsScroll(box) || appxIsScrollReg(box) || (box.widget && box.widget.wWidgetType == WIDGET_TYPE_IMG_EDITOR_FRAME)) && 
              (box.rowtext && box.rowtext.length > 0) &&
              (( box.bit_mask & BORD_OBS ) != 0 || ( box.bit_mask & BORD_OLINE ) != 0 )) {
        for (var j = 0; j < box.rowtext.length; j++) {
            var rowtxt = box.rowtext[j];
            if (rowtxt.pos_row == 1 && rowtxt.isTitle && rowtxt.string && rowtxt.string.trim().length > 0) {
                return(true);
            }
        }
    }
    //if we get to this point, we don't have a title text
    return(false);
}

//Display.drawCursor
function appxshowcursor(bFocus) {
    try {
        var cel = appxGetCellSize();
        var pos = appxGetCursorPos();
        if (!bFocus || ilfEditorMode) {
            var bCurItem = false; //cursor inside field?
            var box = appxfindbox(pos.row, pos.col, 1, 1, true);
            if (!box) return;
            //check if this box is image editor
            var box_is_image_editor = false;
            if(box.widget && box.widget.wWidgetType == WIDGET_TYPE_IMG_EDITOR_FRAME ){
                box_is_image_editor = true;
            }

            var $cur = $("#appxCursor");
            var firstModItem = null;
			var curAlpha = !ilfEditorMode ? .5 : .4;
            if ($cur.length == 0) {
                $cur = $("<div>");
                $cur.attr("id", "appxCursor");
                $cur.css({
                    "background-color": "rgba(0,0,0," + curAlpha + ")",
                    "height": cel.h,
                    "position": "absolute",
                    "width": cel.w,
                    "z-index": "100000",
                    "pointer-events": "none"
                });
                $("#appx_main_container").append($cur);
            }

            var curLeft = ((pos.col * cel.w) + appx_session.centerHorizontalOffset);
            if (appxIsScrollReg(box)) {
                curLeft -= !ilfEditorMode ? 3 : 5;
            }
            var curTop = (pos.row * cel.h);

            //add the box border to it for more accuracy
            var boxhtml = $("#appx_main_container").children(".appxbox").get(0);
            if(boxhtml){
                curTop  += parseInt($(boxhtml).css("border-top-width"));
                curLeft += parseInt($(boxhtml).css("border-left-width"));
            }
            $cur.css({
                "left": (curLeft + "px"),
                "top": (curTop + "px")
            });

            $(".appx-scroll-act-new").removeClass("appx-scroll-act-new");
             
            //do the following only if we are NOT in image editor
            if(!box_is_image_editor){    
                if (appxIsScrollReg(box) && !appxIsScrollAct(box) && (!ilfEditorMode || (ilfEditorMode && appx_session.getProp("highlightSelectIlfRow")))) {
                    $("#box_" + box.widget.wBoxNumber.toString()).addClass("appx-scroll-act-new");
                }

                for (var i = 0; i < box.items.length; i++) {
                    var item = box.items[i];
                    if (item[1] != null) {
                        var $tag = item[1][0];

                        if (item[0].hasOwnProperty("pos_row")) { //Item, not box

                            if (firstModItem == null && appxIsModifiable(item[0]))
                                firstModItem = item;

                            var pc = 0;
                            if (appx_session.keyLeft) {
                                pc = 1;
                            }
                            /*Set position row & column to adjust for being inside box instead
                            **of statically positioned on main screen*/
                            var modPos = {
                                row: pos.row - box.begin_row + 1,
                                col: pos.col - box.begin_column + 1
                            };
                        /*Note: Position adjustment seems to be not needed for scrolling screens?
                                Do this only when we navigate through the items in a record not when
                                we are changing the selected row
                            #BUG: 4565 */ 
                            if(appxIsScrollReg(box) && appxIsScrollAct(box)){
                                modPos = {
                                    row: pos.row,
                                    col: pos.col
                                }
                            }

                            if (
                                appxIsModifiable(item[0]) &&
                                modPos.row >= item[0].pos_row &&
                                modPos.row <= (item[0].pos_row + item[0].size_rows - 1) &&
                                pc + modPos.col >= item[0].pos_col &&
                                modPos.col <= (item[0].pos_col + item[0].size_cols - 1)
                            ) {
                                bCurItem = true;
                                setInputFocus($tag);
                                break;
                            }
                        }
                    }
                }
                
				if (!ilfEditorMode) {
					$cur.css("visibility", ((bCurItem || appxIsLocked() || (appx_session.getProp("drawBlockCursor") != true && !appx_session.showCursor)) ? "hidden" : "visible"));
				} else {
					// Always show the cursor when in the Ilf editor
					$cur.css("visibility","visible");
				}
            }
            else{
                //if we are in image editor, always display the cursor
                $cur.css("visibility","visible");
                //if body has the focus,change the focus to image editor so it can intercept the keyboard events
                if(!document.activeElement || document.activeElement == document.body){
                    $(".appxImageEditor").trigger("focus");
                }
            }
            return bCurItem;

        } else {
            var eFocus = $("#appxitem_" + pos.col + "_" + pos.row);
            if (eFocus.length === 0 || !eFocus.hasClass("appx-modifiable")) {
                eFocus = $(".appx-modifiable").first();
            }
            if (eFocus[0] !== undefined) {
                setInputFocus(eFocus[0]);
            }
        }
    }
    catch (ex) {
        console.log("appxshowcursor: " + ex);
        console.log(ex.stack);
    }
}

function appxnestwidgets() {
    var boxNos = {};
    try {
        for (var i = 0; i < appx_session.current_show.boxes.length; i++) {
            var box = appx_session.current_show.boxes[i];
            box.widget = new Widget(0, "", box.data);
            if (box.widget) {
                boxNos[box.widget.wBoxNumber] = i;
            }
        }
    }
    catch (ex) {
        console.log("appxidboxes: " + ex);
        console.log(ex.stack);
    }

    try {
        var itemsNeedReprocessing = {data:[]};
        // process items
        for (var i = 0; i < appx_session.items.length; i++) {
            var item = appx_session.items[i][0];
            var box = appxitembox(item.pos_row, item.pos_col, item.size_rows, item.size_cols);
            var $tag = appx_session.items[i][1];
            /* If we are in image editor and we put a widget on column 1, appx engine thinks that the widget belongs to the rowtext, since in 
            ** image editor, rowtexts are being sent as items. So, it merges the row text and widget. To get around 
            ** this problem, we need to recreate these tags without widget. We cannot do it during the tag creattion earlier because
            ** we don't have the box info so we don't know if we are in image editor */
           //put the element in a cache for now and reprocess them after we processed all the items.
            if(box && box.widget && box.widget.wWidgetType == WIDGET_TYPE_IMG_EDITOR_FRAME){
                if(item.widget && item.widget.wWidgetOriginalType != WIDGET_TYPE_ROWTEXT){
                    item.widget = null;
                    itemsNeedReprocessing.data.push(item);
                    continue;
                }
            }

            /*Items not in scroll boxes haven't been adjusted for box positioning yet.
            **Not sure where scroll box adjusting gets done for items.*/
            if (!(appxIsScroll(box) || appxIsScrollReg(box))) {
                item.init_pos_row = item.pos_row;
                item.init_pos_col = item.pos_col;
                item.pos_row -= box.begin_row - 1;
                item.pos_col -= box.begin_column - 1;
                var topAdjPx = item.pos_row * appx_session.rowHeightPx;
                var leftAdjPx = item.pos_col * appx_session.colWidthPx;

                if ($tag) {
                    $tag.css({
                        "top": topAdjPx + "px",
                        "left": leftAdjPx + "px"
                    });
                }
            } else {
                if ($tag) {
                    var tempTop = ((box.begin_row - item.pos_row) * appx_session.rowHeightPx);
                    $tag.css({
                        "top": tempTop + "px",
                    });
                }
            }
            if (box) {
                box.items.push(appx_session.items[i]);
            }

        }
        appx_session.items = [];

        /*we need to re-process the row text items again but this time without widget*/
        if(itemsNeedReprocessing.data.length > 0){
            /* This function adds the items that need to be processed to appx_session.items and appx_session.rowtexts
            ** we need to make sure that we clears those, otherwise we get duplicate. appx_session.items are already cleared
            ** and the ones that are going to be added are the ones we need to reprocess; appx_session.rowtexts have not been 
            ** processed yet, so we are going to have duplicate after running appxitemhandler function. To get around this issue,
            ** restore the appx_session.rowtexts array after running this function.
            */ 
            let rowtext_size = appx_session.rowtext.length; 
            appxitemshandler(itemsNeedReprocessing);
            /*appxitemhandler function adds the new rowtexts to the beginning of the array, so we want to keep the last part of it*/
            appx_session.rowtext = appx_session.rowtext.slice(appx_session.rowtext.length - rowtext_size )
            // process items
            for (var i = 0; i < appx_session.items.length; i++) {
                var item = appx_session.items[i][0];
                var box = appxitembox(item.pos_row, item.pos_col, item.size_rows, item.size_cols);
                var $tag = appx_session.items[i][1];

                /*Items not in scroll boxes haven't been adjusted for box positioning yet.
                **Not sure where scroll box adjusting gets done for items.*/
                if (!(appxIsScroll(box) || appxIsScrollReg(box))) {
                    item.init_pos_row = item.pos_row;
                    item.init_pos_col = item.pos_col;
                    item.pos_row -= box.begin_row - 1;
                    item.pos_col -= box.begin_column - 1;
                    var topAdjPx = item.pos_row * appx_session.rowHeightPx;
                    var leftAdjPx = item.pos_col * appx_session.colWidthPx;

                    if ($tag) {
                        $tag.css({
                            "top": topAdjPx + "px",
                            "left": leftAdjPx + "px"
                        });
                    }
                } else {
                    if ($tag) {
                        var tempTop = ((box.begin_row - item.pos_row) * appx_session.rowHeightPx);
                        $tag.css({
                            "top": tempTop + "px",
                        });
                    }
                }
                if (box) {
                    box.items.push(appx_session.items[i]);
                }

            }
            appx_session.items = [];
        }

        // process widgets
        for (var i = 0; i < appx_session.widgets.length; i++) {
            var wgt = appx_session.widgets[i];
            var wx = wgt[0];
            var boxno = wgt[0].boxid;
            var boxid = boxNos[boxno];
            var box = appx_session.current_show.boxes[boxid];
            /*Widgets in scroll boxes haven't been adjusted for box positioning.
            **Non-scroll box widgets get adjusted in engine.*/
            if (!appxIsFullscreen(box) && appxIsScroll(box)) {
                var $tag = wgt[1];
                var scrollbox = box;
                var box = appxitembox((scrollbox.begin_row + wx.wPositionY - 1), (scrollbox.begin_column + wx.wPositionX - 1), wx.wSizeH, wx.wSizeW);
                var topAdj = (box.begin_row - scrollbox.begin_row) + 1;
                var leftAdj = (box.begin_column - scrollbox.begin_column) + 1;
                $tag.css({
                    "top": (parseInt($tag.css("top")) - (topAdj * appx_session.rowHeightPx)) + "px",
                    "left": (parseInt($tag.css("left")) - (leftAdj * appx_session.colWidthPx)) + "px"
                });
                if ($tag.data("row")) {
                    $tag.data("row", $tag.data("row") + (scrollbox.begin_row - 1));
                }
                if ($tag.data("col")) {
                    $tag.data("col", $tag.data("col") + (scrollbox.begin_column - 1));
                }
            }
            if (box) { box.widgets.push(appx_session.widgets[i]); }
            else { console.log("appxnestwidgets() failed to find box for widget"); }
        }
        appx_session.widgets = [];

        // process rowtext
        for (var i = 0; i < appx_session.rowtext.length; i++) {
            var beginRow = 1;
            var rowtxt = appx_session.rowtext[i];
            /*Some rowtext box adjustment gets done in the engine. If it is not
            **done in the engine then we adjust for both scroll box items and
            **non-scroll box items*/
            if (rowtxt.type == ROWTEXT_TYPE_ITEM) {
                box = appxitembox(rowtxt.pos_row, rowtxt.pos_col, rowtxt.size_rows, rowtxt.size_cols)
            } else {
                var boxNo = rowtxt.boxid; // Box number from Appx
                var boxIdx = boxNos[boxNo];
                if (boxIdx >= 0) {
                    var box = appx_session.current_show.boxes[boxIdx];
                    if (!appxIsFullscreen(box) && appxIsScroll(box)) {
                        var scrollbox = box;
                        box = appxitembox((scrollbox.begin_row + rowtxt.pos_row - 1), (scrollbox.begin_column + rowtxt.pos_col - 1), rowtxt.size_rows, rowtxt.size_cols);
                        beginRow = scrollbox.begin_row;
                    }
                }
            }
            if (!(appxIsScroll(box) || appxIsScrollReg(box))) {
                if (rowtxt.type == ROWTEXT_TYPE_ITEM) {
                    rowtxt.pos_row -= box.begin_row - 1;
                    rowtxt.pos_col -= box.begin_column - 1;
                }
            }
            else {
                rowtxt.pos_row -= box.begin_row - beginRow;
                rowtxt.pos_col -= box.begin_column - 1;
            }
            box.rowtext.push(rowtxt);
        }
        appx_session.rowtext = [];

    }
    catch (ex) {
        console.log("appxnestwidgets: " + ex);
        console.log(ex.stack);
    }
}

//Show Message Handler (CharView.updater?)
function appxshowhandler(x) {
    try {
        if (x == null || x.data == null) return;
        appx_session.current_show = (x.data);
        var show = appx_session.current_show;
        var mode = show.curraction[0];

        $("#appx_main_container *").each(function $_each() {
                var newid = $(this).attr("id") + "-stale";
                $(this).attr("id", newid);
            });

		appxCheckForIlfEditorMode(mode);	

        appxnestwidgets();

        if( appxHandleEmBox() ) {
            appx_session.buildingEm = true;
        }
        else {
            $("#appx_status_date").html("");
        }

        if( ! appx_session.buildingEm ) {
            appxshowboxes();
            appxprepscreen();
            setTimeout(function setTimeoutCallback() {
				appxSetTabIndexes();
            }, 0);
	
            setTimeout(function findnonfocusboxes() {
				// Setup then gather the non 'stale' appxbox boxes from the html then add them to a collection
				let boxes = [];
				let boxcount = 0;				
				$(".appxbox").each(function $_each() {
					if ($(this).attr('id').indexOf("stale") !== -1) {
						return;
					}
					boxcount = boxcount + 1;
					// Add to the collection the appxbox layer (z-Index) information whether its modifiable or not and the html appxbox object itself  
					boxes.push( [parseInt($(this).css("zIndex"), 10), $(this).hasClass("appx-not-modifiable") ? false:true, $(this)] );
				});
				
				if (boxcount => 1) {
					// Iterate the js boxes
					for (let i = 0; i < appx_session.current_show.boxes.length; i++) {
						let currentbox = appx_session.current_show.boxes[i];
						// Iterate html appxboxes collection to match each html box with a js box
						for (let j in boxes) {
							// Check whether this js box object matches the html appxbox object as to layer and  
							//  whether the js box object is non-modifiable and whether it contains any items  
							if ((boxes[j][0] ==  currentbox.layer) && (boxes[j][1] != true) && (currentbox.items.length > 0) ) {
								let box = boxes[j][2] 
								// Iterate the items in the box and mark each as not-modifiable and disabled
								for (let k = 0; k < box[0].children.length; k++) {
									let tag = box[0].children[k];
									if (tag != null && ((tag.classList.contains("appx-modifiable") == true) || (tag.classList.contains("appxdatefield") == true))) {
										// Mark this item as not-modifiable and disabled
										tag.classList.remove("appx-modifiable");
										tag.classList.add("appx-not-modifiable");
										tag.setAttribute("disabled", "");
										// For date pickers, its children need to be disabled			
										if (tag.classList.contains("appxdatefield") == true) { 
											for (let l = 0; l < tag.children.length; l++) {
												let child = tag.children[l];
												// Mark this child as disabled
												child.setAttribute("disabled", "");
												// Clone this child so the event handlers are removed
												child.replaceWith(child.cloneNode(true));
											}
										}
									}
								}	
								break;
							}
						}						
					}
				}
            }, 1);
	
			// When in ilf editor change mode, we want to set the focus and move the cursor to the input element
			//  that is at or is the nearest to, the right of, the current cursor position
			if (ilfEditorMode && ilfAddorChangeMode) {
				// We'll wait a bit here so appxSetTabIndexes has a chance to finish  	
				setTimeout(function () {				
					let cur = appxGetCursorPos(); 
					for (let idx = 0; idx < appx_session.current_show.boxes.length; idx++) {
						let currentbox = appx_session.current_show.boxes[idx];
						if (cur.row == currentbox.begin_row && cur.row == currentbox.end_row && 
							cur.col >= currentbox.begin_column && + cur.col <= currentbox.end_column) {
							let found = false;
							for (let j = currentbox.items.length - 1; j >= 0; j--) {
								let item = currentbox.items[j][0];
								if (item.widget.wWidgetType == WIDGET_TYPE_RAW_TEXT &&
									cur.row == item.pos_row && cur.col <= item.pos_col + item.size_cols) {
									// For now, we won't be geting any more data from the server and  
									//  we're nearly done processing the data we just recieved
									//  given that, we'll unlock now so we can set the focus
									appxSetLocked(false);
									appxPutCursor(item.pos_col, item.pos_row);
									let tag = currentbox.items[j][1];
									if (tag != null) { setInputFocus(tag); }
									found = true;
									break;
								}
							}
							if (!found) {
								// Since an element wasn't found above, the current cursor position must be set beyond the end of the statement  
								// Given that, we'll find the first input element on that line to focus and move the cursor to
								for (let j = currentbox.items.length - 1; j >= 0; j--) {
									let item = currentbox.items[j][0];
									if (item.widget.wWidgetType == WIDGET_TYPE_RAW_TEXT) {
										appxSetLocked(false);
										appxPutCursor(item.pos_col, item.pos_row);
										let tag = currentbox.items[j][1];
										if (tag != null) { setInputFocus(tag); }
										break;
									}
								}
								break;
							} else {
								break;
							}
						}
					}
					// When a scan request occurs on a constant field in ilf editor change mode, we need to assemble the options into a datalist 
					//  then associate that datalist to the input element that is the constant field  
					if (ilfScanDataReady) {
						ilfScanDataReady = false;	
						let cur = appxGetCursorPos(); 
						let inputTag = $("#" + getClientId("appxitem_" + cur.col + "_" + cur.row));
						if (inputTag.length) {
							// Create the datalist tag then add an id for the datalist tag afterwards reference that id in the input element
							let datalist = $('<datalist>');
							let datalistId = "datalist_" + inputTag.prop('id');
							datalist.attr('id', datalistId);
							inputTag.attr('list', datalistId);
							
							// Because a datalist will not show any options in the list if the input element already has a value
							//  we'll add some events to store and remove the value and then restore the value if no option is selected  
							inputTag.on('click', function() { ilfScanOrgValue = $(this).val(); $(this).val(''); });
							inputTag.on('mouseleave', function() { if ($(this).val() == '') { $(this).val(ilfScanOrgValue); }});
							
							let length = appx_session.objItems.length;
							for (let idx = 0; idx < length; idx++) {
								// The option '--Select Path--' appears to appear in every constant scan but is not needed here so will ignore it 
								if (appx_session.objItems[idx].includes('--Select Path--')) { continue; } 
								let optionTag = $('<option>').attr('value', appx_session.objItems[idx]).appendTo(datalist);
								if (appx_session.objItems[idx] == inputTag.val()) {
									optionTag.attr('selected', 'selected');
								}
							}
							// We need some extra z-index to be on top of following scrolling records
							var zIndex = 99999;
							var offset = inputTag.offset();
							offset.top += 21;
							var parent = inputTag.parent();							
							datalist.appendTo(parent)
								.css({
									'position': 'absolute',
									'z-index': zIndex,
									'min-width': inputTag.width()
									})
								.focus()
								.offset(offset);
						}
					}
				}, 5);
			}
        }

        AppxResource.sendHeld();

        screenflipped = false;

        if ((appx_session.pendingResources.length == 0 && appx_session.pendingTables === 0) || (Math.abs(show.curraction[0] & M_WAIT) == 0)) {
            appxshowscreen();
        }

		if (ilfSelecting) {
			// Highlight the selected row(s) 
			var highlightSelection = false;	
			if (ilfRespecify) {
				var pos = appxGetCursorPos();
				ilfFirstSelectedRow = ilfLastSelectedRow = pos.row + 1;
				ilfFirstRowSnapshot = JSON.stringify(appx_session.current_show.boxes[pos.row].rowtext);
				ilfRespecify = false;
				highlightSelection = true;
				ilfRelativePagePosition = ilfStartingPagePosition;
			}
			// We'll only highlight the first selected row if its visible on the screen
			// Note:  This is to account for any page key useage during selection
			if (ilfStartingPagePosition == ilfRelativePagePosition && !highlightSelection) {
				for (var idx = 0; idx < appx_session.current_show.boxes.length; idx++) {
					if (appx_session.current_show.boxes[idx].rowtext.length == 0) { continue; }
					var currentRowSnapshot = JSON.stringify(appx_session.current_show.boxes[idx].rowtext);
					var currentRowCompare = currentRowSnapshot.localeCompare(ilfFirstRowSnapshot);
					if (currentRowCompare == 0) {
						var firstRowSnapshot = JSON.stringify(appx_session.current_show.boxes[ilfFirstSelectedRow - 1].rowtext);
						var firstRowCompare = firstRowSnapshot.localeCompare(ilfFirstRowSnapshot);
						if (firstRowCompare != 0) {
							if (idx + 1 != ilfFirstSelectedRow) {
								ilfFirstSelectedRow = ilfLastSelectedRow = idx + 1;
							}
						}				
						highlightSelection = true;
						break;
					}
				}
			}
			// Regardless of whether the first selected row is visible on the screen we'll need to
			// setup to highlight the selected rows if the selection process has been completed
			// Note:  Agian, this is to account for any page key useage during selection
			if (!highlightSelection && ilfSelection) { 
				var pos = appxGetCursorPos();
				if (ilfStartingPagePosition < ilfRelativePagePosition) {
					if (ilfFirstSelectedRow != ilfLastSelectedRow) {
						ilfFirstSelectedRow = 3;
						ilfLastSelectedRow =  pos.row + 1;
					} else {
						ilfFirstSelectedRow = ilfLastSelectedRow =  pos.row + 1;
					}
				} else {
					ilfFirstSelectedRow = pos.row + 1;
					ilfLastSelectedRow = appx_session.current_show.boxes.length - 1; 
				}
				highlightSelection = true;
			}
			if (highlightSelection) { 	 
				// Setup the css for the actual Highlighting 
				var selectedcount = (ilfLastSelectedRow >= ilfFirstSelectedRow) ? (ilfLastSelectedRow - ilfFirstSelectedRow) : (ilfFirstSelectedRow - ilfLastSelectedRow);
				var startrow = (ilfLastSelectedRow >= ilfFirstSelectedRow) ? ilfFirstSelectedRow : ilfLastSelectedRow;
				for (var count = 0; count <= selectedcount; count++) {
					var rowid = "#box_"+ (startrow + count);
					$(rowid).css({"background-color": ilfTextColors.get(("pink"))}); 			
				}
			}
		}

        if (Math.abs(show.curraction[0] & M_SAVE) != 0) {
            appx_session.dirtySinceSave = false;
        }

        if ((Math.abs(show.curraction[0] & M_WAIT) == 0) &&
            (appx_session.pendingResources.length == 0)) {
            sendappxshow(OPT_NULL, []);
        }
        else {
            $(document).tooltip();
            blocked = false;
            try {
                $("#main").unblock();
            }
            catch (e) { }
            appxstarttimeout();
            if (appx_session.pendingResources.length == 0 && appx_session.pendingTables === 0)
                appxSetStatusStateText(APPX_STATE_READY);
            else {
                appxSetStatusStateText(APPX_STATE_IMAGES);
            }
        }
        if (appx_session.processhelp == true) {
            appx_session.processhelp = false;
            setTimeout(function setTimeoutCallback() {
                    appxwidgetcallback(appx_session.processhelpoption);
                    appx_session.processhelpoption = null;
                }, 0);
        }

        if( appx_session.buildingEm && ( $("#appx_status_date").text().length === 0 || ! $("#appx_status_date") )) {
            appx_session.buildingEm = false;
            appx_session.pendingTables = 0;
        }
    }
    catch (ex) {
        console.log("appxshowhandler: " + ex);
        console.log(ex.stack);
    }
}

function appxshowrowtextforbox(box) {
    var count = 0;
    var equalRow = 0;
    var headingOne = 0;
    var headingTwo = 0;
    var zIndexModifier = 25; //default wLayer for RowText based on Widget class
	var colorvalue = "inherit";
	var isRowCommented = false;
	var isVerbLabel = false;
	var isVerbReturn = false;
	var ilfChangeMode = (ilfEditorMode && (box.rowtext.length != box.items.length)) ? true : false;
	var isAuditBox = (box.isAuditBox !== undefined) ? true : false; 
	// When ILF editiing, we need to identify the 'Database Management' box so we don't later set the rowtext colorvalue to "inherit"
	var isDatabaseManagementBox = (ilfEditorMode && box.begin_row == 5 && box.end_row == 19 && box.rowtext.length == 8) ? true : false;
	// When ILF editiing, we need to identify the 'Process Data Dictionary' box so we don't later process it for the ILF inquiry mode or set the rowtext colorvalue to "inherit"
	var isProcessingDataDictionaryBox = (ilfEditorMode && box.begin_row == 8 && box.end_row == 18) ? true : false;
	
	// When we're in the Ilf editor add or change mode, we need to generate the missing row text and assign colors to the various text input fields 
	if (ilfChangeMode) {
		appxIlfEditorChangeModeAddRowtext(box, colorvalue);
	}		
	
    for (var idx = 0; idx < box.rowtext.length; idx++) {
        var rowtxt = box.rowtext[idx];
        var boxNo = box.widget.wBoxNumber;
		
		// When we're in the Ilf editor inquire mode, we need to generate the missing row text and assign colors to the various fields 
		if (ilfEditorMode && !ilfChangeMode && !isAuditBox && !isProcessingDataDictionaryBox) {
			var tuple = appxIlfEditorInquireModeAddRowtext(box, idx, rowtxt, colorvalue, isRowCommented, isVerbLabel, isVerbReturn);
			colorvalue = tuple.colorvalue;
			isRowCommented = tuple.isRowCommented;
			isVerbLabel = tuple.isVerbLabel;
			isVerbReturn = tuple.isVerbReturn;
		}		
		
		if (rowtxt.colorvalue !== undefined) {
			colorvalue = rowtxt.colorvalue;
		}				

        if (rowtxt.pos_row == 1 && rowtxt.string.trim() == "Standard Toolbars")
            continue;
        if (rowtxt.string && rowtxt.string.length > 1 && rowtxt.string[0] == '=' && box.scrollrow && box.scrollrow == rowtxt.pos_row + 1)
            continue;
        if (rowtxt.string && rowtxt.string.length > 1 && (idx + 2 <= box.rowtext.length) &&
			((box.rowtext[idx + 1] !== undefined && box.rowtext[idx + 1].string[0] == '=' && box.scrollrow == box.rowtext[idx + 1].pos_row + 1) ||
			(box.rowtext[idx + 2] !== undefined && box.rowtext[idx + 2].string[0] == '=' && box.scrollrow == box.rowtext[idx + 2].pos_row + 1))) {
			rowtxt.pos_row++;
		}
        if (rowtxt.pos_row > 0) {
            if (rowtxt.pos_row == 1 && box.widget && box.widget.wLabel && box.widget.wLabel.trim() == rowtxt.string.trim()) {
                continue;
            }
            count++;
            var x = 0,
                y = 0,
                w = 0,
                h = 0;
            var colW = appx_session.colWidthPx;
            var rowH = appx_session.rowHeightPx;
            if (box && appxIsScrollReg(box)) {
                x = 1;
                y = 1;
            }
            x = (rowtxt.pos_col - x) * colW;
            y = (rowtxt.pos_row - y) * rowH;
            w = rowtxt.size_cols * colW + 5; //added 5 so IE would fit text in box
            h = rowtxt.size_rows * rowH;

            var $tag = $("<div>");
            if (rowtxt.size_rows > 1)
                if (rowtxt.wordWrap) {
                    $tag.addClass("rowtextwrap");
                } else {
                    $tag.addClass("appx-alpha-field");
                }

            else
                $tag.addClass("rowtext");

            if (rowtxt.type == ROWTEXT_TYPE_ITEM) {
                $tag.addClass("rowtextitem");
            }

            if (rowtxt.uline) {
                $tag.addClass("appx-uline");
            }

            if (rowtxt.string.length > 1 && rowtxt.string[0] == '=')
                $tag.addClass("ColHdgSep");
            $tag.attr("id", "rowtext_" + count.toString() + "_" + box.begin_row.toString());
			if (!ilfEditorMode) {
				$tag.html(rowtxt.string.replace(/[\xb6\x0d\x0a]/g, " "));
			} else {
				$tag.html(rowtxt.string.replace(/[\x0d\x0a]/g, " "));
			}

            $tag.css({
                "margin": "0",
                "padding": "0"
            });

			if (ilfEditorMode && !isDatabaseManagementBox && !isProcessingDataDictionaryBox) { $tag.css({"color": colorvalue}); }
            $tag.css({
                "position": "absolute",
                "left": x,
                "top": y,
                "height": h,
                "width": w,
                "z-index": box.layer + (zIndexModifier * -1)
            });

            for (var i = 0; i < box.items.length; i++) {
                var item = box.items[i];
                if (item.length === 4 && item[3] === rowtxt) {
                    box.items[i][1] = $tag;
                    break;
                }
            }
            if (box != null) {
                var $box = $("#screenBuf #box_" + boxNo);
                if ($box.length) $box.append($tag);
            }
        }
    }
}

function appxshowitemsforbox(box, isActiveBox, boxHtml) {
    //sort array of boxes and items for tabindex

    for (var i = box.items.length - 1; i >= 0; i--) {
        var item = box.items[i][0];
        var itemNext = null;
        if (i > 0) {
            itemNext = box.items[i - 1][0];
        }
        if (box.items[i][1] == null) {
            continue;
        }
        var boxtop = 0;
        if (!appxIsScrollReg(box)) {
            boxtop = box.begin_row;
        }

        var $tag = box.items[i][1];
        var $box = $_screenBuf;

		if (isActiveBox || (ilfEditorMode && ilfAddorChangeMode)) { appxSetTabElement(item, $tag); }

        if (appxIsModifiableCapable(item) && appxIsScrollReg(box))
            $tag.removeClass("appx-not-modifiable");
        var boxno = box.widget.wBoxNumber;
        appxwidgetdimensions(item, $tag, box);
        var $boxTemp = $("#screenBuf #box_" + boxno);
        if ($boxTemp.length) {
            $box = $boxTemp;
        } else {
            console.log("no box...");
        }

        //put a click handler on scrolling records
        if (appxIsScrollReg(box)) {
            //ignore a click on modifiable fields (inside the active record)
            if (!appxIsModifiable(item)){
                $tag.click(function tag_onclick(event) {
                    appxScrollClick($(this).parent(), event);
                });
            } 
            else{
                //if modifiable, we want to stop the click event's propegation, so we don't fire option return when we click inside of a field
                $tag.click(function tag_onclick(event) {
                    event.stopPropagation();
                });
            }
            
        }

        /*If item is a list box or was originally listbox, then we check the position of the field to the right,
        **if it overlaps then we change tag from select to input*/
        if ( ($tag.is("select") || $tag.hasClass("appx-listbox-originally")) && navigator.userAgent.indexOf("Mobile") === -1) {
            var tagWidth = parseInt($tag.css("width"));
            var tagLeft = parseInt($tag.css("left"));
            var selRight = tagLeft + tagWidth;
            var selTop = parseInt($tag.css("top"));
            var nextTag = null;
            if (i - 1 >= 0) {
                nextTag = box.items[i - 1][1];
            }
            var boxWidth = parseInt($box.css("width")) - 4;//-4 for padding of box
            var hitBoxRight = boxWidth < selRight;
            if (nextTag || hitBoxRight) {
                appxwidgetdimensions(itemNext, nextTag, box);
                var nextLeft = boxWidth;

                /*If it's hitting box edge we do not need to check the top, if it's
                **hitting another element then we check tops to see if both are on
                **the same row*/
                var topsOrBox = true;
                if (!hitBoxRight) {
                    nextLeft = parseInt(nextTag.css("left"));
                    topsOrBox = (selTop === parseInt(nextTag.css("top")));
                }
                if (topsOrBox && (selRight > nextLeft)) {
                    var padWidth = $tag.attr("data-padWidth");
                    var initWidth = tagWidth - padWidth;
                    var noPadRight = selRight - padWidth;
                    var minCol = 4 * appx_session.colWidthPx;

                    /*If tags would overlap, test whether they won't overlap if we
                    **remove padding. Copying java client in putting in a 5 column
                    **minimum size. */
                    if (noPadRight < nextLeft) {
                        /*If tags won't overlap without padding then do the math to
                        **set the tag width to 1px (another element) or 5px (edge of
                        **box) less than an overlap*/
                        var spacing = hitBoxRight ? 5 : 1;
                        var w = ((nextLeft - spacing) - tagLeft);
                        if(w > minCol){
                            $tag.width(w + "px");
                        } else {
                            //if still doesn't fit, then draw it with minimum acceptable
                            //size knowing that it overlaps the next field becaus eof design problem.
                            $tag.width(minCol.toString()+"px");
                            /*
                            var attrs = {};

                            //retrieve all the attributes of the original tag
                            $.each($tag[0].attributes, function $_each(idx, attr) {
                                attrs[attr.nodeName] = attr.nodeValue;
                            });

                            //Create new tag
                            $tag = $("<input type='text' />")

                            //Add all attributes to new tag
                            for (var attr in attrs) {
                                $tag.attr(attr, attrs[attr]);
                            }

                            //Change width to correct size for input field
                            var w = (appx_session.colWidthPx * item.size_cols + 5) + "px";
                            $tag.css({
                                "width": w
                            });
                            */
                        }
                    }
                }
            }
                }

                // Fix for bug #4441 Scroll Screen - separate lines and alignment; with adjustments to work in conjunction with
                // fix for bug #4455 Separator line is missing from an item of row text
                // When the design calls for a separator, either before or after the row text item, add the required CSS class
                var $addedSeparator = false;
                if (item.type == ROWTEXT_TYPE_ITEM) {
                        var $divTagBefore = $("<div>");
                        var $divTagAfter = $("<div>");
                        if (item.widget.wSepBefore != null &&
                                item.widget.wSepBefore === true) {
                                $addedSeparator = true;
                                $divTagBefore.addClass("sepBefore");
                                $box.append($divTagBefore);
                                $tag.removeClass("sepBefore");
                        }
                        if (item.widget.wSepAfter != null &&
                                item.widget.wSepAfter === true) {
                                $addedSeparator = true;
                                $divTagAfter.addClass("sepAfter");
                                $box.append($divTagAfter);
                                $tag.removeClass("sepAfter");
                        }

            // Retrieve all the attributes of the target tag
                        var attrs = {};
            $.each($tag[0].attributes, function $_each(idx, attr) {
                attrs[attr.nodeName] = attr.nodeValue;
            });

            //Loop through and find the 'style' attribute
            for (var attr in attrs) {
                                if (attr == "style") {
                                        // Copy the 'style' attribute from the target tag to the new <div>(s)
                                        // then adjust the new tag's position and prominence
                            var zIndexModifier = 10;
                            if (item.widget.wLayer !== null || item.widget.wLayer !== undefined) {
                                zIndexModifier = item.widget.wLayer;
                        }
                                        if (item.widget.wSepBefore === true) {
                                                $divTagBefore.attr(attr, attrs[attr]);
                                                $divTagBefore.css({"height": parseInt(boxHtml.css("height")) + "px" });
                                                $divTagBefore.css({"left": (parseInt($tag.css("left")) - 4) + "px" });
                                                $divTagBefore.css({"width":  1 + "px" });
                                                $divTagBefore.css({"top":  0 + "px" });
                                    $divTagBefore.css({"z-index": (box.layer +  (zIndexModifier * -1))});
                                        }
                                        if (item.widget.wSepAfter === true) {
                                                $divTagAfter.attr(attr, attrs[attr]);
                                                $divTagAfter.css({"height": parseInt(boxHtml.css("height")) + "px" });
                                                $divTagAfter.css({"left": (parseInt($tag.css("left")) + parseInt($tag.css("width")) - 2) + "px" });
                                                $divTagAfter.css({"width":  1 + "px" });
                                                $divTagAfter.css({"top":  0 + "px" });
                                    $divTagAfter.css({"z-index": (box.layer +  (zIndexModifier * -1))});
                                        }
                                        break;
                                }
                        }
                }

        if (box.items[i].length < 4) {
                        // Fix for bug #4441 Scroll Screen - separate lines and alignment; with adjustments to work in conjunction with
                        // fix for bug #4442 Scroll Screen - separator line for checkbox
                        if ((item.widget !== undefined && item.widget != null) &&
                                 (item.widget.wWidgetType !== undefined &&
                                  item.widget.wWidgetType != null) &&
                                 (item.widget.wWidgetType == WIDGET_TYPE_CHECK_BOX ||
                                  item.widget.wWidgetType == WIDGET_TYPE_NONE ||
                                  item.widget.wWidgetType == WIDGET_TYPE_RAW_TEXT ||
                                  item.widget.wWidgetType == WIDGET_TYPE_LABEL) &&
                                 (item.widget.wSepBefore === true ||
                                  item.widget.wSepAfter === true) &&
                                  $addedSeparator == false) {

                                var $divTagBefore = $("<div>");
                                var $divTagAfter = $("<div>");
                                if (item.widget.wSepBefore === true) {
                                $divTagBefore.addClass("sepBefore");
                                        $tag.removeClass("sepBefore");
                                        $box.append($divTagBefore);
                                }
                                if (item.widget.wSepAfter === true) {
                                        $divTagAfter.addClass("sepAfter");
                                        $tag.removeClass("sepAfter");
                                        $box.append($divTagAfter);
                                }

                                $box.append($tag);

                // Retrieve all the attributes of the target tag
                                var attrs = {};
                $.each($tag[0].attributes, function $_each(idx, attr) {
                    attrs[attr.nodeName] = attr.nodeValue;
                });

                //Loop through and find the 'style' attribute
                for (var attr in attrs) {
                                        if (attr == "style") {
                                                // Copy the 'style' attribute from the target tag to the new <div>(s)
                                                // then adjust the new tag's position and prominence
                                    var zIndexModifier = 10;
                            if (item.widget.wLayer !== null || item.widget.wLayer !== undefined) {
                                        zIndexModifier = item.widget.wLayer;
                                }
                                                if (item.widget.wSepBefore === true) {
                                                        $divTagBefore.attr(attr, attrs[attr]);
                                                        $divTagBefore.css({"height": parseInt(boxHtml.css("height")) + "px" });
                                                        $divTagBefore.css({"left": (parseInt($tag.css("left")) - 4) + "px" });
                                                        $divTagBefore.css({"width":  1 + "px" });
                                                        $divTagBefore.css({"top":  0 + "px" });
                                            $divTagBefore.css("z-index", (box.layer +  (zIndexModifier * -1)));
                                                }
                                                if (item.widget.wSepAfter === true)  {
                                                        $divTagAfter.attr(attr, attrs[attr]);
                                                        $divTagAfter.css({"height": parseInt(boxHtml.css("height")) + "px" });
                                                        $divTagAfter.css({"left": (parseInt($tag.css("left")) + parseInt($tag.css("width")) - 2) + "px" });
                                                        $divTagAfter.css({"width":  1 + "px" });
                                                        $divTagAfter.css({"top":  0 + "px" });
                                        $divTagAfter.css({"z-index": (box.layer +  (zIndexModifier * -1))});
                                                }
                                                break;
                                        }
                                }
                        }
                        else {
                    $box.append($tag);
                        }

            var zIndexModifier = 10;
            if (item.widget.wLayer !== null || item.widget.wLayer !== undefined) {
                zIndexModifier = item.widget.wLayer;
            }
			
			// Bug #4969 - So, the textarea widget is above other items when resized, we'll change its z-index to be 1 > then the other items z-index
			if (item.widget.wWidgetType == WIDGET_TYPE_TEXT_AREA) {
				zIndexModifier--;
			}
			
            $tag.css("z-index", (box.layer +  (zIndexModifier * -1)));
        }
        if (appxIsScannable(item)) {
            var cel = appxGetCellSize();
            var pad = 1;
            var itemLeft = item.pos_col * cel.w;
            var tagLeft = parseInt($tag.css("left"));
            var item2TagPx = itemLeft - tagLeft;
            var scanLeft = tagLeft + parseInt($tag.css("width")) + pad;
            var scanRight = scanLeft + 9;
            var scanTop = parseInt($tag.css("top")) + 4;
            var addScan = true;


            if (itemNext) {
                var nextLeft = ((itemNext.pos_col * cel.w) - item2TagPx);
                var nextTop = (item.pos_row * cel.h);
                if (item.pos_row == itemNext.pos_row) {
                    if (scanLeft >= nextLeft || scanRight >= nextLeft) {
                        addScan = false;
                    }
                }
            }
            if (addScan) {
                var col = item.widget.wPositionX;
                var row = item.widget.wPositionY;
                if (col === null) {
                    col = $tag.data().col;
                    row = $tag.data().row;
                }
                $("<img>")
                    .attr("src", "" + appxClientRoot + "/images/scanicon.png")
                    .data({
                        "col": col,
                        "row": row
                    })
                    .css({
                        "left": scanLeft,
                        "position": "absolute",
                        "top": scanTop,
                        "z-index": $tag.css("z-index")
                    })
                    .mousedown(function $_mousedown() {
                        appx_session.scan = true;
                        appxPutCursor($(this).data().col, $(this).data().row);
                                                appxSnapshotScanCursor();
                        appxwidgetcallback(OPT_SCAN);
                    })
                    .appendTo($box);
            }
        }
        
        /*make the items disabled if we are in image editor*/
        if(box.widget.wWidgetType == WIDGET_TYPE_IMG_EDITOR_FRAME){
            /*make the widgets appropriate for Image Editor*/
            appxImageEditorStyle(item.widget, $tag);
        }
    }
}

//double buffering
var $_screenBuf = null; //appx_session?
function appxshowscreen() {
    if (screenflipped)
        return;
    screenflipped = true;

    if( ! appx_session.buildingEm ) {
        var $screen = $("#appx_main_container");
        $screen.attr("id", "screenBuf");

        $_screenBuf.attr("id", "appx_main_container");
        if ($_screenBuf.enhanceWithin)
            $_screenBuf.enhanceWithin();
        $_screenBuf.css("visibility","visible"); //show();

        $screen.css("visibility","hidden"); //hide();
        $screen.empty();

        $_screenBuf = $screen;

        appxshowcursor(true);
        callValidateText();
    }

    setTimeout(function setTimeoutCallback() {
        appxSetLocked(false);
    }, 0);
}

function appxprepscreen() { //flip
    createDropDownMenu();
    createPopupMenu();
    createToolbarMenu();
    applymessages();
    $(".temp").each(function destroyTemp() {
        this.remove();
    });

    //Using context menu library to add popup menus to screen
    $.contextMenu('destroy');
    $("*").unbind("contextmenu");


    if (!($.isEmptyObject(appx_session.currentmenuitems.popupItems))) {
        for (var key in appx_session.currentmenuitems.popupItems) {
            if (appx_session.currentmenuitems.popupItems[key].icon !== undefined) {
                var icon = appx_session.currentmenuitems.popupItems[key].icon;
                var url = "";
                //check if the url already exists in cache
                if(appx_session.image_cache[icon].url && appx_session.image_cache[icon].url != ""){
                    url = appx_session.image_cache[icon].url;
                }
                //if not build the url for it while we are waiting for it to load
                else{
                    var iconUrl = appx_session.image_cache[icon].wIcon.substring(1, appx_session.image_cache[icon].wIcon.lastIndexOf(",")).replace(",", ".");
                    //if appx engine is 64bit, we need to change the 3rd part to be 16 characters if it is 8 or less than 8 characters
                    //pad it to be 16 if we are on x64 engine
                    if(appx_session._APPX64){
                        var iconUrlSplit = iconUrl.split('.');
                        if(iconUrlSplit[2].length <= 8){
                            var needBytes = 16 - iconUrlSplit[2].length;
                            while(needBytes > 0 ){
                                iconUrlSplit[2] += "_";
                                needBytes--;
                            }
                            iconUrl = iconUrlSplit.join('.');
                        }
                    }
                
                    url = appx_session.appxResourceUrl + iconUrl;
                }
                var style = $("<style>");
                style.attr("type", "text/css");
                style.html(".context-menu-icon-" + icon + " { background-image: url(" + url + "); background-repeat: no-repeat; background-size: contain;}");
                style.addClass("temp");
                style.appendTo($("head")[0]);
            }
        }
        $.contextMenu({
            selector: ".context-selector",
            zIndex: 20000,
            items: appx_session.currentmenuitems.popupItems
        });
        $(":input").contextmenu(false);
    }
    /*init imageEditor Popup menu if we are in appx image editor*/
    if($(".appx-active-box.appxImageEditor").length >= 1 ){
        $.contextMenu({
            selector: ".appx-active-box.appxImageEditor, .appx_ie_widget, .appx_ie_rowtext",
            zIndex: 20000,
            items: appx_session.imageEditor_popupMenuItems,
            events:{
                preShow: function(option, event){
                    return imageEditor_popupMenu_preshow_event(option, event);
                }
            }
        });
    }
    applystyles();
    if (appx_session.token_cache) {
        for (var i = 0; i < appx_session.token_cache.keys.length; i++) {
            var key = appx_session.token_cache.keys[i];
            if (key != "") {
                if (!appxtokengetitem(key)) {
                    continue;
                } else {
                    applyToken(key);
                }
            }
        }
    }

    if (appx_session.getProp("showScrollbar") == true) {
        var vcrcount = 0;
		var adjustH = !ilfEditorMode ? appx_session.rowHeightPx - 5 : appx_session.rowHeightPx - 9;
        $.each($("#screenBuf .appx-scroll"), function $_each(i, el) {
            vcrcount++;
			if (ilfEditorMode) { $(el).css("border", "2px solid #c0c0c0"); }
            var zi = $(el).css("z-index");
            var $grouphtml = $("<div id='appx-group-vcr-" + vcrcount + "'>").addClass("appx-vcr-group");
            var $vcrhtml = $("<div id='appx-scroll-vcr-" + vcrcount + "'>").addClass("appx-vcr").css({
                "left": (parseInt($(el).css("width")) - (!ilfEditorMode ? 16 : 19)) + "px",
                "height": (parseInt($(el).css("height")) + adjustH) + "px"
            });

/* */
            if (appx_session.getProp("dockingScrollbar") == true) {  /*  ##DELETEUSERPREFS##   */
                $grouphtml.addClass("appx-vcr-hide");
                $vcrhtml.hover(function $_hover_enter() {
                    // mouse enter
                    $("#appx-group-vcr-" + vcrcount).removeClass("appx-vcr-hide");
                }, function $_hover_exit() {
                    // mouse exit
                    $("#appx-group-vcr-" + vcrcount).addClass("appx-vcr-hide");
                });
            }
/* */

            var extra = (Math.max(parseInt($(el).css("height")) - (19 * 6), 19) - 0) + adjustH;
            $grouphtml.html("");
            $grouphtml.append($("<button type='button'>").addClass("appx-vcr-btn").addClass("appx-vcr-up3").click(function $_click(event) {
                event.preventDefault();
                appxwidgetcallback(OPT_SCROLL_FIRST);
                return false;
            }));
            $grouphtml.append($("<button type='button'>").addClass("appx-vcr-btn").addClass("appx-vcr-up2").click(function $_click(event) {
                event.preventDefault();
                appxwidgetcallback(OPT_SCROLL_PREV);
                return false;
            }));
            $grouphtml.append($("<button type='button'>").addClass("appx-vcr-btn").addClass("appx-vcr-up1").click(function $_click(event) {
                event.preventDefault();
                appxwidgetcallback(OPT_SCROLL_DOWN);
                return false;
            }));
            $grouphtml.append($("<button type='button'>").addClass("appx-vcr-btn").addClass("appx-vcr-scan").css({
                "height": extra + "px"
            }).click(function $_click(event) {
                event.preventDefault();
                appxwidgetcallback(OPT_KEY_ENTRY);
                return false;
            }));
            $grouphtml.append($("<button type='button'>").addClass("appx-vcr-btn").addClass("appx-vcr-down1").click(function $_click(event) {
                event.preventDefault();
                appxwidgetcallback(OPT_SCROLL_UP);
                return false;
            }));
            $grouphtml.append($("<button type='button'>").addClass("appx-vcr-btn").addClass("appx-vcr-down2").click(function $_click(event) {
                event.preventDefault();
                appxwidgetcallback(OPT_SCROLL_NXT);
                return false;
            }));
            $grouphtml.append($("<button type='button'>").addClass("appx-vcr-btn").addClass("appx-vcr-down3").click(function $_click(event) {
                event.preventDefault();
                appxwidgetcallback(OPT_SCROLL_LAST);
                return false;
            }));

            $vcrhtml.html($grouphtml);
            $(this).append($vcrhtml);
        });
    }
}

function clearBoxRowText($mTag, $box) {
    var boxLeft = parseInt($mTag.css("left").substring(0, $mTag.css("left").length - 2));
    var boxRight = parseInt($mTag.css("width").substring(0, $mTag.css("width").length - 2)) + boxLeft;
    var boxTop = parseInt($mTag.css("top").substring(0, $mTag.css("top").length - 2));
    var boxBottom = parseInt($mTag.css("height").substring(0, $mTag.css("height").length - 2)) + boxTop;

    /*Cycle through rowtext items on screen to see if they have a placement conflict
    **with widget*/
    $box.find(".rowtext").not(".wait").each(function rowtext_each() {
        var rowtextLeft = parseInt($(this).css("left").substring(0, $(this).css("left").length - 2));
        var rowtextRight = parseInt($(this).css("width").substring(0, $(this).css("width").length - 2)) + rowtextLeft;
        var rowtextTop = parseInt($(this).css("top").substring(0, $(this).css("top").length - 2));
        var rowtextBottom = parseInt($(this).css("height").substring(0, $(this).css("height").length - 2)) + rowtextTop;

        if ((rowtextLeft < boxRight && rowtextRight > boxLeft) &&
            (rowtextTop < boxBottom && rowtextBottom > boxTop)) {
                //instead of deleting, resize it*/ is still has some issues but better than removing it
                //$(this).remove();
                if(rowtextLeft < boxLeft){
                    //Shrink rowtext to stop when it reaches the widget
                    $(this).css("width", boxLeft - rowtextLeft);
                }
                /*if (rowtextRight > boxRight){
                    //split the rowtext and sho the right part it
                    //TODO:figure out how to do this!!!
                }
                if( rowtextRight <= boxRight && rowtextLeft >= boxLeft){
                    $(this).remove();
                }*/
                

                //for now remove the row text untill a solution is added to the above if
                else{
                    $(this).remove();
                }
                    
                
                
        }
    });
}

function appxshowwidgetsforbox(box, isActiveBox) {
    while (box.widgets.length > 0) {
        var wgt = box.widgets.shift(),
            $tag = wgt[1];
        var wx = wgt[0];
        var boxno = box.widget.wBoxNumber;
        if (parseInt(wx.wWidgetType) == WIDGET_TYPE_ROWTEXT && wx.wLabel[5] == '=')
            continue;

        /*If box contains rowtext and widgets, clear rowtext to remove overlaps*/
        if (parseInt(wx.wWidgetType) != WIDGET_TYPE_BOX) {
            clearBoxRowText($tag, $("#screenBuf #box_" + boxno));
        }

        $("#screenBuf #box_" + boxno).append($tag);
        if (wgt[0].wMovable && box.widget.wWidgetType != WIDGET_TYPE_IMG_EDITOR_FRAME) {
            $tag.data({
                "wMovableCommand": wgt[0].wMovableCommand
            })
            $tag.draggable({
                cancel: false,
                stop: function $_draggable_stop() {
                    var r = Math.floor(($(this).offset().top - $("#appx_main_container").offset().top) / appx_session.rowHeightPx);
                    var c = Math.floor(($(this).offset().left - $("#appx_main_container").offset().left) / appx_session.colWidthPx);
                    appxPutCursor(c, r);

                    if ($(this).data("wMovableCommand")) {
                        appxwidgetcallback($(this).data("wMovableCommand"));
                    }
                }
            });
        }

        if (isActiveBox && parseInt(wgt[0].wWidgetType) == WIDGET_TYPE_BUTTON) {
            appxSetTabElement(wx, $tag);
        } else {
            //chrome makes fieldsets selectable when they have a tabindex
            if (!$tag.is("fieldset"))
                $tag.attr("tabindex", -1);


            if (parseInt(wgt[0].wWidgetType) == WIDGET_TYPE_BUTTON && !$tag.parent().hasClass("appx-scroll-act")) {
                $tag.prop("disabled", true);
                // Bug #4437. Disabled widgets should gray their buttons icon
                if( $( $tag[0].firstChild ).hasClass('appx-icon-trailing-text') ) {
                    $( $tag[0].firstChild ).addClass('appx-icon-trailing-text-disabled')
                }
                $tag.css({
                    "color": ""
                });
            }
            $tag.removeClass("default"); //can be a button on an underlying frame
        }

        if(box.widget.wWidgetType == WIDGET_TYPE_IMG_EDITOR_FRAME){
            /*make the widgets appropriate for Image Editor*/
            appxImageEditorStyle(wx, $tag);
        }

    }
}

/*
**Function recieves item and tag and creates tab ordered array of objects
**so that we can set correct tabindex order on the html elements when
**adding them to the screen
**
**@param wori: widget or item to process
**@param $tag: element for widget or item that is being processed
**
*/
function appxSetTabElement(wori, $tag) {
    try {
        if (wori && $tag) {
            var saveTabElement = function saveTabElement(wdgt) {
                if (!wdgt || wdgt.wEnabled != false) {
                    if (!appx_session.tablist) {
                        appx_session.tablist = [];
                    }
                    var tab = appx_session.tablist;
                    var lvl = (wdgt ? parseInt(wdgt.wTabGroup) : 0);
                    var grp = "grp-" + (wdgt ? wdgt.wTabSubGroupId || '0' : '0');
                    //if level is not a number then we assign it 0 as default
                    if (isNaN(lvl)) {
                        lvl = 0;
                    }
                    /*if item is in default group and a button then we set
                    **its group differently to allow for different tab order*/

                    if (grp == "grp-0" && wdgt.wWidgetType == WIDGET_TYPE_BUTTON) {
                        grp = "grp-dfltButtons";
                    }
                    var obj = {
                        'grp': grp,
                        'tag': $tag
                    };

                    tab[lvl] = tab[lvl] || [];
                    if (!tab[lvl][grp]) {
                        tab[lvl][grp] = [];
                    }
                    tab[lvl][grp].push(obj);
                }
            };

            if (wori.hasOwnProperty("wWidgetType")) { //button widget
                if (parseInt(wori.wWidgetType) == WIDGET_TYPE_BUTTON) {
                    saveTabElement(wori);
                }
            } else if (wori.hasOwnProperty("widget") && wori.widget) { //modifiable item
                if (appxIsModifiable(wori)) {
                    if (!$tag.prop("disabled")) {
                        saveTabElement(wori.widget);
                    }
                }
            }
        }
    }
    catch (ex) {
        console.log("appxSetTabElement: " + ex);
        console.log(ex.stack);
    }
}

// called from tag keyhandlers attached in appxSetTabIndexes
function appxSetTabFocus($tagSrc, bBack, mAutoTab) {
    try {
		var divisor = (ilfEditorMode && ilfAddorChangeMode) ? Math.ceil(appx_session.tablist.length / 100) * 100 : 100; 
        var ti = (parseInt($tagSrc.attr("tabindex")) % divisor);
        var tl = appx_session.tablist;

        var el = tl[ti];
        if (el.hasClass("appxdatefield") && el.find(".appxdatevalue"))
            el = el.find(".appxdatevalue");

        if (bBack)
            el.blur();

        ti = (bBack ? --ti : ++ti);

        if (ti < 0)
            ti = tl.length - 1;
        else if (ti >= tl.length)
            ti = 0;

        el = tl[ti];

        /*If we got here because of autotab and are tabbing to a button,
        **then we need to autotab to the default button instead of the next
        **button on screen.*/
        if (el.is("button") && mAutoTab && ($("button.default").length > 0)) {
            el = $("button.default");
        } else if (el.is("button") && mAutoTab) {
            return;
        }
        setInputFocus(el);

    }
    catch (ex) {
        console.log("appxSetTabFocus: " + ex);
        console.log(ex.stack);
    }
}

// called from appxshowhandler, sort and apply tabable buttons and items
function appxSetTabIndexes() {
    try {

        if (appx_session.tablist) {
            var tablist = [];

            var pushTabElement = function pushTabElement($tag, tabGrp) {
                /*If tag is set to display:none then we do not assign a tab index to
                **it, otherwise the tabbing will stop if the index is set and the
                **element has been set not to display*/
                if ($tag.css("display") !== "none") {
                    tablist.push($tag);
                    $tag.attr("tabindex", (tabGrp + (tablist.length - 1)));
                    $tag.on("keydown", function $_onKeydown(ke) {
                        var self = this;
                        if (ke.which == appx_session.getProp("mapTabKey")) { //tab
                            appxSetTabFocus($(self), ke.shiftKey);
                        }
                        else if (ke.which == 13 && $(self).is("button")) { //enter
                            $(self).trigger("click");
                        }
                        else { //cancel input when communicating (e.g. key pause)
                            if (!appxIsLocked()) return true;
                        }
                        ke.preventDefault();
                        ke.stopPropagation();
                        return false;
                    });
                    if (!$tag.is("button")) {
                        // Implements Auto Tab-Out
                        // modifier and non-printing keys such as Shift, Esc, Del
                        // trigger keydown events and not keypress events
                        // using keyup however, because keypress sometimes doesn't
                        // overwrite chars after a select
                        $tag.on("keyup", function $_onKeyup(ke) {
                            try {

                                if (!ke.altKey && !ke.ctrlKey && !ke.metaKey) {
                                    var k = ke.which;
                                    //http://www.cambiaresearch.com/articles/15/javascript-char-codes-key-codes
                                    //skip nonprintable keycodes
                                    if (k > 46 && (k < 91 || k > 93) && (k < 112 || k > 125)) {
                                        /*Input fields should use selectionstart to check
                                        **for cursor position and not tab when at last
                                        **position in a field. But only certain fields
                                        **use the selectionstart property, so we need to
                                        **check and make sure it has property before
                                        **trying to use it for the autotab option*/
                                        var max = $(this).attr("maxlength");
                                        var autoTab = appx_session.getProp("autoTabOut");
                                        var thisTag = getInputElement(this);

                                        /*If field doesn't have max length to check or if
                                        **autotab is false then there is no reason to
                                        **check*/
                                        if (max && autoTab) {
                                            var tab = false;
                                            var cursorPosition = getCursorPosition($(thisTag));
                                            if (cursorPosition > -1) {
                                                if (max <= cursorPosition) {
                                                    tab = true;
                                                }
                                            } else {
                                                if( $(thisTag).hasClass('appxdatevalue') ) {
                                                    if (max <= $(thisTag).val().replace(/_/g,' ').trim().length) {
                                                    tab = true;
                                                }
                                            }
                                                else {
                                                    if (max <= $(thisTag).val().trim().length) {
                                                    tab = true;
                                                }
                                            }
                                            }
                                            if (tab) {
                                                //select next item
                                                appxSetTabFocus($(this), false, true);
                                            }
                                        }
                                    }
                                }
                            }
                            catch (ex) {
                                alert(appx_session.language.alerts.keypressError + ex);
                                console.log(ex.stack);
                            }
                            return true; //bubble
                        });
                    }
                }
            }; //pushTabElement

            var sortTabElements = function sortTabElements(objA, objB) {

				// For the Ilf editor change mode we don’t want to sort the tabs
				if (ilfEditorMode && ilfAddorChangeMode) { return 0; }  

                var posA = objA.tag.position();
                var posB = objB.tag.position();
                posA.top = parseInt(objA.tag.css("top"));
                posA.left = parseInt(objA.tag.css("left"));
                posB.top = parseInt(objB.tag.css("top"));
                posB.left = parseInt(objB.tag.css("left"));

                if (posA.top < posB.top) {
                    return -1; // lower 'row', put before
                }
                else if (posA.top > posB.top) {
                    return 1; // higher 'row', put after
                }
                if (posA.left < posB.left) { //same top, check left
                    return -1;
                }
                else if (posA.left > posB.left) {
                    return 1;
                }
                return 0; //same top and left
            };

            var tab = appx_session.tablist;
            var tabGroup = [];
            var tabGroupNumList = [];
            for (var lvl = 0; lvl < tab.length; lvl++) {
                var grpCnt = 0;
                if (tab[lvl]) {
                    for (var keys in tab[lvl]) {
                        tab[lvl][keys].sort(function tab_sortArrayOverride(objA, objB) {
                            // items go in front of buttons inside a tab level
                            // tab groups don't care about element types
                            if (!objA.tag.is("button") && objB.tag.is("button")) {
                                return 1;
                            }
                            if (objA.tag.is("button") && !objB.tag.is("button")) {
                                return -1;
                            }
                            // both element types are equal, check positions
                            return sortTabElements(objA, objB);
                        });

                        tab[lvl][keys].sort(sortTabElements);
                        tabGroup[grpCnt++] = tab[lvl][keys][0];
                    }
                    /*Split groups into separate groups if another group is supposed
                    **to be in the middle of that group*/
                    for (var keys in tab[lvl]) {
                        for (var KEYS in tab[lvl]) {
                            if (keys !== KEYS && KEYS !== "grp-dfltButtons") {
// Bug #4445, Input Tab sequencing not reacting the same (as Java client)
// Previously: split if 1. the first of this group is after the first of the parent group, AND
//                      2. the last of this group is before the last of the parent group
//                              if ((sortTabElements(tab[lvl][keys][0], tab[lvl][KEYS][0]) > 0) &&
//                                  (sortTabElements(tab[lvl][keys][(tab[lvl][keys].length - 1)], tab[lvl][KEYS][(tab[lvl][KEYS].length - 1)]) < 0)) {
// Now, split if 1. the first of this group is after the first of the parent group, AND
//               2. the parent group is either default group (0) or some split thereof, AND
//               3. the first of this group is before the last of the parent group
//               *. the interrupting tab group cannot be the grp-dfltButtons group
                                if ((sortTabElements(tab[lvl][keys][0], tab[lvl][KEYS][0]) > 0) &&
                                    ( KEYS == 'grp-0' || KEYS.includes( 'split' ) ) &&
                                    ( keys !== 'grp-dfltButtons' ) &&
                                    (sortTabElements(tab[lvl][keys][0], tab[lvl][KEYS][(tab[lvl][KEYS].length - 1)]) < 0)) {

                                    var arrayLength = tab[lvl][KEYS].length;
                                    var arrayCount = 0;
                                    var splitKey = KEYS + "-split"
// We know we're going to split the parent - where in the parent group are we interrupting?
                                    for (var i = 0; i < tab[lvl][KEYS].length; i++) {
                                        if ((sortTabElements(tab[lvl][keys][0], tab[lvl][KEYS][i]) < 0)) {
                                            arrayCount = i;
                                            break;
                                        }
                                    }
// Create new sub-group 'splitKey', populate with all KEYS widgets from point of interception forward, with same widgets removed from original group
                                    tab[lvl][splitKey] = tab[lvl][KEYS].splice(arrayCount, (arrayLength - arrayCount));
                                    for (var i = 0; i < tab[lvl][splitKey].length; i++) {
                                        tab[lvl][splitKey][i].grp = splitKey;
                                    }
// add this new split sub-group to table of tab groups
                                    tabGroup[tabGroup.length] = tab[lvl][splitKey][0];
                                }
                            }
                        }
                    }

                    tabGroup.sort(sortTabElements);
                    /*Modify tab objects with a tab group order, default buttons
                    **are always last*/
                    for (var keys in tab[lvl]) {
                        var dfltButtonsAfter = [];
                        var dfltButtonsBefore = [];
                        if ((keys === "grp-dfltButtons") && (tabGroup.length > 1)) {
                            var comparison = tabGroup[0];
                            if (comparison.grp === "grp-dfltButtons") {
                                comparison = tabGroup[1];
                            }
                            for (var i = 0; i < tab[lvl][keys].length; i++) {
                                if ((sortTabElements(comparison, tab[lvl][keys][i])) < 1) {
                                    dfltButtonsAfter.push(tab[lvl][keys][i]);
                                } else {
                                    dfltButtonsBefore.push(tab[lvl][keys][i]);
                                }
                            }

                            tab[lvl][keys] = dfltButtonsAfter.concat(dfltButtonsBefore);
                            tab[lvl][keys].tabGrp = 2000;
                            tabGroupNumList.push(tab[lvl][keys].tabGrp);

                        } else {

                            for (var i = 0; i < tabGroup.length; i++) {
                                if (tabGroup[i] == tab[lvl][keys][0]) {
                                    tab[lvl][keys].tabGrp = (i * 100);
                                    tabGroupNumList.push(tab[lvl][keys].tabGrp);
                                    break;
                                }
                            }
                        }
                    }

                    /*Push the tab elements based on our tab group ordering*/
                    tabGroupNumList.sort(numberSort);
                    for (var i = 0; i < tabGroupNumList.length; i++) {
                        for (var keys in tab[lvl]) {
                            if (tab[lvl][keys].tabGrp === tabGroupNumList[i]) {
                                for (var j = 0; j < tab[lvl][keys].length; j++) {
                                    pushTabElement(tab[lvl][keys][j].tag, tab[lvl][keys].tabGrp);
                                }
                            }
                        }
                    }
                }
            }

            // apply list filled through pushTabElement and used by appxSetTabFocus
            appx_session.tablist = tablist;
        }

    }
    catch (ex) {
        console.log("appxSetTabIndexes: " + ex);
        console.log(ex.stack);
    }
}

//Sends show data to the server TMNET_MSG_TYPE_SHOW
/* TShow -> MDisplay - for 32bit
  public byte[] terminal = new byte[4];  0
  public byte mode;                      4
  public byte[3] padding                 5
  public byte[] keymap = new byte[4];    8
  public MRCBlock cursor;               12 (+8) row, col = 16
  public int timeout;                   20
  public byte[] charId = new byte[4];   24
  public int option;                    28
  public int status;                    32
  public int boxCount;                  36
  public byte[] boxPtr = new byte[4];   40
  public MRCBlock cursorAlt;            44 (+8)
  public int optionAlt;                 52
 */
/* TShow -> MDisplay - for 64bit
  public byte[] terminal = new byte[8];  0
  public byte mode;                      8
  public byte[7] padding                 9
  public byte[] keymap = new byte[8];    16
  public MRCBlock cursor;               24 (+8) row, col = 28
  public int timeout;                   32
  public byte[] charId = new byte[4];   36
  public int option;                    40
  public int status;                    44
  public int boxCount;                  48
  public byte[] boxPtr = new byte[4];   52
  public MRCBlock cursorAlt;            56 (+8)
  public int optionAlt;                 64
  public byte[4] padding;               68
 */
function sendappxshow(option, data) {
    if( !appx_session.buildingEm )
        clearAndReset();

    appx_session.showCursor = false;
    appxSetStatusStateText(APPX_STATE_BUSY);
    try {
        if( !appx_session.buildingEm ) {
			var activeTableProcessId = 0;
            $(".appxtablewidget").each(function $_each() {
                let uigrid = $(this).attr('id');
				let isActiveElement = document.activeElement;
				let isActiveTable = false;
				if (isActiveElement.offsetParent != null) {
					isActiveTable = isActiveElement.offsetParent.id.includes(uigrid);	
				} else {
					isActiveTable = true;
				}

				if ( isActiveTable ) {
					let activeTable = AppxTable.getAppxTable( $("#" + uigrid).data("tableHashKey") );
					activeTableProcessId = activeTable._widgetData.wPcbId;
				}					
			});
			
            $(".appxtablewidget").each(function $_each() {
                    var uigrid = $(this).attr('id');
					
                    AppxTable.updateTableFromGrid( uigrid );

                    var selrows = AppxTable.getSelections( uigrid );
                    var selkeys = [];
					var targetTable = AppxTable.getAppxTable( $("#" + uigrid).data("tableHashKey") );
					
				    // We’ll only process the selkeys of tables that are in the same process as the table that is active (has focus)
					//  or when no table has focus, we’ll process the selkeys of all tables 
					if ( (activeTableProcessId == targetTable._widgetData.wPcbId) || (activeTableProcessId == 0) ) {
						for (var i = 0; i < selrows.length; i++) {
							//remove the added 'i' from the selkeys
							/* we added 'i' to the begining of the id to comply with html id (must include at least one character)
							   so the added 'i' needs to be removed from the key before we send it to the engine */
							selkeys.push(selrows[i].substring(1));
						}
						// Sort the selected rows when there is more than one row selected and we aren't submitting them in selection order
						if ((selkeys.length > 1) && (!appx_session.getProp("submitSelectedRowsInSelectionOrder"))) {
							// Get the map object to sort on from the table
							const sortorder = targetTable._tableData.selectedRowCollection;
							// Sort the selected keys by their row numbers 
							if (sortorder !== undefined) {
								selkeys.sort(function compareRowNumbers(a, b) {
										if (sortorder.has(a) != true || sortorder.has(b) != true ) {
											return 0;
										}  
										if (sortorder.get(a) < sortorder.get(b)) {
											return -1;
										}  
										if (sortorder.get(a) > sortorder.get(b)) {
											return 1;
										}  
										return 0;
									 }
								);
							}
						}	
					}
									
                    $(this).data("selkeys", selkeys);
                });
        }

        if (appxIsLocked() && option != null && option != OPT_NULL && appx_session.processhelpoption == null ) return; //e.g. obsolete focus handler
        appx_session.applyStylesCount = 0;
        appx_session.currenttabledata = [];
        appxSetLocked(true);
        appxstoptimeout();

        appx_session.override = false;

        var showstructints = [];
        var rtnshow = appx_session.current_show;
        if (rtnshow != null) {

            var mode;

            if(appx_session._APPX64)
            {
                mode = rtnshow.rawdata[8]; //mode
                //have to set return status in 0xC message to indicate and option was selected
                if (option != null && option >= 0) rtnshow.rawdata[47] = SHOW_STAT_OPT;//status

                var dv = new DataView(new Uint8Array(rtnshow.rawdata).buffer);
                
                var cur = appxGetCursorPos();
                dv.setUint32(24, cur.row); //cursor row
                dv.setUint32(28, cur.col); //cursor column
				
				// Task #668 - When the lost focus event is set on multiple new logic field widgets
				//	we need to only send the lost focus event for the widget that lost the focus, not both     
				var isLogicWidget = false;
				if (option == OPT_TAB_OUT && data.length >= 2) {
					var currentFocus = $(":focus");
					currentFocus.widgetOption = option;
					for (const currentValue of data) {
						if (($(currentValue).hasClass("togglebutton") || $(currentValue).hasClass("sliderswitch")) && 
							(currentValue != currentFocus.context.activeElement)) {
							isLogicWidget = true;
							break;
						}
					}
				}				
				
                if(option != null && !isLogicWidget) {
                    dv.setUint32(40, parseInt(option));//option
                } else{
                    dv.setUint32(40, OPT_NULL);//option
                }

                if (mode == M_SHOW || mode == M_COMPARE) {
                    dv.setUint32(44, SHOW_STAT_NO_WAIT);//status
                    dv.setUint32(40, OPT_NULL);//option
                }

                dv.setInt32(56, 0);//alt cursor row
                dv.setInt32(60, 0);//alt cursor col
                dv.setUint32(64, 0);//alt option
                dv.setUint32(68, 0);//padding buffer
                if (rtnshow.altuseroption && appx_session.scan) {
                    var temp1 = 0;
                    var temp2 = 0;

                    temp1 = dv.getUint32(24); //cursor row
                    temp2 = new DataView(new Uint8Array(rtnshow.altcursorrow).buffer).getUint32(0);// alt cursor row
                    dv.setUint32(24, temp2); //cursor row
                    dv.setUint32(56, temp1); //cursor alt row

                    temp1 = dv.getUint32(28); //cursor col
                    temp2 = new DataView(new Uint8Array(rtnshow.altcursorcol).buffer).getUint32(0);// alt cursor col
                    dv.setUint32(28, temp2); //cursor col
                    dv.setUint32(60, temp1); //cursor alt col

                    temp1 = dv.getUint32(40); //option
                    temp2 = new DataView(new Uint8Array(rtnshow.altuseroption).buffer).getUint32(0);// alt option
                    dv.setUint32(40, temp2); //option
                    dv.setUint32(64, temp1); //alt option

                    rtnshow.altuseroption = null;
                    rtnshow.altcursorrow = null;
                    rtnshow.altcursorcol = null;
                } 
                else if (rtnshow.altuseroption) {
                    /*
                    alt user option is a buffered option (like lose focus). We need to execute the buffered option
                    first. So in here we swap alt option with option
                    For example, if focus is on a field that has LOSE FOCUS option, and user click on a button, we want
                    the lose focus to execute first, the button option to execute. In this scenarion appx caches the lose focus option in 
                    altuseroption, then waits for a little bit (200ms) to see if any other option happens (button click, gain focus,...). 
                    If nothing happens then we send lose focus, if not, we swap altuser option with option and send both at the same time.  
                    */

                    let row = 0;
                    let altrow = 0;
                    let col = 0;
                    let altcol = 0;
                    let option = 0;
                    let altoption = 0;
                  
                    row = dv.getUint32(24); //cursor row
                    altrow = new DataView(new Uint8Array(rtnshow.altcursorrow).buffer).getUint32(0);// alt cursor row
                    
                    col = dv.getUint32(28); //cursor col
                    altcol = new DataView(new Uint8Array(rtnshow.altcursorcol).buffer).getUint32(0);// alt cursor col
                    
                    option = dv.getUint32(40); //option
                    altoption = new DataView(new Uint8Array(rtnshow.altuseroption).buffer).getUint32(0);// alt option

                    //there is a posibility that the alt option and option are the same. If thets the case, don't sent both options
                    if(row != altrow || col != altcol || option != altoption){
                        dv.setUint32(24, altrow); //cursor row
                        dv.setUint32(56, row); //cursor alt row
                        dv.setUint32(28, altcol); //cursor col
                        dv.setUint32(60, col); //cursor alt col
                        dv.setUint32(40, altoption); //option
                        dv.setUint32(64, option); //alt option
                    }

                    rtnshow.altuseroption = null;
                    rtnshow.altcursorrow = null;
                    rtnshow.altcursorcol = null;
                }

            }//end if (64-bit)
            else{
                mode = rtnshow.rawdata[4];
                //have to set return status in 0xC message to indicate and option was selected
                if (option != null && option >= 0) rtnshow.rawdata[35] = SHOW_STAT_OPT;

                var dv = new DataView(new Uint8Array(rtnshow.rawdata).buffer);

                //20140314 chris@praclox.nl cursor
                var cur = appxGetCursorPos();
                dv.setUint32(12, cur.row);
                dv.setUint32(16, cur.col);
                if(option != null){
                    dv.setUint32(28, parseInt(option));
                } else{
                    dv.setUint32(28, OPT_NULL);
                }

                if (mode == M_SHOW || mode == M_COMPARE) {
                    dv.setUint32(32, SHOW_STAT_NO_WAIT);
                    dv.setUint32(28, OPT_NULL);
                }

                dv.setInt32(44, 0);
                dv.setInt32(48, 0);
                dv.setUint32(52, 0);
                if (rtnshow.altuseroption && appx_session.scan) {
                    var temp1 = 0;
                    var temp2 = 0;

                    temp1 = dv.getUint32(12);
                    temp2 = new DataView(new Uint8Array(rtnshow.altcursorrow).buffer).getUint32(0);
                    dv.setUint32(12, temp2);
                    dv.setUint32(44, temp1);

                    temp1 = dv.getUint32(16);
                    temp2 = new DataView(new Uint8Array(rtnshow.altcursorcol).buffer).getUint32(0);
                    dv.setUint32(16, temp2);
                    dv.setUint32(48, temp1);

                    temp1 = dv.getUint32(28);
                    temp2 = new DataView(new Uint8Array(rtnshow.altuseroption).buffer).getUint32(0);
                    dv.setUint32(28, temp2);
                    dv.setUint32(52, temp1);

                    rtnshow.altuseroption = null;
                    rtnshow.altcursorrow = null;
                    rtnshow.altcursorcol = null;
                } 
                else if (rtnshow.altuseroption) {
                    /*  
                    alt user option is a buffered option (like lose focus). We need to execute the buffered option
                    first. So in here we swap alt option with option
                    For example, if focus is on a field that has LOSE FOCUS option, and user click on a button, we want
                    the lose focus to execute first, the button option to execute. In this scenarion appx caches the lose focus option in 
                    altuseroption, then waits for a little bit (200ms) to see if any other option happens (button click, gain focus,...). 
                    If nothing happens then we send lose focus, if not, we swap altuser option with option and send both at the same time.  
                    */

                    let row = 0;
                    let altrow = 0;
                    let col = 0;
                    let altcol = 0;
                    let option = 0;
                    let altoption = 0;
                    
                    row = dv.getUint32(12); //cursor row
                    altrow = new DataView(new Uint8Array(rtnshow.altcursorrow).buffer).getUint32(0);// alt cursor row
                    
                    col = dv.getUint32(16); //cursor col
                    altcol = new DataView(new Uint8Array(rtnshow.altcursorcol).buffer).getUint32(0);// alt cursor col
                    
                    option = dv.getUint32(28); //option
                    altoption = new DataView(new Uint8Array(rtnshow.altuseroption).buffer).getUint32(0);// alt option

                    //there is a posibility that the alt option and option are the same. If thets the case, don't sent both options
                    if(row != altrow || col != altcol || option != altoption){
                        dv.setUint32(12, altrow); //cursor row
                        dv.setUint32(44, row); //cursor alt row
                        dv.setUint32(16, altcol); //cursor col
                        dv.setUint32(48, col); //cursor alt col
                        dv.setUint32(28, altoption); //option
                        dv.setUint32(52, option); //alt option
                    }

                    rtnshow.altuseroption = null;
                    rtnshow.altcursorrow = null;
                    rtnshow.altcursorcol = null;
                }
            }//end else (32-bit)

            var showstructints = [];
            for (var i = 0; i < new Uint8Array(dv.buffer).length; i++) {
                showstructints[i] = dv.getUint8(i);
            }
        }

        var ms = {
            cmd: 'appxmessage',
            args: [0, 0, 0, 2, 12, 0, 0, 0],
            handler: 'appxshowhandler',
            data: null
        };
        appx_session.ws.send(JSON.stringify(ms));

        ms = {
            cmd: 'appxmessage',
            // we need to calculate the screen size here based on attach function(defaults to 31x110 which require 54 bytes to send repeating (ff 00)s which means ((255 x (54/2))/2 = 3410
            args: [0, 54, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 209, 0],
            handler: 'appxshowhandler',
            data: null
        };
        appx_session.ws.send(JSON.stringify(ms));

        ms = {
            cmd: 'appxmessage',
            args: showstructints,
            handler: 'appxshowhandler',
            data: null
        };
        appx_session.ws.send(JSON.stringify(ms));
        if (Math.abs(mode & M_COMPARE) != 0 && appx_session.dirtySinceSave) {
            ms = {
                cmd: 'appxmessage',
                args: [1],
                handler: 'appxshowhandler',
                data: null
            };
            appx_session.ws.send(JSON.stringify(ms));
        }
        else {
            ms = {
                cmd: 'appxmessage',
                args: [0],
                handler: 'appxshowhandler',
                data: null
            };
            appx_session.ws.send(JSON.stringify(ms));
        }
        if (appx_session.override) {

            ms = {
                cmd: 'appxmessage',
                args: [0, 0, 0, 2, 86, 0, 0, 0, 0, 0, 0, 1, 11, 27, 0, 3, 84, 82, 75],
                handler: 'appxshowhandler',
                data: null
            };
            appx_session.ws.send(JSON.stringify(ms));

        }
        else if (data.length > 0) {

            ms = {
                cmd: 'appxmessage',
                args: [0, 0, 0, 2, 86, 0, 0, 0],
                handler: 'appxshowhandler',
                data: null
            };
            appx_session.ws.send(JSON.stringify(ms));

            var msgdata = [];
            for (var dl = 0; dl < data.length; dl++) {
                var dataval = "";
				console.log("sendappxshow proccessing element type " + "\"" + $(data[dl]).prop('nodeName') + "\"" + " that has these classes: " + $(data[dl]).attr('class').split(/\s+/));
                if ($(data[dl]).find(".appxdatevalue").val()) {
                    dataval = $(data[dl]).find(".appxdatevalue").val();
                } else if ($(data[dl]).closest(".appxitem").find("input").val()) {
                    dataval = $(data[dl]).closest(".appxitem").find("input").val();
                } else if ($(data[dl]).hasClass("masked") && $(data[dl]).val() === "") {
                    dataval = $(data[dl]).data()["_inputmask_opts"].alias.replace(/\*/g, " ");
                } else if ($(data[dl]).val()) {
                    /*If data is file upload element we need to do extra checking and
                    **modify file name so that APPX knows the file was stored in
                    **mongo*/
                    if (($(data[dl]).hasClass("appxfilewrapper") &&
                        $(data[dl]).hasClass("DULC")) ||
                        $(data[dl]).hasClass("DnD")) {
                        var dv = $(data[dl]).val();
                        var filesArrayLength;
                        filesArrayLength = appx_session.filesUploadArray.length;
                        if (filesArrayLength > 1 || $(data[dl]).hasClass("DnD")) {
                            var dvd = dataval = "$(sendFile)\\c:\\directory\\";
                            var fType = "folder";
                            if ($(data[dl]).hasClass("DnD") && filesArrayLength == 1) {
                                fType = "file";
                                dvd = dataval = "$(sendFile)\\";
                            }
                            /*Only method for sending directory or multiple files is
                            **to create drag & drop object for each file.*/
                            for (var i = 0; i < filesArrayLength; i++) {
                                var fileName = appx_session.filesUploadArray[i].name.replace(/ /g, "_");
                                var dndName = "$(sendFile)\\" + "[[" + i + "]]" + fileName;
                                if (filesArrayLength == 1) {
                                    dndName = "$(sendFile)\\" + fileName;
                                }
                                var mRow, mCol;
                                mRow = $(data[dl]).data("row");
                                mCol = $(data[dl]).data("col");
                                appx_session.dndData.push({
                                    row: mRow,
                                    col: mCol,
                                    parentType: $(data[dl]).data("parent_type"),
                                    path: dvd + fileName,
                                    name: dndName,
                                    ext: fileName.substring(fileName.lastIndexOf(".") + 1).toLowerCase(),
                                    mtype: fType,
                                    size: appx_session.filesUploadArray[i].size.toString(),
                                    props: []
                                });
                            }
                        } else {
                            if (dv.indexOf("\\") > -1 || dv.indexOf("\/") > -1) {
                                if (dv.indexOf("\\") > -1) {
                                    dataval = "$(sendFile)" + dv.substring(dv.lastIndexOf("\\"));
                                } else {
                                    dataval = "$(sendFile)" + dv.substring(dv.lastIndexOf("\/"));
                                }
                            } else {
                                dataval = "$(sendFile)\\" + dv;
                            }
                        }
                    } else {
                        dataval = $(data[dl]).val();
                    }
                } else if ($(data[dl]).closest(".appxitem").val()) {
                    dataval = $(data[dl]).closest(".appxitem").val();
                } else if ($(data[dl]).find(".appx-data").html()) {
                    dataval = $(data[dl]).find(".appx-data").html();
                } else if ($(data[dl]).hasClass("togglebutton")){
                    if ($(data[dl]).hasClass("down")) {
                        dataval = "Y";
						$(data[dl]).val(dataval);
                    } else {
                        dataval = "N";
						$(data[dl]).val(dataval);
                    }
                } else if ($(data[dl]).hasClass("sliderswitch-slider")){
                    if ($(data[dl]).hasClass("checked")) {
                        dataval = "Y";
						$(data[dl]).val(dataval);
                    } else {
                        dataval = "N";
						$(data[dl]).val(dataval);
                    }
				}

                if ($(data[dl]).css("text-transform") == "uppercase") {
                    dataval = dataval.toUpperCase();
                }
                /*BUG #5004: Replace the curly qoutations only when the field is not a unicode field*/
                /* “ ” ‘ ’ */
                if($(data[dl]).data("unicode") != true ){
                    dataval = dataval.replace(/\u2018|\u2019/g, "'"); 
                    dataval = dataval.replace(/\u201C|\u201D/g, '"');
                }

                var id = $(data[dl]).closest(".appxitem").attr('id');
                if (id == "" || !id) {
                    id = $(data[dl]).closest(".appxitem").closest("div").attr('id');
                }
                if (id !== undefined) {
                    var datarow = parseInt($("#" + id).data("row"));
                    msgdata.push(datarow);
                    var datacol = parseInt($("#" + id).data("col"));
                    msgdata.push(datacol);
                } else {
                    /*If there is no item, then check to see if it was a widget and
                    **get position of widget*/
                    var datarow = (parseInt($(data[dl]).closest(".appxwidget").closest("div").data("row")));
                    msgdata.push(datarow);
                    var datacol = (parseInt($(data[dl]).closest(".appxwidget").closest("div").data("col")));
                    msgdata.push(datacol);
                }
                var u8strarray = toUTF8Array(dataval);
                /*Since release 6.1 (unicode) we send the data length as 4 bytes so we can send large field lengths*/
                if((appx_session.server_extended_feature_mask & TMNET_FEATURE2_LARGE_WORK_FIELD) == TMNET_FEATURE2_LARGE_WORK_FIELD){
                    Array.prototype.push.apply(msgdata, hton32(u8strarray.length));
                }
                else{
                    var byte2 = u8strarray.length % 256;
                    var byte1 = (u8strarray.length - byte2) / 256;
                    msgdata.push(byte1);
                    msgdata.push(byte2);
                }
                //This cases range error on large data. So, got replaced by a for loop
                //Array.prototype.push.apply(msgdata, u8strarray);
                for(i=0;i<u8strarray.length;i++){
                    msgdata.push(u8strarray[i]);
                }
            }//end for

            clearClientIds();
            /*If we came into this to build a drop object then we send the engine
            **a message length of 0, otherwise we let it process as normal.*/
            if ($(data[0]).hasClass("DnD")) {
                ms = {
                    cmd: 'appxmessage',
                    args: [0, 0, 0, 0],
                    handler: 'appxshowhandler',
                    data: null
                };
                appx_session.ws.send(JSON.stringify(ms));
            } else {
                ms = {
                    cmd: 'appxmessage',
                    args: hton32(data.length),
                    handler: 'appxshowhandler',
                    data: null
                };
                appx_session.ws.send(JSON.stringify(ms));

                ms = {
                    cmd: 'appxmessage',
                    args: msgdata,
                    handler: 'appxshowhandler',
                    data: null
                };
                appx_session.ws.send(JSON.stringify(ms));
            }

        }
        else {

            ms = {
                cmd: 'appxmessage',
                args: [0, 0, 0, 2, 86, 0, 0, 0, 0, 0, 0, 0],
                handler: 'appxshowhandler',
                data: null
            };
            appx_session.ws.send(JSON.stringify(ms));

        }

        //END SENDSHOW and LONGDATA

        /* BEGIN-------------------------------- */
        /* SEND TABLE DATA - ALWAYS SEND MESSAGE */
        /* even when length of table data is 0   */
        /* ------------------------------------- */

        ms = {
            cmd: 'appxmessage',
            args: [0, 0, 0, 4, 90, 0, 0, 0],
            handler: 'appxshowhandler',
            data: null
        };
        appx_session.ws.send(JSON.stringify(ms));

        var keycnt = 0;
        data = $(".appxtablewidget");
        var msgdata = [];
        for (var dl = 0; dl < data.length; dl++) {

            var dataobj = $(data[dl]).data();

            if (dataobj.selkeys) {
                for (var z = 0; z < dataobj.selkeys.length; z++) {
                    var dataval = "";
                    // We should add a handler in keydown event to change to upper if css text tranform UPPER case is set
                    // Or check field class here to see if it should be uppercase
                    dataval = dataobj.selkeys[z];

                    //Table Position ROW - need 4bytes BE
                    var datarow = hton32(parseInt(dataobj.row));
                    Array.prototype.push.apply(msgdata, datarow);
                    //Table Poition COL - need 4bytes BE
                    var datacol = hton32(parseInt(dataobj.col));
                    Array.prototype.push.apply(msgdata, datacol);
                    //SEQ - need 4bytes BE
                    var seq = hton32(keycnt + 1);
                    Array.prototype.push.apply(msgdata, seq);
                    //Parent Type 1byte: alway a 6
                    msgdata.push(6);
                    //Key length in hex 
                    msgdata.push(dataval.length / 2);
                    //convert key values to hex
                    for (var k = 0; k < dataval.length; k += 2) {
                        msgdata.push(parseInt(dataval.substring(k, k + 2), 16));
                    }
                    keycnt++;
                }
            }
        }

        //RUN THRU 4 BYTE BE
        var msglength = hton32(keycnt);

        ms = {
            cmd: 'appxmessage',
            args: msglength,
            handler: 'appxshowhandler',
            data: null
        };

        appx_session.ws.send(JSON.stringify(ms));

        if (data.length > 0) {
            ms = {
                cmd: 'appxmessage',
                args: msgdata,
                handler: 'appxshowhandler',
                data: null
            };
            appx_session.ws.send(JSON.stringify(ms));
        }

        /* END---------------------------------- */
        /* SEND TABLE DATA - ALWAYS SEND MESSAGE */
        /* even when length of table data is 0   */
        /* ------------------------------------- */

        //<dnd>
        /* BEGIN-------------------------------- */
        /* SEND DROP DATA  - ALWAYS SEND MESSAGE */
        /* even when length of table data is 0   */
        /* ------------------------------------- */

        /*
         **  DropInfo Data is organized in this way...
         **
         **  HEADER - 8 byte standard header with a data length of 4 and type 92
         **           where the 4 bytes of data is a uint32 count of dropinfo
         **           records to follow.
         **  count  - uint32 count of dropinfo records to follow.
         **
         **  ===== DropInfo Record, one per count =====
         **
         **    rc_blk - uint32 row, uint32 col of the widget that was dropped onto.
         **    parent - uint32 of the widget parent type of the widget dropped onto.
         **    path   - uint16 string length of path followed by string bytes.
         **    name   - uint16 string length of name followed by string bytes.
         **    ext    - uint16 string length of extention followed by string bytes.
         **    type   - uint16 string length of type followed by string bytes.
         **    size   - uint32 file size
         **    pcount - uint16 count of properties to follow for this dropinfo record.
         **
         **  ===== Property Record, one per pcount =====
         **
         **      keyword - uint16 string length of keyword followed by string bytes.
         **      value   - uint16 string length of value followed by string bytes.
         */
        ms = {
            cmd: 'appxmessage',
            args: [0, 0, 0, 4, 92, 0, 0, 0], //TMNET_MSG_TYPE_DRAG_AND_DROP = 92
            handler: 'appxshowhandler',
            data: null
        };
        appx_session.ws.send(JSON.stringify(ms));

        var dropcount = appx_session.dndData.length;
        ms = {
            cmd: 'appxmessage',
            args: hton32(dropcount),
            handler: 'appxshowhandler',
            data: null
        };
        appx_session.ws.send(JSON.stringify(ms));
        var htmlEncoding = $('meta[name=appx-character-encoding]').attr("content");
        for (var loop = 0; loop < dropcount; loop++) {
            var mDnD = appx_session.dndData[loop];
            //Should be a uint RC_BLK of the widget position
            //4 byte row
            ms = {
                cmd: 'appxmessage',
                args: hton32(mDnD.row),
                handler: 'appxshowhandler',
                data: null
            };
            appx_session.ws.send(JSON.stringify(ms));
            //4 byte col
            ms = {
                cmd: 'appxmessage',
                args: hton32(mDnD.col),
                handler: 'appxshowhandler',
                data: null
            };
            appx_session.ws.send(JSON.stringify(ms));

            // 4 byte integer parent type
            ms = {
                cmd: 'appxmessage',
                args: hton32(mDnD.parentType),
                handler: 'appxshowhandler',
                data: null
            };
            appx_session.ws.send(JSON.stringify(ms));

            // 2 byte integer dropinfo path len
            ms = {
                cmd: 'appxmessage',
                args: hton16((toUTF8Array(mDnD.path)).length),
                handler: 'appxshowhandler',
                data: null
            };
            appx_session.ws.send(JSON.stringify(ms));

            // string dropinfo path
            ms = {
                cmd: 'appxmessage',
                args: toUTF8Array(mDnD.path),
                handler: 'appxshowhandler',
                data: null
            };
            appx_session.ws.send(JSON.stringify(ms));

            // 2 byte integer dropinfo name len
            ms = {
                cmd: 'appxmessage',
                args: hton16((toUTF8Array(mDnD.name)).length),
                handler: 'appxshowhandler',
                data: null
            };
            appx_session.ws.send(JSON.stringify(ms));

            // string dropinfo name
            ms = {
                cmd: 'appxmessage',
                args: toUTF8Array(mDnD.name),
                handler: 'appxshowhandler',
                data: null
            };
            appx_session.ws.send(JSON.stringify(ms));

            // 2 byte integer dropinfo ext len
            ms = {
                cmd: 'appxmessage',
                args: hton16((toUTF8Array(mDnD.ext)).length),
                handler: 'appxshowhandler',
                data: null
            };
            appx_session.ws.send(JSON.stringify(ms));

            // string dropinfo ext
            ms = {
                cmd: 'appxmessage',
                args: toUTF8Array(mDnD.ext),
                handler: 'appxshowhandler',
                data: null
            };
            appx_session.ws.send(JSON.stringify(ms));

            // 2 byte integer dropinfo type len
            ms = {
                cmd: 'appxmessage',
                args: hton16((toUTF8Array(mDnD.mtype)).length),
                handler: 'appxshowhandler',
                data: null
            };
            appx_session.ws.send(JSON.stringify(ms));

            // string dropinfo type
            ms = {
                cmd: 'appxmessage',
                args: toUTF8Array(mDnD.mtype),
                handler: 'appxshowhandler',
                data: null
            };
            appx_session.ws.send(JSON.stringify(ms));

			if (appx_session._isServerUnicode == true) {
				// 4 byte int size
				ms = {
					cmd: 'appxmessage',
					args: htonll(mDnD.size),
					handler: 'appxshowhandler',
					data: null
				};
				appx_session.ws.send(JSON.stringify(ms));
			} else {
				// 8 byte int size
				ms = {
					cmd: 'appxmessage',
					args: htonl(mDnD.size),
					handler: 'appxshowhandler',
					data: null
				};
				appx_session.ws.send(JSON.stringify(ms));
			}

            // 2 byte integer propcount
            var propcount = mDnD.props.length;
            ms = {
                cmd: 'appxmessage',
                args: hton16(propcount),
                handler: 'appxshowhandler',
                data: null
            };
            appx_session.ws.send(JSON.stringify(ms));
            for (var proploop = 0; proploop < propcount; proploop++) {
                var prop = mDnD.props[proploop];

                // 2 byte integer dropinfo keyword len
                ms = {
                    cmd: 'appxmessage',
                    args: hton16((toUTF8Array(prop.name)).length),
                    handler: 'appxshowhandler',
                    data: null
                };
                appx_session.ws.send(JSON.stringify(ms));
                // string dropinfo keyword
                ms = {
                    cmd: 'appxmessage',
                    args: toUTF8Array(prop.name),
                    handler: 'appxshowhandler',
                    data: null
                };
                appx_session.ws.send(JSON.stringify(ms));

                // 2 byte integer dropinfo value len
                ms = {
                    cmd: 'appxmessage',
                    args: hton16((toUTF8Array(prop.value)).length),
                    handler: 'appxshowhandler',
                    data: null
                };
                appx_session.ws.send(JSON.stringify(ms));

                // string dropinfo value
                ms = {
                    cmd: 'appxmessage',
                    args: toUTF8Array(prop.value),
                    handler: 'appxshowhandler',
                    data: null
                };
                appx_session.ws.send(JSON.stringify(ms));
            }
        }
        appx_session.dndData = [];
        //</dnd>

    }
    catch (ex) {
        console.log("sendappxshow: " + ex);
        console.log(ex.stack);
    }

}

//TShow
//var M_CHECK   = 0x01;
var M_SHOW = 0x02;
//var M_ALARM   = 0x04;
var M_WAIT = 0x08;
//var M_PAINT   = 0x10;
var M_COMPARE = 0x20;
var M_SAVE = 0x40;
//MBox
var SCROLL = 0x00010000; //Scrolling area
var SCROLL_REG = 0x00020000; //Scrolling Region
var SCROLL_ACT = 0x00040000; //Scrolling ActiveReg
var IN_PROGRESS = 0x00080000; // box is in progress message
var BORD_OLINE = 0x10000000  // Draw a top border using overlines    
//var CHR_BORD_ULINE = 0x20000000  // Draw a bottom border using underlines
var BORD_OBS = 0x40000000  // Obscure other overlapping borders    
var BORD_AROUND = 0x80000000; // Draw the border around the coord's   
//var BORDER      = 0x08000000;
//Document long constants
/*var CHR_SCROLL =     0x0000000000010000;//Scrolling area
var CHR_SCROLL_REG = 0x0000000000020000;//Scrolling Region
var CHR_SCROLL_ACT = 0x0000000000040000;//Scrolling ActiveReg
var CHR_BORD_BOX = 0x0000000008000000;//complete rect border*/

/*
**  The SHOW_STAT values listed below are the valid values for the show_stat
**  field in the show_blk.
*/
var SHOW_STAT_OPT       =   1	/* An option was selected		    */
var SHOW_STAT_TIME_OUT  =   2	/* Time expired before an option was chosen */
var SHOW_STAT_SYS_MSG   =   4   /* The system sent BOB a message            */
var SHOW_STAT_NO_WAIT   =   8   /* No option was selected and the caller    */

function appxIsLocked() {
    return appx_session.locked;
}

function appxSetLocked(b) {
     appx_session.locked = b;
    if (!b) {
        while (appx_session.runOnUnlock.length) {
            appx_session.runOnUnlock.shift().call();
        }
    }
}

function appxGetCellSize() {
    return {
        "w": appx_session.colWidthPx,
        "h": appx_session.rowHeightPx
    };
}

//clamp column
function appxFixCursorCol(c) {
    if (c < 1) c = 1;
    if (c > appx_session.screencols) c = appx_session.screencols;
    if (c > 255) c = 255;
    return c;
}

//clamp line/row
function appxFixCursorRow(r) {
    if (r < 1) r = 1;
    if (r > appx_session.screenrows - 3) r = appx_session.screenrows - 3;
    if (r > 255) r = 255;
    return r;
}

/** Display.getCursorPos / Document.lookupCursorPosition
 * Get the current cursor position.
 */
function appxGetCursorPos() {
    var c = appxFixCursorCol(ab2int32(appx_session.current_show.cursorcol));
    var r = appxFixCursorRow(ab2int32(appx_session.current_show.cursorrow));
    return {
        "col": c,
        "row": r
    };
}

function appxIsInProgress(box) {
    return (Math.abs(box.bit_mask & IN_PROGRESS) != 0 && (box.widget == null || box.widget.wWidgetType != 203));
}

function appxIsEmBuild(box) {
    return (Math.abs(box.bit_mask & IN_PROGRESS) != 0 && (box.widget != null && box.widget.wWidgetType == 203));
}

function appxIsFullscreen(box) {
    try {
        return (
            (box.end_column - box.begin_column) + 1 >= appx_session.screencols &&
            (box.end_row - box.begin_row) + 1 >= (appx_session.screenrows - 3)
        );
    } catch (ex) {
        console.log("screen.appxIsFullscreen: " + ex);
        console.log(ex.stack);
    }
}

function appxIsScroll(box) {
    return (Math.abs(box.bit_mask & SCROLL) != 0);
}

function appxIsScrollAct(box) {
    return (Math.abs(box.bit_mask & SCROLL_ACT) != 0);
}

function appxIsScrollReg(box) {
    return (Math.abs(box.bit_mask & SCROLL_REG) != 0);
}

/** Display.putCursor / Document.setCursorPos
 * Puts the cursor at the specified position.
 * @param c column
 * @param r row //l line
 */
function appxSnapshotScanCursor() {
    appx_session.scan_cursorrow = appx_session.current_show.cursorrow;
    appx_session.scan_cursorcol = appx_session.current_show.cursorcol;
}
function appxGetScanCursorPos() {
	// Bug #4398 - Initialize the scan cursor to the screen cursor when its in the uninitialized state (i.e., -1).
	if (appx_session.scan_cursorcol == -1 &&
		appx_session.scan_cursorrow == -1) {
		var cur = appxGetCursorPos();
		appx_session.scan_cursorcol = cur.col;
		appx_session.scan_cursorrow = cur.row;
	}
    var c = appxFixCursorCol(ab2int32(appx_session.scan_cursorcol));
    var r = appxFixCursorRow(ab2int32(appx_session.scan_cursorrow));
    return {
        "col": c,
        "row": r
    };
}
function appxClearScanCursor() {
    appx_session.scan_cursorrow = -1;
    appx_session.scan_cursorcol = -1;
}
function appxPutCursor(c, r) {
    try {
        var bCurItem = false; //cursor inside field?
        if (!appxIsLocked() ) {
            var cur = appxGetCursorPos(); //prevent recursive call through focus
            if (cur.col != c || cur.row != r) {
                appx_session.current_show.cursorcol = [0, 0, 0, appxFixCursorCol(c)];
                appx_session.current_show.cursorrow = [0, 0, 0, appxFixCursorRow(r)];
                bCurItem = appxshowcursor(false); //don't call focus
            }
        }
       /* This fix was for bug #4560 but it caused bug #4712. For now remove it
       else{
            //wait a little to see if locks gets created
            setTimeout(function(){
                if (!appxIsLocked() ) {
                    var cur = appxGetCursorPos(); //prevent recursive call through focus
                    if (cur.col != c || cur.row != r) {
                        appx_session.current_show.cursorcol = [0, 0, 0, appxFixCursorCol(c)];
                        appx_session.current_show.cursorrow = [0, 0, 0, appxFixCursorRow(r)];
                        bCurItem = appxshowcursor(false); //don't call focus
                    }
                }
            },100);
        }*/
        return bCurItem;
    }
    catch (ex) {
        console.log("appxPutCursor: " + ex);
        console.log(ex.stack);
    }
}

function appxPutTabOut() {
    try {
        if (!appxIsLocked()) {
            var cur = appxGetCursorPos();
            appx_session.current_show.altcursorcol = [0, 0, 0, appxFixCursorCol(cur.col)];
            appx_session.current_show.altcursorrow = [0, 0, 0, appxFixCursorRow(cur.row)];
            appx_session.current_show.altuseroption = [0, 0, 1, 76];
        }
    }
    catch (ex) {
        console.log("appxPutTabOut: " + ex);
        console.log(ex.stack);
    }
}

function appxScrollClick($rowtag, event) {
    //don't do anything if it is already selected
    var col = $rowtag.data("col");
    var row = $rowtag.data("row");
    if (!col || !row) {
        return;
    }
    event.stopPropagation();
	var ilfcol = Math.floor(appx_session.mouseX / appx_session.colWidthPx);
	ilfcol = ilfcol - Math.floor($rowtag.parent()[0].getBoundingClientRect().left / appx_session.colWidthPx ) + col;
    appxPutCursor(!ilfEditorMode ? col : ilfcol, row);
    //if we are in old ilf debugger we dont want to execute ption return when we click on a row
    if($rowtag.parent().hasClass("appxILFDebuggerStatements")){
        return;
    }
	// Handle the inadvertent click that occurred 
	// somewhere outside of the editable Ilf code area 
	var ilfChangeMode = $rowtag.data("ilfchangemode");
	if (ilfChangeMode !== undefined) { return; }
	// When in Ilf editor mode save the first or last row selected
	// depending on the state of Ilf selection switches
	if (ilfEditorMode && !ilfSelecting) {
		ilfFirstSelectedRow = parseInt($rowtag.prop('id').substring(4), 10);
	}
	if (ilfEditorMode && ilfSelecting) { 
		ilfLastSelectedRow = parseInt($rowtag.prop('id').substring(4), 10);
		return; 
	} 
    appxwidgetcallback(OPT_ENTER);
}

function appxSetStatusPIDText(str) {
    $("#appx_status_pid").html(str);
}

function appxSetStatusDbText(str) {
    $("#appx_status_db").html(str);
}

function appxSetStatusApText(str) {
    $("#appx_status_ap").html(str);
}

function appxSetStatusVerText(str) {
    $("#appx_status_ver").html(str);
}

function appxSetStatusUserText(str) {
    $("#appx_status_user").html(str);
}

function appxSetStatusKeymapText(str) {
    $("#appx_status_keymap").html(str);
}

function appxSetStatusMsgText(str) {
    appxSetStatusText(str);
}

function appxSetStatusModeText(str) {
    $("#appx_status_mode").html(str);
}

var APPX_STATE_BUSY = 0;
var APPX_STATE_READY = 1;
var APPX_STATE_DIRTY = 2;
var APPX_STATE_IMAGES = 3;
var APPX_STATE_EMS = 4;
var appx_state_last = -1;

function appxSetStatusStateText(state) {
    if (state == appx_state_last)
        return;

    appx_state_last = state;

    var $str = $("<span>");

    switch (state) {
        case APPX_STATE_BUSY:
            $("*").addClass("wait");
            $str.html("busy").addClass("appx-state-busy");
            break;
        case APPX_STATE_READY:
            $("*").removeClass("wait");
            $str.html("ready").addClass("appx-state-ready");
            break;
        case APPX_STATE_DIRTY:
            $str.html("ready+").addClass("appx-state-dirty");
            appx_session.dirtySinceSave = true;
            break;
        case APPX_STATE_IMAGES:
            $str.html("images").addClass("appx-state-images");
            break;
        case APPX_STATE_EMS:
            $str.html("compile").addClass("appx-state-ems");
            break;
    }

    $("#appx_status_stat").html($str);
}

function appxSetStatusEmbldText(str) {
    $("#appx_status_embld").html(str);
}

function appxSetStatusProgressText(str) {
    $("#appx_status_progress").html(str);
}

function appxClearStatusMsgText() {
    $("#appx-status-msg").html("");
    $("#appx-status-msg").css("background-color", "white");
}

/*
** This fuction shows messages in status bar
**      Str: is the message
**      severity: Type of message
**            valid values:  {
**                                0 : Info
**                                1 : Warning
**                                2 : Error
**                                3 : Cancel
**                            }
*/
function appxSetStatusText(str, severity) {
    str = str.trim();
    var $statusbar = $("#appx-status-msg");
    var $msghtml = $("<span>").html(str);
    $msghtml.addClass("status-msg");
    switch (severity) {
        case 0:
            $msghtml.addClass("status-msg-info");
			if (appx_session.getProp("silenceSystemMessageSounds") == false) {
				appxloadurlhandler( {'data':'$messagebeep:'});   // Bug#4447 - no sound on errors, warnings
			} 
            break;
        case 1:
            $msghtml.addClass("status-msg-warning");
			if (appx_session.getProp("silenceSystemMessageSounds") == false) {
				appxloadurlhandler( {'data':'$warningbeep:'});   // Bug#4447 - no sound on errors, warnings
			} 
            break;
        case 2:
            $msghtml.addClass("status-msg-error");
			if (appx_session.getProp("silenceSystemMessageSounds") == false) {
					
				appxloadurlhandler( {'data':'$errorbeep:'});     // Bug#4447 - no sound on errors, warnings
			} 
            break;
        case 3:
            $msghtml.addClass("status-msg-cancel");
			if (appx_session.getProp("silenceSystemMessageSounds") == false) {
				appxloadurlhandler( {'data':'$cancelbeep:'});    // Bug#4447 - no sound on errors, warnings
			} 
            break;
    }
    if (str != undefined && str != "") {
        if($statusbar.text().length > 0){
            $statusbar.append("<br/>");
        }
        $statusbar.append($msghtml);
    }
}

function appxstarttimeout() {
    appxstoptimeout();
    var s = appx_session.current_show;
    if ((!s) || s.timeout == null || s.timeout <= 0 || s.timeout > 60000) return;
    appx_session.interactT = setTimeout(function setTimeoutCallback() {
        appxwidgetcallback(s.useroption ? ab2int32(s.useroption) : OPT_DIR_PROC_1);
    }, (s.timeout * 1000));
}

function appxstoptimeout() {
    if (appx_session.interactT) clearTimeout(appx_session.interactT);
    appx_session.interactT = null;
}

function appxstartblurtimeout() {
    appxstopblurtimeout();
    appx_session.interactTblur = setTimeout(function setTimeoutCallback() {
        appxwidgetcallback(OPT_TAB_OUT);
    }, (1000));
}

function appxstopblurtimeout() {
    if (appx_session.interactTblur) clearTimeout(appx_session.interactTblur);
    appx_session.interactTblur = null;
}

function RowTextStruct() {
    var self = this;
    this.type = ROWTEXT_TYPE_NONE;
    this.boxid = 0;
    this.pos_row = 0;
    this.pos_col = 0;
    this.size_rows = 0;
    this.size_cols = 0;
    this.string = null;
    this.uline = false;
};

var ROWTEXT_TYPE_NONE = 0;
var ROWTEXT_TYPE_ITEM = 1;
var ROWTEXT_TYPE_WIDGET = 2;

/*Show popup for displaying about info or telling user that functionality has not
 *yet been implemented*/
function showPopup(functional) {
    if (functional) {
        var prefs =
            "<h1>About APPX</h1>" +
            "Server Connector Version: " + appx_session.getProp("serverConnectorVersionStr") + "</br>" +
            "Local Connector Version: " + appx_session.getProp("localConnectorVersionStr") + "</br>" +
            "Browser Client APPX Directory Version: " + appx_session.getProp("clientServerVersionStr") + "</br>" +
            "Browser Client Web Directory Version: " + appx_session.getProp("clientPublicVersionStr");

    } else {
        var prefs =
            "<h1>Functionality Not Currently Enabled</h1>" +
            "<p>Functionality for this option has not been enabled in this release of APPX.</p>";

    }

    var d = $("<div id='appx_popup'>")
        .css({
            "background": "rgba(50, 50, 50, 0.7)",
            "width": "100%",
            "height": "100%",
            "min-height": "220px",
            "z-index": "10000000",
            "display": "none",
            "position": "absolute",
            "top": "0px",
            "left": "0px",
            "font-family": "verdana",
            "font-size": "11px"
        })
        .appendTo("body");

    var prefwrap = $("<div style='border: 10px solid #333;'>")
        .css({
            "width": "550px",
            "height": "220px",
            "background": "#fff"
        })
        .appendTo("#appx_popup")
        .position({
            "my": "center",
            "at": "center",
            "of": window
        })
        .draggable();

    var prefsdiv = $("<div>")
        .css({
            "margin": "0px 10px",
            "width": "550px",
            "height": "220px"
        });

    var closer = $("<div>")
        .css({
            "background": "#333",
            "text-align": "right",
            "padding": "5px",
            "color": "#F5F539",
            "font-weight": "bold",
            "padding-bottom": "10px"
        })
        .append($("<span>close(X)</span>")
            .click(function $_click() {
                $("#appx_popup").hide();
                $("#appx_popup").remove();
                if (appxLocalRequired === "true") {
                    localos_session = new LOCALOS();
                }
            }));

    $(prefwrap).prepend($("<div>").append(closer));

    $(prefsdiv).append($("<div>").append(prefs));

    $(prefsdiv).appendTo(prefwrap);

    $("#appx_popup").show();
}
