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

// what_str =  "@(#)Appx $Header: /src/cvs/appxHtml5/server/appx-client-screen.js,v 1.289 2016/06/16 20:08:25 jnelson Exp $";

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

function appxmsgshandler(x) {
    appx_session.msgs = [];
    var i = 0;
    var $msgshtml = "";
    while (x.data.length > 0) {
        var msg = x.data.shift();
        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");
                    break;
                case 1:
                    $msghtml.addClass("status-msg-warning");
                    break;
                case 2:
                    $msghtml.addClass("status-msg-error");
                    break;
                case 3:
                    $msghtml.addClass("status-msg-cancel");
                    break;
            }
            appx_session.msgs.push($msghtml);
        }
    }
}

function applymessages() {
    var $newhtml = $("<div>");
    var msgcount = 0;
    for (var i = 0; i < appx_session.msgs.length; i++) {
        var $msghtml = appx_session.msgs[i];
        if (i > 0)
            $newhtml.append("<br>");
        $newhtml.append($msghtml);
    }
    $("#appx-status-msg").html($newhtml);
}

/*
**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
		 $("#screenBuf ." + key + "_pic").css({
		     "background-image" : "url('" + url +"')"
		 });
		 $("#screenBuf ." + key + "_ico").css({
		     "background-image" : "url('" + url +"')"
		 });
		 $("#screenBuf ." + key + "_img").attr("src", url);
		 if (url.indexOf("./images/missing.png") != -1) {
		     $("." + key + "_img").addClass("appx-image-missing");
		 }
		 $("#screenBuf .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) {
        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();
        appxApplyStylesEditor();
        appxApplyStylesHtmlViewer();
        appxApplyStylesTable();
	appxApplyStylesTitleButtons();
	appxApplyStylesSlider();
    }
    catch (ex) {
        console.log("applystyles: " + ex);
	console.log(ex.stack);
    }
}

function appxApplyStylesTitleButtons() {
    $.each( $("#screenBuf .appx-title-button-help"), function (k, el0) {
	$(el0).click(function (event) {
	    event.preventDefault();
	    appx_session.processhelp = true;
	    $.each( $("#screenBuf .appxfield"), function (k, e10) {
		$(e10).click(function (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 (k, el0) {
	$(el0).click(function (event) {
	    event.preventDefault();
	    appxwidgetcallback(OPT_ENTER);
	});
    });
    $.each( $("#screenBuf .appx-title-button-close"), function (k, el0) {
	$(el0).click(function (event) {
	    event.preventDefault();
	    appxwidgetcallback(OPT_END);
	});
    });
}

function appxApplyStylesCheckbox() {
    try {
	var browser = checkBrowser();
        $.each($("input[type=checkbox]"), function (k, el0) {
		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(event) {
				    event.preventDefault();
				})   
			    .click(function (event) {
				    event.preventDefault();
				});
		    }
		}
		else {
		    $(el0).data('checked', $(el0).checked)
			.keypress(function(event) {
				var el = $(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");
				    el.addClass("dirty");
				}
				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");
				    el.addClass("dirty");
				}
			    })   
			.click(function (e) {
				var el = $(this);
				if (el.data('checked') == 0 && el.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");
					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");
				}
			    });
		}
	});
    }
    catch (ex) {
        console.log("appxApplyStylesCheckbox: " + ex);
	console.log(ex.stack);
    }
}

function appxApplyStylesColorPicker() {
    try {
        //may need to loop and get ids here;
        $.each($("#screenBuf .appxcolorpicker"), function (i, el) {
            $(el).parent().width($(el).parent().width() + (appx_session.colWidthPx * 2) - 2);
            $(el).colpick({
                layout: 'hex',
                submit: true,
                colorScheme: 'light',
                onChange: function (hsb, hex, rgb, elc, bySetColor) {
                    $(elc).css('border-color', '#' + hex);
                    // Fill the text box just if the color was set using the picker, and not the colpickSetColor function.
                    if (!bySetColor) $(elc).val(hex);
                }
            }).keyup(function () {
                $(this).colpickSetColor(this.value);
            });
            //icon click opens colorpicker
            $(el).parent().find("img").colpick({
                layout: 'hex',
                submit: true,
                colorScheme: 'light',
                onChange: function (hsb, hex, rgb, elc, bySetColor) {
                    $(elc).parent().find("input").css('border-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);
                }
            });
        });
    }
    catch (ex) {
        console.log('appxApplyStylesColorPicker: ' + ex);
	console.log(ex.stack);
    }
}

function appxApplyStylesDate() {
    try {
        $.each($("#screenBuf .appxdatefield"), function (i, el) {
            $(el).width($(el).width() + (appx_session.colWidthPx * 2.5));
            var datevalue = $(el).find(".appxdatevalue").first();
            datevalue.css({
                "font-size": appx_session.basefontsize + "px"
            });
            var datepicker = $(el).find(".appxdatepicker").first();
            var parts = JSON.parse(datepicker.val());
            var opt = {
                beforeShow: function (inp, inst) {
                    appx_session.activeDatepicker = $(this);
                },
                buttonImage: ""+appxClientRoot+"/images/calendar.gif",
                buttonImageOnly: true,
                changeMonth: true,
                changeYear: true,
                dateFormat: parts.datemsk,
                onClose: function (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"
            };
            datepicker.datepicker(opt);
            datepicker.val(parts.value);
            $(el).find("img").css({
                "position": "absolute",
                "right": "2px",
                "top": "2px",
                "z-index": 100000
	    }).click(function(){
		appx_session.activeDatepicker = $(this);			    
	    });
        });
    }
    catch (ex) {
        console.log('appxApplyStylesDate: ' + ex);
	console.log(ex.stack);
    }
}

function appxApplyStyleEditor(cacheIDorEl, cacheID) {
    try {
	var el = [];
	if (cacheID){
	    el = $("#screenBuf").find("textarea." + cacheIDorEl);
	    if (el.length === 0){
		return;
	    }
	}else{
	    el[0] = cacheIDorEl;
	}
	for (var els = 0; els < el.length; els++){
	    $(el[els]).removeClass("ckeditor");
	    var twidth = $(el[els]).css("width");
            var theight = $(el[els]).css("height");
            // the height of the CKE editor greatly depends on the editor's config settings
            // We should plan on a better way to figure exact height rather than subtract the header height
	    
	
	    /* 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 = {};
	    if ($(el[els]).attr("disabled") == "disabled") {
		config.removePlugins = "elementspath,toolbar";
		config.allowedContent = true;
		config.resize_enabled = false;
		editor = CKEDITOR.replace($(el[els]).attr("id"), config);
	    }else if( $(el[els]).data("editorConfig")) {
	    config = AppxResource.cache[$(el[els]).data("editorConfig")];
		editor = CKEDITOR.replace($(el[els]).attr("id"), {customConfig: appx_session.image_cache[cacheIDorEl].url});
	    }else{
		editor = CKEDITOR.replace($(el[els]).attr("id"), {customConfig: '../custom/appx-ckeditor-config.js'});
	    }
	    $(el[els]).attr("name",editor.name);
	    var funcVal = els;
            editor.on('instanceReady', function () {
		$("#cke_"+$(el[funcVal]).attr("id")).find("iframe").attr("title","");
		$("#cke_"+$(el[funcVal]).attr("id")).css({
		    "position": "absolute",
		    "top": $(el[funcVal]).css("top"),
		    "left": $(el[funcVal]).css("left"),
		    "z-index": $(el[funcVal]).css("z-index")
		});
		setTimeout(function() {
                    editor.resize(
			( parseInt(twidth) -4 
			), 
			( parseInt(theight) +2
			), false);
		},0);
	    });
	    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 (i, el) {
		if( $(el).data("editorConfig") && !AppxResource.cache[$(el).data("editorConfig")] ) {
		    // do nothing yet, it will load after the resource arrives
		}
		else {
		    appxApplyStyleEditor(el, false);
		}
	});
    }
    catch (ex) {
        console.log('appxApplyStylesEditor: ' + ex);
	console.log(ex.stack);
    }
}

function appxApplyStylesHtmlViewer() {
    try {
        $.each($("#screenBuf .appx-html-viewer a"), function (k, el0) {
            var opt = $(el0).attr("href");
            if (!isNaN(opt)) {
                $(el0).attr({
                    "href": "#"
                }).click(function (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 (i, el) {
	    var coldata = appx_session.currenttabledata[$(el).attr("id")];
            var gridprops = null;
            if (appx_session.hasOwnProperty("gridpropscache")){
                gridprops = appx_session.gridpropscache[$(el).data("tableHashKey")];
	    }
	    if(coldata) {
		if (gridprops) {
		    coldata.scrollTop = gridprops.scrollTop;
		    coldata.scrollLeft = gridprops.scrollLeft;
		    coldata.lastSortOrder = gridprops.sortorder;
		    coldata.lastSortName = gridprops.sortname;
		    coldata.rowCount = gridprops.rowCount;
		    coldata.colCount = gridprops.colCount;
		    coldata.colModel = gridprops.colModel;
		    coldata.collist = gridprops.collist;
		    coldata.filters = gridprops.filters;
		    coldata.floatcol = gridprops.floatcol;
		    coldata.lastPage = gridprops.lastPage;
		    coldata.showRowNumbers = gridprops.showRowNumbers;
		    coldata.showFooterBar = gridprops.showFooterBar;
		    if (gridprops.lastSortColumns.length > 0){
			coldata.lastSortColumns = gridprops.lastSortColumns;
		    }
		}
		createGridMongo(coldata, $(el).attr("id"));
	    }
    	    $(el).height((parseInt($(el).height())+appx_session.rowHeightPx)+"px");

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

    catchEnterKeyInGridPager();
}

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

function appxApplyStylesSlider() {
    try {
        $.each($("#screenBuf .slider"), function (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(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);
    }
}

function catchEnterKeyInGridPager() {
    $('input.ui-pg-input').keydown(function(e){
	    e.stopPropagation();
		});
}

//Attributes Message Handler
function appxattributeshandler(x) {}

//Extra Attributes Message Handler
function appxextraattributeshandler(x) {}

function attachMouseListener(obj) {
    obj.mousedown(function(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 = "";
    //TODO? not adding screenBuf to the DOM might be faster because of reflow (replaceWith)
    if ($_screenBuf == null) {
        var $screen = $("#appx_main_container").empty();
        $_screenBuf = $screen.clone();
        $_screenBuf.attr("id", "screenBuf");
        $_screenBuf.appendTo($screen.parent());
        $_screenBuf.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.rowwidthpx;
    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"
            });
            $tag.css({
            "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;
        $tag.css({
            "top": (parseInt($tag.css("top")) + topAdjPx) + "px"
        });
        $tag.css({
            "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 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;
        appx_session.topbox = null;
        appx_session.tablist = null;
        for (var i = 0; i < appx_session.current_show.boxes.length; i++) {
            inProgressBox = null;
            var box = appx_session.current_show.boxes[i];
            //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 == 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;
            }
            if (appxIsScrollReg(box)) {
                box3 = null;
                box2 = null;
                box1 = null;
            }
            else {
                box3 = box2;
                box2 = box1;
                box1 = box;
            }
            if (box3 != null) {
                if (box3.begin_column == box2.begin_column && box2.begin_column == box1.begin_column) {
                    if (box3.end_column == box2.end_column && box2.end_column == box1.end_column) {
                        if (box1.end_row >= (box2.begin_row - 1) && box3.end_row >= (box1.begin_row - 1)) {
                            if (appxIsScroll(box1) && box3.bit_mask == box2.bit_mask) {
                                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) {
                if (box2.begin_column <= box1.begin_column) {
                    if (box2.end_column >= box1.end_column) {
                        if ((box2.begin_row - 1) <= box1.end_row && box2.end_row >= (box1.end_row)) {
                            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 && box1.widget.wLabel == null ) {
				    if( box1.widget.wBorder == null ) {
				    box1.newbox = i - 1;
				    appxMergeBoxes(box1, box2);
				    box1 = box2;
				    box2 = box3;
				    }
				}
			    }
                        }
                    }
                }
            }
            if (!(appxIsScroll(box) || appxIsScrollReg(box))) {
                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;
        var scrollBoxNo = 0;
        var boxLayer = 0;
        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)){
		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;
            if (!(appxIsScroll(box) || appxIsScrollReg(box))) {
                if (box.rowtext && box.rowtext.length > 0) {
                    var title = "";
                    for (var j = 0; j < box.rowtext.length; j++) {
                        var rowtxt = box.rowtext[j];
                        if (rowtxt.pos_row == 1 ) {
                            title += rowtxt.string.trim() + " ";
			    if(rowtxt.isTitle){
				rowtxt.string = "";
			    }
                        }
                    }
                    if (title.length > 0 && box.widget.wLabel == null)
                        box.widget.wLabel = title;
                }
            }

            var boxwidth = ((box.end_column - box.begin_column) * cel.w) + (3 * cel.w) + 2;
            var boxheight = ((box.end_row - box.begin_row) * cel.h) + (3 * cel.h);
            var boxtop = (box.begin_row * cel.h) - cel.h;
            var boxleft = (box.begin_column * cel.w) - cel.w;
            if (appxIsScroll(box)) {
                boxleft += 3;
                boxwidth -= 6;
            }
            if (appxIsScrollReg(box)) {
                boxleft += 3;
                boxwidth -= 6;
            }
            var $boxhtml2 = null;
            if (box.widget.wLabel && box.widget.wLabel != "") {
                var m = 2; //margin
                var t = m;
                var l = m;
                var w = (boxwidth - (m * 4)) + "px";
                var h = (cel.h) + "px";
                if (appxFillWindow == "true" && box.end_column - box.begin_column + 1 == appx_session.screencols) {
                    w = "100%";
                    t = "0px";
                    l = "0px";
                }
                var $boxhtml2 = $("<div>").css({
                    "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());
		box.widget.isBox = false;
                appxwidgetshandlerpropsex(box.widget, $boxhtml2, true);
            }
            //</toolbar>
            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%";
            }

            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.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;
            appxwidgetshandlerprops(box.widget, $boxhtml);

            //store box between items for tabindexing
            $_screenBuf.append($boxhtml); //"#appx_main_container"
            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 ||
                    //TODO this doesn't work when there are buttons on a footer frame box
                    //-> look at box attributes, ignore scrolling regions?
                    (_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
                    $boxhtml.height(boxheight - (2 * cel.h));
                }
                else if (appxIsScrollReg(box)) {
                    if (appxIsScrollAct(box)) {
                        $boxhtml.addClass("appx-scroll-act");
			$boxhtml.removeClass("appxbox");
                        bPrevScrollAct = true;
                    }
                    else {
                        if (bPrevScrollAct) {
                            bPrevScrollAct = false;
			    $boxhtml.removeClass("appxbox");
                            $boxhtml.addClass("appx-scroll-act-next");
                        }
                        else {
                            $boxhtml.addClass("appx-scroll-reg");
			    $boxhtml.removeClass("appxbox");
                        }
                    }
		    $boxhtml.data("col", box.begin_column);
                    $boxhtml.data("row", box.begin_row);
                    $boxhtml.click(appxScrollClick);
                    $boxhtml.css("top", boxtop + cel.h); //remove the -1 offset row
                    $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: false,
		    drop: function () {
			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"));
			}
		    }
		});
	    }
	    

	    
            $boxhtml.dblclick(function () {
		    if (appx_session.getProp("appxDoubleClick") == "true")
			appxwidgetcallback(OPT_ENTER);
            });
            $("a").each(function () {
                $(this).attr("tabindex", -1);
            });
            $("button").each(function () {
                $(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;
	    }
            appxshowitemsforbox(box, topBoxIdx == scrollIdx);
            appxshowwidgetsforbox(box, topBoxIdx == scrollIdx);
            appxshowrowtextforbox(box);
            if (!(appxIsScroll(box) || appxIsScrollReg(box))) {
                if (topBoxIdx != i) {
                    $boxhtml.addClass("appx-not-modifiable");
                }
            }
            boxcnt++;
        }

        if (inProgressBox && !blocked) {
            try {
                blocked = true;
                $("#main").block({
                    message: inProgressBox.widget.wLabel + "...",
                    overlayCSS: {
                        backgroundColor: null,
                        opacity: null,
                        cursor: "wait"
                    },
                    centerY: false,
                    css: {
                        "z-index": 10000
                    },
                    blockMsgClass: "appx-in-progress-msg"
                });
            }
            catch (e) {}
        }
    }
    catch (ex) {
        console.log("appxshowboxes: " + ex);
	console.log(ex.stack);
    }
}

//Display.drawCursor
function appxshowcursor(bFocus) {

    try {
	var bCurItem = false; //cursor inside field?
        var cel = appxGetCellSize();
        var pos = appxGetCursorPos();
	var box = appxfindbox(pos.row, pos.col, 1, 1, true);
	//var box = appxfindbox(pos.row, pos.col, 1, 1, true);
        if (!box) return;
        var $cur = $("#appxCursor");
        var firstModItem = null;
        if ($cur.length == 0) {
            $cur = $("<div>");
            $cur.attr("id", "appxCursor");
            $cur.css({
                "background-color": "rgba(0,0,0,.5)",
                "height": cel.h,
                "position": "absolute",
                "width": cel.w,
                "z-index": "100000"
            });
            $("#appx_main_container").append($cur);
        }
        $cur.css({
            "left": (pos.col * cel.w),
            "top": (pos.row * cel.h),
        });

	$(".appx-scroll-act-new").removeClass("appx-scroll-act-new");
	if( appxIsScrollReg(box) && !appxIsScrollAct(box) ) {
	    $("#box_" + box.widget.wBoxNumber.toString()).addClass("appx-scroll-act-new");
	}

        //TODO: can get this faster through query, see appxinvokehandler()
        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;

                    //TODO: when we press left, the cursor is already outside the field and bCurItem test fails
                    var pc = 0;
                    if (appx_session.keyLeft) {
			pc = 1;
                    }

                    if (
			appxIsModifiable(item[0]) &&
			    pos.row >= item[0].pos_row &&
			    pos.row <= (item[0].pos_row + item[0].size_rows - 1) &&
			    pc + pos.col >= item[0].pos_col &&
			    pos.col <= (item[0].pos_col + item[0].size_cols - 1)
                    ) {
			bCurItem = true;
			if ($($tag).hasClass("appxdatefield") && $($tag).find(".appxdatevalue")){
			    $tag = $($tag).find(".appxdatevalue");
			}
			$tag.focus();
			break;
                    }
		}
	    }
        }

	if (appxIsLocked() && !bCurItem) {
	    var itmtag = null;
	    box = appxfindbox(pos.row, pos.col, 1, 1, false);
	    
	    for (var i = 0; i < box.items.length; i++) {
		var item = box.items[i];
		if (item[1] != null) {
		    var $tag = item[1];

		    if (item[0].hasOwnProperty("pos_row")) { //Item, not box		    
			if (appxIsModifiable(item[0])) {
			    itmtag = $tag;
			}
		    }
		}
	    }

	    if (itmtag) {
		itmtag.focus();
	    }
	}

        $cur.css("visibility", ((bCurItem || appxIsLocked() || (appx_session.getProp("drawBlockCursor") != "true" && !appx_session.showCursor)) ? "hidden" : "visible"));
        return bCurItem;
    }
    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 {
        // 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);
            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];
            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);
                $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 rowtxt = appx_session.rowtext[i];
	    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);
		    }
		}
	    }
	    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 - 1;
		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 () {
		var newid = $(this).attr("id") + "-stale";
		$(this).attr("id", newid);
	    });
        appxnestwidgets();
        appxshowboxes();
        appxprepscreen();
	setTimeout(function(){
		appxSetTabIndexes();
	    }, 0);
	
	AppxResource.sendHeld();

        screenflipped = false;
        if (appx_session.getProp("doubleBufferScreen") == false || appx_session.pendingResources.length == 0 || ( Math.abs(show.curraction[0] & M_WAIT) == 0) ){
              appxshowscreen();
	}

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

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

    while (box.rowtext.length > 0) {
        var rowtxt = box.rowtext.pop();
        var layer = 0;
        var boxNo = box.widget.wBoxNumber;
        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) {
            equalRow = rowtxt.pos_row;
            continue;
        }
        if (equalRow > 0 && equalRow == rowtxt.pos_row + 1) {
            headingOne = rowtxt.pos_row;
            rowtxt.pos_row++;
        }
        if (equalRow > 0 && headingOne > 0 && equalRow == rowtxt.pos_row + 2) {
            headingTwo = rowtxt.pos_row;
            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 = 0;
		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());
            $tag.text(rowtxt.string.replace(/[\xb6\x0d\x0a]/g, " "));

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

            var $box = $("<div>").addClass("appx--box"); //container
            //var clsText = TEXT_LEFT;
            //$box.addClass(clsText).appendTo($tag);
            $box.appendTo($tag);

            $tag.css({
                "position": "absolute",
                "left": x,
                "top": y,
                "height": h,
                "width": w,
                "z-index": -20
            });

            if (box != null) {
                var $box = $("#screenBuf #box_" + boxNo);
                if ($box.length) $box.append($tag);
            }
        }
    }
}

function appxshowitemsforbox(box, isActiveBox) {
    //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;

        clearBoxRowText(box, item.pos_row, item.pos_col, item.size_rows, item.size_cols);

        if (!appxIsScrollReg(box)) {
            boxtop = box.begin_row;
        }

        var $tag = box.items[i][1];
	var $box = $_screenBuf;
        if (isActiveBox) appxSetTabElement(item, $tag);

        if (item.pos_row - boxtop >= 0) {

            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(appxScrollClick);
            }
        }

	/*If item is a list box, 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")){
	    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){
		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 = 5 * 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) && (initWidth > minCol) ) {
			/*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) + "px";
			$tag.width(w);
		    } else {
			var attrs = {};
			
			//retrieve all the attributes of the original tag
			$.each($tag[0].attributes, function(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 
			});
		    }
		}
	    }
	}
	
	$box.append($tag);
        $tag.css("z-index", (box.layer) - 10);

        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 ) {
		
		$("<img>")
		    .attr("src", ""+appxClientRoot+"/images/scanicon.png")
		    .css({
			"left": scanLeft,
			"position": "absolute",
			"top": scanTop,
			"z-index": $tag.css("z-index")
		    })
		    .click({
			col: item.pos_col,
			row: item.pos_row,
			tag: $tag
		    }, function (e) {
			appxPutCursor(e.data.col, e.data.row);
			appxwidgetcallback(OPT_SCAN);
		    })
		.appendTo($box);
	    }
        }
    }
}

//double buffering
var $_screenBuf = null; //appx_session?
function appxshowscreen() {
    if( screenflipped )
	return;
    screenflipped = true;
    var $screen = $("#appx_main_container");
    $screen.attr("id", "screenBuf");
    $screen.empty();
    $screen.hide();
    
    $_screenBuf.attr("id", "appx_main_container");
    if( $_screenBuf.enhanceWithin )
	$_screenBuf.enhanceWithin();
    
    $_screenBuf.show();
    
    $_screenBuf = $screen;
    
    appxshowcursor(true);
    setTimeout(function(){
	appxSetLocked(false);
    },0);
}

function appxprepscreen() { //flip
    //TODO: appx_main_container should become a flexible container id (e.g. new Appx('#mydiv'))

    createDropDownMenu();
    createPopupMenu();
    createdToolbarMenu();

    applymessages();
	//Using context menu library to add popup menus to screen
	if (!(jQuery.isEmptyObject(appx_session.currentmenuitems.popupItems))){
	    $.contextMenu("destroy");
	    $.contextMenu({
		selector: '#appx_main_container',
		zIndex: 20000,
		items: appx_session.currentmenuitems.popupItems
	    });
	}

    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;
        $.each($("#screenBuf .appx-scroll"), function (i, el) {
            vcrcount++;
	    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({
                "position": "absolute",
                "z-index": zi,
                "top": (parseInt($(el).css("top")) + 0) + "px",
                "left": (parseInt($(el).css("left")) + parseInt($(el).css("width")) - 16) + "px",
                "width": "16px",
                "height": $(el).css("height")
            });

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

            var extra = Math.max(parseInt($(el).css("height")) - (16 * 6), 16) - 0;
            $grouphtml.html("");
            $grouphtml.append($("<button type='button'>").addClass("appx-vcr-btn").addClass("appx-vcr-up3").click(function ( event ) {
                event.preventDefault();
                appxwidgetcallback(OPT_SCROLL_FIRST);
                return false;
            }));
            $grouphtml.append($("<button type='button'>").addClass("appx-vcr-btn").addClass("appx-vcr-up2").click(function ( event ) {
                event.preventDefault();
                appxwidgetcallback(OPT_SCROLL_PREV);
                return false;
            }));
            $grouphtml.append($("<button type='button'>").addClass("appx-vcr-btn").addClass("appx-vcr-up1").click(function ( 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 ( event ) {
                event.preventDefault();
                appxwidgetcallback(OPT_KEY_ENTRY);
                return false;
            }));
            $grouphtml.append($("<button type='button'>").addClass("appx-vcr-btn").addClass("appx-vcr-down1").click(function ( event ) {
                event.preventDefault();
                appxwidgetcallback(OPT_SCROLL_UP);
                return false;
            }));
            $grouphtml.append($("<button type='button'>").addClass("appx-vcr-btn").addClass("appx-vcr-down2").click(function ( event ) {
                event.preventDefault();
                appxwidgetcallback(OPT_SCROLL_NXT);
                return false;
            }));
            $grouphtml.append($("<button type='button'>").addClass("appx-vcr-btn").addClass("appx-vcr-down3").click(function ( event ) {
                event.preventDefault();
                appxwidgetcallback(OPT_SCROLL_LAST);
                return false;
            }));

            $vcrhtml.html($grouphtml);
            $("#screenBuf").append($vcrhtml);
        });
    }
}







function intersects(left1, right1, top1, bottom1, left2, right2, top2, bottom2) {
    return !(left2 > right1 ||
        right2 < left1 ||
        top2 > bottom1 ||
        bottom2 < top1);
}

var spaces = "                                                  " +
    "                                                  " +
    "                                                  " +
    "                                                  " +
    "                                                  ";

function clearBoxRowText(box, r, c, h, w) {
    for (var ii = 0; ii < box.rowtext.length; ii++) {
        var rowtxt = box.rowtext[ii];
        if (intersects(rowtxt.pos_col, rowtxt.pos_col + rowtxt.size_cols - 1, rowtxt.pos_row, rowtxt.pos_row + rowtxt.size_rows - 1, c, c + w - 1, r, r + h - 1)) {
	    if (rowtxt.pos_row == 1 && rowtxt.string.trim() == "Standard Toolbars")
                continue;
            var colStart = Math.max(rowtxt.pos_col, c) - rowtxt.pos_col;
            var colEnd = Math.min(rowtxt.pos_col + rowtxt.size_cols, c + w) - rowtxt.pos_col;
            var newStr = (colStart > 0 ? rowtxt.string.substring(0, colStart) : "") +
                (spaces.substring(0, colEnd - colStart)) +
                (colEnd < rowtxt.string.length ? rowtxt.string.substring(colEnd) : "");
            rowtxt.string = newStr;
        }
    }
}

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 (parseInt(wx.wWidgetType) != WIDGET_TYPE_BOX)
	    clearBoxRowText(box, wx.wPositionY, wx.wPositionX, wx.wSizeH, wx.wSizeW);
	    

        $("#screenBuf #box_" + boxno).append($tag);
        if (wgt[0].wMovable) {
            $tag.data({
                "wMovableCommand": wgt[0].wMovableCommand
            })
            $tag.draggable({
                cancel: false,
                drop: function () {
                    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.prop("disabled", true);
                $tag.css({
                    "color": ""
                });
            }
            $tag.removeClass("default"); //can be a button on an underlying frame
        }

    }
}

/*
**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 (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) {
    try {
        var ti = (parseInt($tagSrc.attr("tabindex")) % 100);
        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 (el.hasClass("appxdatefield") && el.find(".appxdatevalue"))
	    el = el.find(".appxdatevalue");
	
        el.focus(); //always for buttons

    }
    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 ($tag, tabGrp) {
                tablist.push($tag);
		$tag.attr("tabindex", (tabGrp + (tablist.length -1)) );
                $tag.on("keydown", function (ke) {
                    if (ke.which == 9) { //tab
                        appxSetTabFocus($(this), ke.shiftKey);
                    }
                    else if (ke.which == 13 && $(this).is("button")) { //enter
                        $(this).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 (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)) {
                                    var max = $(this).attr("maxlength");
                                    if (max && max <= $(this).val().trim().length) {
                                        if (appx_session.getProp("autoTabOut")) {
                                            appxSetTabFocus($(this)); //select next item
                                        }
                                    }
                                }
                            }
                        }
                        catch (ex) {
                            alert("$tag keypress: " + ex);
			    console.log(ex.stack);
                        }
                        return true; //bubble
                    });
                }
            }; //pushTabElement

            // TODO? should we check by rows and columns due to micro-positioning?
            var sortTabElements = function (objA, objB) {
	
                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 (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" ){
				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)){
				    var arrayLength = tab[lvl][KEYS].length; 
				    var arrayCount = 0;
				    var splitKey = KEYS + "-split"
				    for (var i = 0; i < tab[lvl][KEYS].length; i++) {
					if ((sortTabElements(tab[lvl][keys][0],tab[lvl][KEYS][i]) < 0)){
					    arrayCount = i;
					    break;
					}
				    }
				    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;
				    }
				    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
/* TShow -> MDisplay
  public byte[] terminal = new byte[4];  0
  public byte mode; 0 0 0                4
  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
 */
function sendappxshow(option, data) {
    appx_session.showCursor = false;
    appxSetStatusStateText(APPX_STATE_BUSY);
    try {
        $(".appxtablewidget").each(function () {
            var uigrid = $("#newtablewidget_" + $(this).attr('id'));
	    /*Make sure we have a grid before trying to get data from grid*/
	    if (uigrid.getGridParam() !== null){
		var selrows = uigrid.getGridParam("selarrrow");
		var selkeys = [];
		var numrows = uigrid.getGridParam("records");
		var rowcount = uigrid.getGridParam("postData").rowCount;
		var colcount = uigrid.getGridParam("postData").colCount;
		var sortname;
		var sortorder;
		var collist = uigrid.getGridParam("postData").collist;
		var colmodel = uigrid.getGridParam("postData").colModel;
		var filters = uigrid.getGridParam("postData").filters;
		var lastPage = uigrid.getGridParam("page");
		var lastSortColumns = uigrid.getGridParam("remapColumns");
		var uigridbdiv = $(this).find(".ui-jqgrid-bdiv");
		var showRowNumbers = uigrid.getGridParam("rownumbers");
		var showFooterBar = uigrid.getGridParam("postData").showFooterBar;
		var floatcol = uigrid.getGridParam("shrinkToFit") === false ? 0 : 1;
		for (var i = 0; i < selrows.length; i++) {
		    selkeys.push(selrows[i]);
		}
		$(this).data("selkeys", selkeys);
		if (uigridbdiv) {
                    var gridprops = null;

                    if (!gridprops) {
			gridprops = new GridCacheProps();
                    }
		    var coldata = appx_session.currenttabledata[$(this).attr("id")];
		    if(coldata) {
			coldata.scrollTop = uigridbdiv.scrollTop();
			coldata.scrollLeft = uigridbdiv.scrollLeft();
			sortname = coldata.lastSortName;
			sortorder = coldata.lastSortOrder;
		    }
		    var tempSortname = uigrid.getGridParam("sortname");
		    var tempSortorder = uigrid.getGridParam("sortorder");
		    if (sortname && sortname !== undefined){
			var inSortN = false;
			for (var i = 0; i < sortname.length; i++){
			    if ( tempSortname === sortname[i]){
				if ( (tempSortname !== "initialSort") &&
				     (i === sortname.length -1)){
				    sortorder[i] = tempSortorder;
				}
				inSortN = true;
			    }
			}
			if (!inSortN){
			    sortname.push(tempSortname);
			    sortorder.push(tempSortorder);
			}
		    }else {
			sortname = [tempSortname];
			sortorder = [tempSortorder];
		    }
		    
		    if(gridprops) {
			gridprops.scrollTop = uigridbdiv.scrollTop();
			gridprops.scrollLeft = uigridbdiv.scrollLeft();
			gridprops.floatcol = floatcol;
			gridprops.colModel = colmodel;
			gridprops.collist = collist;
			gridprops.rowCount = rowcount;
			gridprops.colCount = colcount;
			gridprops.sortname = sortname;
			gridprops.sortorder = sortorder;
			gridprops.filters = filters;
			gridprops.lastPage = lastPage;
			gridprops.records = numrows;
			gridprops.lastSortColumns = lastSortColumns;
			gridprops.showRowNumbers = showRowNumbers;
			gridprops.showFooterBar = showFooterBar;
			appx_session.gridpropscache[$(this).data("tableHashKey")] = gridprops;
		    }
		}
	    }
        });

        //TODO? is the option 0 check needed?
        if (appxIsLocked() && option != 0 && option != 0xFFFF) 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 = rtnshow.rawdata[4];

            //have to set return status in 0xC message to indicate and option was selected
            if (option >= 0) rtnshow.rawdata[35] = 1;

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

            //20140314 chris@praclox.nl cursor
            var cur = appxGetCursorPos();
            dv.setUint32(12, appxFixCursorRow(cur.row));
            dv.setUint32(16, appxFixCursorCol(cur.col));
            dv.setUint32(28, parseInt(option));

            if (mode == 2 || mode == 32) {
		dv.setUint32(32, parseInt("8"));
		dv.setUint32(28, parseInt("65535"));
	    }

            dv.setInt32(44, 0);
            dv.setInt32(48, 0);
            dv.setUint32(52, 0);

            if (rtnshow.altuseroption) { // swap current cusrsor and option with alt cursor and option

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

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

            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 ) { //  || data.length > 0) {
            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 = "";
                // 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
		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]).val())
		    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();
		
                if ($(data[dl]).css("text-transform") == "uppercase") 
                    dataval = dataval.toUpperCase();

                dataval = dataval.replace("\u2018","'");
                dataval = dataval.replace("\u2019","'");
                dataval = dataval.replace("\u201C",'"');
                dataval = dataval.replace("\u201D",'"');

                var id = $(data[dl]).closest(".appxitem").attr('id');
		if( id == "" || !id ) {
		    id = $(data[dl]).closest(".appxitem").closest("div").attr('id');
		}
                var ar = getClientId(id).split('_');
                var datarow = parseInt(ar[2]);
                msgdata.push(datarow);
                var datacol = parseInt(ar[1]);
                msgdata.push(datacol);

                var u8strarray = toUTF8Array(dataval);
		var byte2 = u8strarray.length % 256;
		var byte1 = (u8strarray.length - byte2) / 256;
                msgdata.push(byte1);
                msgdata.push(byte2);
                Array.prototype.push.apply(msgdata,u8strarray);
            }

	    clearClientIds();

            var msglength = [0, 0, 0];
            msglength.push(data.length);

            ms = {
                cmd: 'appxmessage',
                args: msglength,
                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];

                    //ROW - need 4bytes BE
                    var datarow = [0, 0, 0, parseInt(dataobj.row)];
                    Array.prototype.push.apply(msgdata,datarow);
                    //COL - need 4bytes BE
                    var datacol = [0, 0, 0, parseInt(dataobj.col)];
                    Array.prototype.push.apply(msgdata,datacol);
                    //SEQ - need 4bytes BE
                    var seq = [0, 0, 0, keycnt + 1];
                    Array.prototype.push.apply(msgdata,seq);
                    //alway a 6
                    msgdata.push(6);

                    msgdata.push(dataval.length / 2);
                    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 = [0, 0, 0];
        msglength.push(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],
            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));

        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(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(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));

            // 4 byte int size (this is where we really need more than cb)
            ms = {
                cmd: 'appxmessage',
                args: hton32(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(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(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_AROUND = 0x80000000;
//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*/

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 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(true); //don't call focus
            }
        }
        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() {
    if (!$(this).hasClass("appx-scroll-act")){
	var col = $(this).data("col");
	var row = $(this).data("row");
	if (!col || !row) {
	    return;
	}
	appxPutCursor(col, row);
	appxwidgetcallback(OPT_ENTER);
    }

}

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 appxSetStatusDateText(str) {
    $("#appx_status_date").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("");
}

function appxSetStatusText(str) {
    str = str.trim();
    var $bar = $("#appx-status-msg");
    if ($bar.length > 0) {
        var clr = (str == "" ? "white" : "lime");
        if (str != "") {
            if (str.indexOf("Error - ") != -1) clr = "red";
            else if (str.indexOf("Warning - ") != -1) clr = "yellow";
        }
        if (str != "") {
            $bar.prepend(str + "\n\n");
        }
    }
}

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 () {
        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 () {
        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;

function createAppxGrid(elem) {
    var sizeH = appx_session.rowHeightPx;
    var sizeW = 8;
    var ratioW = Math.floor($(window).width()/sizeW),
        ratioH = Math.floor($(window).height()/sizeH);
    
    var parent = $('<div />', {
	    class: 'grid', 
	    width: ratioW  * sizeW, 
	    height: ratioH  * sizeH
	}).addClass('grid').appendTo(elem);

    for (var i = 0; i < ratioH; i++) {
        for(var p = 0; p < ratioW; p++){
            $('<div />', {
		    width: sizeW - 1, 
			height: sizeH - 1
			}).appendTo(parent);
        }
    }
}

/*Show popup for displaying about info or telling user that functionality has not 
 *yet been imlemented*/
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 () {
		    $("#appx_popup").hide();
		    $("#appx_popup").remove();
		    localos_session = new LOCALOS();
		}));

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

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

    $(prefsdiv).appendTo(prefwrap);

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