zk.load('zul,zul.wnd,zk',function(){if(zk._p=zkpi('ppi'))try{
(function() {
    function _cancelDelayedResize(wgt) {
        if (wgt && wgt._currentResize) {
            wgt._currentResize.unlisten();
            delete wgt._currentResize;
        }
    }
    
    var _timeoutCallbacks = {}

ppi.util = {
    STYLES: {
        INPUT_FOCUSED: 'focused',
        INPUT_INVALID: 'invalid',
        INPUT_HOVERED: 'hovered',
        INPUT_OPENED: 'opened'
    },

    /**
     * Registers a callback with a given timeout for a specific id. Each call
     * will clear/unregister the old timeout callback registered with the same
     * id.
     *
     * @param id ID to identify the registered callback.
     * @param callback Callback function to register.
     * @param timeoutMillis Timeout in milliseconds after which the callback is called.
     * @memberOf ppi.util
     */
    registerTimeoutCallback : function(id, callback, timeoutMillis) {
        var timeout = 300;
        if (timeoutMillis) {
            timeout = timeoutMillis;
        }
        ppi.util.unregisterTimeoutCallback(id);
        _timeoutCallbacks[id] = setTimeout(callback, timeout);
    },

    /**
     * Unregisters a potentially registered timeout callback.
     *
     * @param id ID of the callback
     * @memberOf ppi.util
     */
    unregisterTimeoutCallback : function(id) {
        if (_timeoutCallbacks[id]) {
            clearTimeout(_timeoutCallbacks[id]);
        }
    },

    /**
     * Executes the provided callback if onCommandReady event is fired at given widget.
     *
     * @param wgt Widget instance
     * @param callback callback to process
     * @memberOf ppi.util
     */
    doOnCommandReady: function(wgt, callback) {
        var listenerObj = {
            onCommandReady: function() {
                callback();
                zWatch.unlisten({onCommandReady: listenerObj});
            }
        }
        zWatch.listen({onCommandReady: listenerObj}); /* will call listenerObj.onCommandReady directly*/
    },

    /**
     * Executes the provided callback if onResponse event is fired at watch level.
     * @param callback callback to process
     *
     * @memberOf ppi.util
     */
    doOnAfterResponse: function(callback) {
        var listener = {
            onResponse: function() {
                callback();
                zWatch.unlisten({onResponse: listener});
            },
            unlisten: function() {
                zWatch.unlisten({onResponse: listener});
            }
        }
        zWatch.listen({onResponse: listener});
        return listener;
    },

    delayedScrollIntoView: function(uuid, viewport) {
        ppi.util.doOnAfterResponse(function() {
            var wgt = zk.$(uuid)
            var vp = zk.$(viewport)
            if (!wgt) return
            if (vp) {
                vp = (vp.className === 'zul.grid.Grid') ? vp.ebody : vp.$n();
                var dim = wgt.$n().getBoundingClientRect();
                if (dim.top >= 0 && dim.bottom <= (vp.innerHeight || vp.clientHeight)) {
                    return;
                }
            }
            zAu.cmd0.scrollIntoView(wgt)
        })
    },

    /**
     * This method registers a resize-event at the end of the current execution.
     *
     * If changing visibility of components on the server side and request a Clients.resize afterwards,
     * the order of execution on the client is strangely the other way round (first resize, then show/hide).
     * To avoid this, zUtl.fireSized will be called after the current execution via setTimeout.
     * This method should only be called by the server via evalJavascript.
     *
     * @param widgetUuid Uuid of the widget to request the resize.
     * @memberOf ppi.util
     */
    registerDelayedResize: function(uuid) {
        var wgt = zk.$(uuid);
        if (!wgt) return;
        
        _cancelDelayedResize(wgt);
        wgt._currentResize = ppi.util.doOnAfterResponse(function() {
            var timeoutId = setTimeout(zAu.cmd1.resizeWgt.bind(this, wgt), 10)
            wgt._currentResize = {
                unlisten: function () {
                    clearTimeout(timeoutId);
                }
            }
        });
    },

    debug: function(msg) {
        var out = msg
    },

    error: function(msg) {
        var out = msgzk.EXCEPTION_PREFIX + msg + msgzk.EXCEPTION_POSTFIX
        zk.error(out);
    },

    // disables drag functionality on dialogs (Trac #2042, ZK #3058)
    disableDraggableOnWindow: function (uuid) {
        var wnd = zk.$(uuid);
        if (wnd && wnd._drag) {
            wnd._drag.destroy();
            wnd._drag = null;
            var cap = wnd.$n('cap');
            cap.style.cursor = 'default';
        }
    },

    // enables drag functionality on dialogs after it was disabled (Trac #2042, ZK #3058)
    enableDraggableOnWindow: function (uuid) {
        var wnd = zk.$(uuid);
        if (wnd) {
            var cap = wnd.$n('cap');
            if (cap && !wnd._drag) {
                wnd._drag = new zk.Draggable(wnd, null, {
                    handle: cap, stackup: true,
                    fireOnMove: false,
                    starteffect: wnd.$class._startmove,
                    ghosting: wnd.$class._ghostmove,
                    endghosting: wnd.$class._endghostmove,
                    ignoredrag: wnd.$class._ignoremove,
                    endeffect: wnd.$class._aftermove,
                    zIndex: 99999
                });
                cap.style.cursor = 'move';
            }
        }
    },

    addClass: function(jqobj, css) {
        if (jqobj && !jqobj.hasClass(css)) jqobj.addClass(css);
    }
}
})();
(function() {
    /**
     * Checks whether a tuplet (x1, y1) is inside the boundaries of (x2,y2) and (width, height).
     * @memberOf ppi.popup.private
     */
    function _isOutside(x1, y1, x2, y2, width, height) {
        return x1 < x2 || x1 > x2+width || y1 < y2 || y1 > y2+height;
    }

    /**
     * Sets the popup's pointer to the correct side.
     * @memberOf ppi.popup.private
     */
    function _setPointer(popup, refPos, refWidth, refHeight, gap, offset) {
        var pp = jq('#' + popup);

        var ppWidth = pp.outerWidth();
        var ppHeight = pp.outerHeight();
        var ppPos = pp.offset();

        _removePointer(popup);

        if ((ppPos.left <= refPos.left
            && ppPos.left + ppWidth >= (refPos.left + refWidth)
            && ppPos.top <= refPos.top
            && ppPos.top + ppHeight >= (refPos.top + refHeight))) {
            return;
        }
        if (ppPos.top + ppHeight <= refPos.top + refHeight / 2) {
            var rightDistance = Math.abs(ppPos.left + offset - (refPos.left + refWidth));
            var closestDistance = rightDistance;
            var closest = 'right';
            var middleDistance = Math.abs(ppPos.left + 0.5*ppWidth - (refPos.left + 0.5*refWidth));
            if (middleDistance < closestDistance) {
                closest = 'middle';
                closestDistance = middleDistance;
            }
            var leftDistance = Math.abs(ppPos.left + ppWidth - refPos.left);
            if (leftDistance < closestDistance) {
                closest = 'left';
            }
            pp.addClass('pointer-south-' + closest);
        } else if (ppPos.top > refPos.top + refHeight / 2) {
            var leftDistance = Math.abs(ppPos.left + offset - (refPos.left + refWidth));
            var closestDistance = leftDistance;
            var closest = 'left';
            var middleDistance = Math.abs(ppPos.left + 0.5*ppWidth - (refPos.left + 0.5*refWidth));
            if (middleDistance < closestDistance) {
                closest = 'middle';
                closestDistance = middleDistance;
            }
            var rightDistance = Math.abs(ppPos.left + ppWidth - refPos.left);
            if (rightDistance < closestDistance) {
                closest = 'right';
            }
            pp.addClass('pointer-north-' + closest);
        } else if (ppPos.left + ppWidth<= refPos.left + refWidth) {
            var leftDistance = Math.abs(ppPos.top - refPos.top + refHeight);
            var closestDistance = leftDistance;
            closest = 'left';
            var middleDistance = Math.abs(ppPos.top + 0.5*ppHeight - (refPos.top + 0.5*refHeight));
            if (middleDistance < closestDistance) {
                closest = 'middle';
                closestDistance = middleDistance;
            }
            var rightDistance = Math.abs(ppPos.top + ppHeight - refPos.top);
            if (rightDistance < closestDistance) {
                closest = 'right';
            }
            pp.addClass('pointer-east-' + closest);
        } else if (ppPos.left > refPos.left){
            var rightDistance = Math.abs(ppPos.top - refPos.top + refHeight);
            var closestDistance = rightDistance;
            closest = 'right';
            var middleDistance = Math.abs(ppPos.top + 0.5*ppHeight - (refPos.top + 0.5*refHeight));
            if (middleDistance < closestDistance) {
                closest = 'middle';
                closestDistance = middleDistance;
            }
            var leftDistance = Math.abs(ppPos.top + ppHeight - refPos.top);
            if (leftDistance < closestDistance) {
                closest = 'left';
            }
            pp.addClass('pointer-west-' + closest);
        }
    }

    /**
     * Removes the pointer of a popup.
     * @memberOf ppi.popup.private
     */
    function _removePointer(uuid) {
        var pp = jq('#' + uuid);
        pp.removeClass('pointer-north-left pointer-north-middle pointer-north-right \
                        pointer-east-left pointer-east-middle pointer-east-right \
                        pointer-south-left pointer-south-middle pointer-south-right \
                        pointer-west-left pointer-west-middle pointer-west-right');
    }

ppi.popup = {
    /**
     * Checks whether the given popup is displayed within the screen borders and repositions or resizes it to fit.
     * @param popup uuid
     *
     * @memberOf ppi.popup
     */
    ensureOnScreen: function(popup) {
        var wgt = zk.$(popup);
        if (!wgt) return
        var pp = jq(wgt)
        var ppPos = pp.offset()
        var ppWidth = pp.outerWidth()
        var ppHeight = pp.outerHeight()
        var closeBtnOverlap = 9
        var marginAgainstScrollbars = 1

        var windowWidth = window.innerWidth
                || document.documentElement.clientWidth
                || document.body.clientWidth;
        var windowHeight = window.innerHeight
                || document.documentElement.clientHeight
                || document.body.clientHeight;

        // something wrong, better stop here.
        if (ppPos == undefined || ppWidth == undefined || ppHeight == undefined) return

        if (ppPos.top - closeBtnOverlap < 0) {
            pp.offset({
                top : closeBtnOverlap,
                left : ppPos.left
            });
            ppPos = pp.offset();
        }
        if (ppPos.top + ppHeight + marginAgainstScrollbars > windowHeight) {
            pp.offset({
                top : windowHeight - ppHeight - marginAgainstScrollbars,
                left : ppPos.left
            });
            ppPos = pp.offset();
        }
        if (ppPos.left < 0) {
            pp.offset({
                top : ppPos.top,
                left : 0
            });
            ppPos = pp.offset();
        }
        if (ppPos.left + ppWidth >= windowWidth) {
            pp.offset({
                top : ppPos.top,
                left : windowWidth - ppWidth
            });
            pp.outerWidth(ppWidth);
        }
    },
    /**
     * Positions a given popup on screen.
     * @param popup uuid
     * @param anchor uuid of anchor component, if set a pointer is displayed
     * @param position zk position-constraint
     *
     * @memberOf ppi.popup
     */
    position : function(popup, anchor, position) {
        setTimeout(function(){
            var wgt = zk.$(popup);
            if (!wgt) return
            var pp = jq(wgt);
            var ppPos = pp.offset();
            var ppWidth = pp.outerWidth();
            var ppHeight = pp.outerHeight();

            if (anchor !== '') {
                var ref = jq('#' + anchor);
                var refPos = ref.offset();
                var refWidth = ref.outerWidth();
                var refHeight = ref.outerHeight();
            }
            if (position !== 'TOP_MIDDLE_PAGE'
                && (anchor == null || typeof refPos == 'undefined' || typeof refWidth == 'undefined' || typeof refHeight == 'undefined')) {
                //defensive error handling though the the anchor should always be != null
                wgt.setVisible(false);
                return;
            }

            var windowWidth = window.innerWidth
                    || document.documentElement.clientWidth
                    || document.body.clientWidth;
            var windowHeight = window.innerHeight
                    || document.documentElement.clientHeight
                    || document.body.clientHeight;

            var topMiddlePageGap = 80;
            var gap = 10;
            var pointerOffset = 6;
            var borderWidth = 6;
            var pointerOffsetNorthRight = 22;
            var pointerOffsetEastLeft = 9;
            var closeBtnOverlap = 9;

            switch (position) {
            case 'BOTTOM_LEFT':
                pp.offset({
                    top : refPos.top + refHeight + gap,
                    left : refPos.left - pointerOffset
                });
                pp.addClass('pointer-north-left');
                break;
            case 'BOTTOM_MIDDLE':
                pp.offset({
                    top : refPos.top + refHeight + gap,
                    left : refPos.left + refWidth/ 2 - ppWidth/ 2
                });
                pp.addClass('pointer-north-middle');
                break;
            case 'BOTTOM_RIGHT':
                pp.offset({
                    top : refPos.top + refHeight + gap,
                    left : refPos.left + refWidth - ppWidth + pointerOffsetNorthRight
                });
                pp.addClass('pointer-north-right');
                break;
            case 'TOP_LEFT':
                pp.offset({
                    top : refPos.top - ppHeight - gap + borderWidth,
                    left : refPos.left - pointerOffset
                });
                pp.addClass('pointer-south-right');
                break;
            case 'TOP_MIDDLE':
                pp.offset({
                    top : refPos.top - ppHeight - gap + borderWidth,
                    left : refPos.left + refWidth/ 2 - ppWidth/ 2
                });
                pp.addClass('pointer-south-middle');
                break;
            case 'TOP_RIGHT':
                pp.offset({
                    top : refPos.top - ppHeight - gap + borderWidth,
                    left : refPos.left + refWidth - ppWidth + pointerOffset
                });
                pp.addClass('pointer-south-left');
                break;
            case 'RIGHT_TOP':
                pp.offset({
                    top : refPos.top - pointerOffset,
                    left : refPos.left + refWidth + gap
                });
                pp.addClass('pointer-west-right');
                break;
            case 'RIGHT_MIDDLE':
                pp.offset({
                    top : refPos.top + refHeight/ 2 - ppHeight/ 2,
                    left : refPos.left + refWidth + gap
                });
                pp.addClass('pointer-west-middle');
                break;
            case 'RIGHT_BOTTOM':
                pp.offset({
                    top : refPos.top + refHeight - ppHeight + borderWidth + pointerOffset,
                    left : refPos.left + refWidth + gap
                });
                pp.addClass('pointer-west-left');
                break;
            case 'LEFT_TOP':
                pp.offset({
                    top : refPos.top - pointerOffsetEastLeft,
                    left : refPos.left - ppWidth - gap
                });
                pp.addClass('pointer-east-left');
                break;
            case 'LEFT_MIDDLE':
                pp.offset({
                    top : refPos.top + refHeight/ 2 - ppHeight/ 2,
                    left : refPos.left - ppWidth - gap
                });
                pp.addClass('pointer-east-middle');
                break;
            case 'LEFT_BOTTOM':
                pp.offset({
                    top : refPos.top + refHeight - ppHeight + borderWidth + pointerOffset,
                    left : refPos.left - ppWidth - gap
                });
                pp.addClass('pointer-east-right');
                break;
            case 'MIDDLE':
                pp.offset({
                    top : refPos.top + refHeight/ 2 - ppHeight/ 2,
                    left : refPos.left + refWidth/ 2 - ppWidth/ 2
                });
                break;
            default:
            case 'TOP_MIDDLE_PAGE':
                pp.offset({
                    top : topMiddlePageGap,
                    left : windowWidth/2 - ppWidth/2
                });
                break;
            }

            ppi.popup.ensureOnScreen(popup);

            var moveListener = function() {
                if (ref && zk(ref).isRealVisible()) {
                    _setPointer(popup, refPos, refWidth, refHeight,  gap, pointerOffset)
                } else if (popup) {
                    popup.detach()
                    ref = null;
                }
            }
            if (anchor && ref) wgt.listen({onMove: moveListener});
        },0);
    },

    /**
     * Marks a component as movable and adds respective listeners.
     * @param component uuid
     * @memberOf ppi.popup
     */
    setMovable: function(uuid) {
        var wgt = zk.$(uuid)
        var $wgt = jq(wgt);
        var pos = $wgt.offset(), clickX, clickY;
        var moveListener = function(moveEvent) {
            $wgt.offset({
                top: moveEvent.pageY - clickY + pos.top,
                left: moveEvent.pageX - clickX + pos.left
            });
            ppi.popup.ensureOnScreen(uuid);
        };
        $wgt.children(".z-popup-content").mousedown(function(downEvent) {
            if (jq(downEvent.target).css("cursor") === "move") {
                pos = $wgt.offset();
                clickX = downEvent.pageX;
                clickY = downEvent.pageY;
                $wgt.children(".z-popup-content").on("mousemove", moveListener);
            }
        });

        $wgt.children(".z-popup-content").mouseup(function() {
            $wgt.children(".z-popup-content").off("mousemove", moveListener);
            wgt.fire('onMove');
        });
    },

    /**
     * Initializes PopupHint component with listeners to stop/enable autohide.
     * @see ppi.popup.position for params
     * @memberOf ppi.popup
     */
    initializeHint: function(popup, anchor, position) {
        ppi.popup.position(popup, anchor, position);
        ppi.popup.setMovable(popup);

        var wgt = zk.$(popup)
        var $wgt = jq(wgt);

        $wgt.mouseover(function(eventData) {
            if ($wgt.attr("mousePos") !== "over") {
                zAu.send(new zk.Event(wgt, 'onStopTimer', null));
                $wgt.attr("mousePos", "over");
            }
        });
        $wgt.mouseout(function(eventData) {
            if (_isOutside(eventData.pageX, eventData.pageY,
                                    $wgt.offset().left, $wgt.offset().top,
                                    $wgt.outerWidth(), $wgt.outerHeight())) {
                zAu.send(new zk.Event(wgt, 'onStartTimer', null));
                $wgt.attr("mousePos", "out");
            }
        });
    }
}
})();

(function() {

    /**
    * Insers spaces according to the provided pattern. The result is set to uppercase and the cursor-position is corrected accordingly.
    *
    * @param widget Widget instance
    * @param formatType type of the desired format.
    *
    * @memberOf ppi.input.masks
    */
    function applyPattern(wgt, pattern, withoutCursorPosition){
        return function(evt){
            if (wgt == null || evt == null || evt.value==null){
                return;
            }
            var input= evt.value;
            var cursorPosition = jq(wgt).prop("selectionStart");
            var result="";

            var cursordiff = 0;
            var inputIndex=0;

            for (var i = 0, len = pattern.length; i < len && inputIndex < input.length ; i++) {
                currentInputChar = input[inputIndex];
                if ((/\s/.test(currentInputChar) && currentInputChar !== " ") ||(currentInputChar === " " && pattern[i] !==" ") ){
                    if(inputIndex < cursorPosition){
                        cursordiff--;
                    }
                    inputIndex++;
                    i--
                }
                else if ((currentInputChar === " " && pattern[i] ===" ") ||
                ( currentInputChar !== " " && pattern[i] !==" ") ){
                    result += currentInputChar;
                    inputIndex++;
                }else if (currentInputChar !== " " && pattern[i] ===" "){
                    if(inputIndex < cursorPosition){
                        cursordiff++;
                    }
                    result+=" ";
                }
            }
            if (inputIndex < input.length){
                result += input.substring(inputIndex, input.length)
            }

            result = result.toUpperCase();

            //https://potix.freshdesk.com/support/tickets/3625
            //SetText für sich funktioniert nicht
            wgt.getInputNode().value = result;
            wgt._lastChg = result; /*needed for the next onChanging in case the same space is deleted multiple times*/
            //to update the onChanging event sent to the server (if necessary)
            evt.value = result;
            evt.data.value = result; 

            if (!withoutCursorPosition) {
                var newCaretPos= cursorPosition + cursordiff;
                var elem = jq(wgt).context;
    
                if(elem.createTextRange) {
                    var range = elem.createTextRange();
                    range.move('character', newCaretPos);
                    range.select();
                } else if(elem.selectionStart) {
                    elem.setSelectionRange(newCaretPos, newCaretPos);
                }
            }
        }
    }

    function toUppercase(wgt){
        return function(evt){
            if (wgt == null || evt == null || evt.value==null){
                return;
            }

            var result = evt.value.toUpperCase();

            //https://potix.freshdesk.com/support/tickets/3625
            //SetText für sich funktioniert nicht
            wgt.getInputNode().value = result;
            wgt._lastChg = result; /*needed for the next onChanging in case the same space is deleted multiple times*/
            //to update the onChanging event sent to the server (if necessary)
            evt.value = result;
            evt.data.value = result; 
        }
    }

    ppi.inputmasks = {
        FORMAT_TYPE: {
            KEY_HASH: 'keyHash',
            IBAN: 'iban',
            UPPERCASE: 'uppercase',
            CREDITOR_ID: 'creditorId'
        },

        /**
        * Creates a function, which can be called during "onChanging" in order to format the client input.
        *
        * @param widget Widget 
        * @param formatType type of the desired format.
        *
        * @memberOf ppi.input.masks
        */
        createInputMaskFunction: function(widget, formatType, withoutCursorPosition) {
            if (formatType === ppi.inputmasks.FORMAT_TYPE.KEY_HASH ){
                 return applyPattern(widget, "xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx", withoutCursorPosition);
            }else if(formatType === ppi.inputmasks.FORMAT_TYPE.IBAN){
                return applyPattern(widget, "xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx", withoutCursorPosition);
            }else if(formatType === ppi.inputmasks.FORMAT_TYPE.UPPERCASE){
                return toUppercase(widget);
            }else if (formatType === ppi.inputmasks.FORMAT_TYPE.CREDITOR_ID ){
                return applyPattern(widget, "xx xx xxx xxxxxxxxxxxxxxxxxxxxxxxxxxxx", withoutCursorPosition);
            }else {
                return null;
            }
        }
    }
})();

var _xcmd0 = {};
zk.override(zAu.cmd0, _xcmd0, {
    showBusy : function(uuid, msg, opts) {
        if (arguments.length == 1) {
            msg = uuid;
            uuid = null;
        }
        zAu.cmd0.clearBusy(uuid);
        var w = uuid ? zk.Widget.$(uuid) : null;
        if (!uuid)
            zUtl.progressbox('zk_showBusy', msg || msgzk.PLEASE_WAIT, true, null, {busy: true});
        else if (w && w.isRealVisible(true)) {
            zk.delayFunction(uuid, function() {
                var args = {
                    id : w.uuid + '-shby',
                    anchor : w.$n(),
                    message : msg
                }
                if (opts && opts.sclass)
                    args.sclass = opts.sclass;
                w.effects_.showBusy = new zk.eff.Mask(args);
            });
        }
    }
});
(function() {
function _setIgnoredCtrlKeysErr(msg) {
    zk.error("ppi.widget.js->setIgnoredCtrlKeys: " + msg);
}

function _getParsedCtrlKeyString(keys) {

    var parsed = [ {}, {}, {}, {}, {}, {}, {}, {}, {} ], which = 0;
    for (var j = 0, len = keys.length; j < len; ++j) {
        var cc = keys.charAt(j);
        switch (cc) {
        case '^':
        case '@':
        case '$':
            var flag = cc == '^' ? 1 : cc == '@' ? 2 : 4;
            if ((which & flag) != 0) {
                // was: 'Combination of Shift, Alt and Ctrl not supported'
                _setIgnoredCtrlKeysErr('Unexpected key combination: ' + keys);
                return null
            } else
                which |= flag;
            break;
        case '#':
            var k = j + 1;
            for (; k < len; ++k) {
                var c2 = keys.charAt(k);
                if ((c2 > 'Z' || c2 < 'A') && (c2 > 'z' || c2 < 'a') && (c2 > '9' || c2 < '0'))
                    break;
            }
            if (k == j + 1) {
                _setIgnoredCtrlKeysErr("Unexpected character " + cc + " in " + keys);
                return null;
            }

            var s = keys.substring(j + 1, k).toLowerCase();
            if ("pgup" == s)
                cc = 33;
            else if ("pgdn" == s)
                cc = 34;
            else if ("end" == s)
                cc = 35;
            else if ("home" == s)
                cc = 36;
            else if ("left" == s)
                cc = 37;
            else if ("up" == s)
                cc = 38;
            else if ("right" == s)
                cc = 39;
            else if ("down" == s)
                cc = 40;
            else if ("ins" == s)
                cc = 45;
            else if ("del" == s)
                cc = 46;
            else if ("bak" == s)
                cc = 8;
            else if (s.length > 1 && s.charAt(0) == 'f') {
                var v = zk.parseInt(s.substring(1));
                if (v == 0 || v > 12) {
                    _setIgnoredCtrlKeysErr("Unsupported function key: #f" + v);
                    return null
                }
                cc = 112 + v - 1;
            } else {
                _setCtrlKeysErr("Unknown #" + s + " in " + keys);
                return null
            }

            parsed[which][cc] = true;
            which = 0;
            j = k - 1;
            break;
        default:
            if (!which || ((cc > 'Z' || cc < 'A') && (cc > 'z' || cc < 'a') && (cc > '9' || cc < '0'))) {
                _setIgnoredCtrlKeysErr("Unexpected character " + cc + " in " + keys);
                return null
            }
            if (which == 4) {
                _setIgnoredCtrlKeysErr("$a - $z not supported (found in " + keys
                        + "). Allowed: $#f1, $#home and so on.");
                return null
            }

            if (cc <= 'z' && cc >= 'a')
                cc = cc.toUpperCase();
            parsed[which][cc.charCodeAt(0)] = true;
            which = 0;
            break;
        }
    }
    return parsed;
}

var _zulWidget = {};
zk.override(zul.Widget.prototype, _zulWidget, {
    bind_ : function() {
        _zulWidget.bind_.apply(this, arguments);

        if (this.$instanceof(zul.wnd.Panelchildren) || this.$instanceof(zul.wnd.Window)) {
            // listen on control keys
            var thisDom = this.$n();

            // make divs focusable
            var tabindex = document.createAttribute('tabindex');
            tabindex.value = '0';
            thisDom.setAttributeNode(tabindex);
        }
    },

    setIgnoredCtrlKeys : function(keys) {
        if (this._ignoredCtrlKeys == keys)
            return;
        if (!keys) {
            this._ignoredCtrlKeys = this._parsedIgnoredCtrlKeys = null;
            return;
        }

        this._ignoredCtrlKeys = keys;
        this._parsedIgnoredCtrlKeys = _getParsedCtrlKeyString(keys);
        if (!this._ctrlKeys) {
            this.setCtrlKeys(keys);
        }
        return this;
    },

    setAllowedCtrlKeys : function(keys) {
        this._parsedAllowedCtrlKeys = [ {}, {}, {}, {}, {} ];
        if (keys)
            this._parsedAllowedCtrlKeys = _getParsedCtrlKeyString(keys);
        return this;
    },

    afterKeyDown_ : function(evt, simulated) {
        var which = evt.ctrlKey ? 1 : evt.altKey ? 2 : evt.shiftKey ? 3 : 0;

        if (this._parsedIgnoredCtrlKeys && this._parsedIgnoredCtrlKeys[which][evt.keyCode]
                && !this._parsedAllowedCtrlKeys[which][evt.keyCode]) {
            return true;
        } else if (_zulWidget.afterKeyDown_) {
            return _zulWidget.afterKeyDown_.apply(this, arguments)
        }
    },
    domAttrs_: function() {
        return _zulWidget.domAttrs_.apply(this, arguments).replace('title=" "', 'title=""')
    }
});

// Trac #3691, Potix #4322
var _zkWidget = {};
zk.override(zk.Widget, _zkWidget ,{
    mimicMouseDown_: function (wgt, noFocusChange, which) { //called by mount
        var modal = zk.currentModal;
        if (modal && !wgt) {
            var cf = zk.currentFocus;
            //Note: browser might change focus later, so delay a bit
            //(it doesn't work if we stop event instead of delay - IE)
            if (cf && zUtl.isAncestor(modal, cf)) cf.focus(0);
            else modal.focus(0);
        } else if (!wgt || wgt.canActivate()) {
            if (!noFocusChange) {
                var wgtVParent;
                zk._prevFocus = zk.currentFocus;
                zk.currentFocus = wgt;
                if (wgt && (wgtVParent = wgt.$n('a')) && jq.nodeName(wgtVParent, 'button', 'input', 'textarea', 'a', 'select', 'iframe')) {
                    //ZK-3230: ie lost the focus when click on scroll bar, to prevent scroll into view automatically, set VParent's position to "fixed" temporary.
                    var oldStyle;
                //  if (zk.ie) {
                //      oldStyle = wgtVParent.style.position;
                //      wgtVParent.style.position = 'fixed';
                //  }
                // DO NOT COMMENT OUT FOCUS
                    wgt.focus();
                //  if (zk.ie) wgtVParent.style.position = oldStyle;
                }
                zk._cfByMD = true;
                setTimeout(function () {zk._cfByMD = false; zk._prevFocus = null;}, 0);
                    //turn it off later since onBlur_ needs it
            }

            if (wgt) // F70-ZK-2007: Add the button number information.
                zWatch.fire('onFloatUp', wgt, {triggerByClick: which}); //notify all
            else
                for (var dtid in zk.Desktop.all)
                    zWatch.fire('onFloatUp', zk.Desktop.all[dtid]); //notify all
        }
    }
});

var _labelImageWidget = {};
zk.override(zul.LabelImageWidget.prototype, _labelImageWidget, {
    _errbox : null,

    setErrorboxSclass : function(arg) {
        this._errorboxSclass = arg;
    },
    setErrorboxIconSclass : function(arg) {
        this._errorboxIconSclass = arg;
    },

    unbind_ : function() {
        this.clearErrorMessage(false);
        this.$supers(zul.LabelImageWidget, 'unbind_', arguments)
    },

    setErrorMessage : function(msg) {
        var isMsg = (msg !== '')
        this.clearErrorMessage(isMsg)
        if (isMsg && this.desktop) {
            if (this.$instanceof(zul.wgt.Button))
                jq(this).addClass('z-button-invalid');
            else
                jq(this).addClass(this.$s('invalid'));
            this._errbox = new zul.inp.Errorbox(this, msg)
            this._errbox.show();
        }
    },
    clearErrorMessage : function(remainError) {
        var w = this._errbox;
        if (w) {
            this._errbox = null;
            w.destroy();
        }
        if (!remainError) {
            this._errmsg = null;
            if (this.$instanceof(zul.wgt.Button))
                jq(this).removeClass('z-button-invalid');
            else
                jq(this).removeClass(this.$s('invalid'));
        }
    }
});

zk.afterLoad('zul.menu', function() {
    var _origMenuitem = {}
    zk.override(zul.menu.Menuitem.prototype, _origMenuitem, {
        doClick_ : function(evt) {
            var anc = this.$n('a')
            if (anc && anc.href && anc.target)
                setTimeout(_origMenuitem.doClick_.bind(this, arguments), 0)
            else
                _origMenuitem.doClick_.apply(this, arguments)
        }
    })
});
})();


var _zulWindow = {};
zk.override(
    zul.wnd.Window.prototype,
    _zulWindow,
    {
        setMode: function(mode) {
            var rslt = _zulWindow.setMode.apply(this, arguments);
            this._adjustModalPosition(this.getPosition(), mode);
            return rslt;
        },
        
        setSclass: function(sclass) {
            _zulWindow.setSclass.apply(this, arguments);
            this._adjustModalPosition(this.getPosition(), this.getMode());
            return this.getSclass();
        },
        
        _adjustModalPosition: function(pos, mode) {
            if (mode && (mode == 'modal' || mode == 'overlapped') && zk.ie > 0 && zk.ie < 10) {
                var sclass = this.getSclass();
                var fix = 'fix-ie';
                if (!sclass) {
                    _zulWindow.setSclass.apply(this, [fix]);

                //do not override display-file-dialog, standard-dialog etc.
                } else if (sclass.indexOf('dialog') == -1 && sclass.indexOf(fix) == -1) {
                    _zulWindow.setSclass.apply(this, [sclass + ' ' + fix]);
                }
            }
        }
    }
);

zk.afterMount(function() {
    var superPopup = {};
    zk.override(zul.wgt.Popup.prototype, superPopup, {
        close: function (opts) {
            if (this._stackup) this._stackup.style.display = 'none';
            this._shallToggle = false;
            try {
                if ((zk.ie || zk.ff) && zk.currentFocus) {
                    var n = zk.currentFocus.getInputNode ? zk.currentFocus.getInputNode() : zk.currentFocus.$n();
                    if (jq.nodeName(n, 'input')) {
                        var a = document.activateElement;
                        if (zk.ff && jq.isAncestor(this.$n(), n)) {
                            jq(n).blur();
                        } else if (zk.ie && document.activateElement && document.activateElement !== n) {
                            // stack at the end to ensure all undoVparent/other focuses are done.
                            if (n) setTimeout(function() {jq(n).focus();zk.currentFocus = zk.$(n);}, 0);
                        }
                    }
                }
            } catch (e) {}

            this.closeAnima_(opts);
        }
    });

    /** Context menu fixes **/
    // No context menu with busy notifier
    var superFullmask = {};
    zk.override(zk.eff.FullMask.prototype, superFullmask, {
        $init : function(opts) {
            superFullmask.$init.apply(this, arguments);
            jq(this.mask).contextmenu(jq.Event.stop);
        }
    });

    var superMask = {};
    // No context menu with modal window
    zk.override(zk.eff.Mask.prototype, superMask, {
        $init : function(opts) {
            superMask.$init.apply(this, arguments);
            jq(this.mask).contextmenu(jq.Event.stop);
            if (opts && opts.sclass) {
                var maskId = '#' + (opts.id || 'z_applymask');
                jq(maskId).addClass(opts.sclass);
            }
        }
    });

    /** Custom message dialog */
    var _getValIfSingleValue = function(obj) {
        if (!obj) return 0
        var val = null;
        for (key in obj)
            if (obj.hasOwnProperty(key))
                if (val)
                    return null
                else
                    val = obj[key]
        return val;
    };

    jq._realAlert = jq.alert

    // override title
    jq.alert = function(msg, opts) {
        if (!opts.title)
            if (opts.icon && msgzk.MESSAGEBOX[opts.icon])
                opts.title = msgzk.MESSAGEBOX[opts.icon]
            else
                opts.title = msgzk.MESSAGEBOX['ERROR']
        jq._realAlert(msg, opts);

        // fix width
        var wnd = zk.$('$aualert')
        if (wnd) {
            var onlyBtnFn = _getValIfSingleValue(opts.button)
            if (onlyBtnFn) wnd.listen({onClose: onlyBtnFn})
            $(wnd).addClass('standard-dialog')
            wnd.setWidth(null)
        }
    }

    /** Fixes for sso environments */

    var _timeoutUri = zk.ajaxURI("/base/timeout.zul");
    var _safeRedirect = function() {
        try {
            jq(window).off('unload')
        } catch (e) {}
        if (window.onbeforeunload) window.onbeforeunload = null
        open(_timeoutUri, "_self")
    }

    var _errorDialogShown = false;
    var _showErrorDialog = function() {
        if (_errorDialogShown) return;
        _errorDialogShown = true;

        jq('#zk_proc').remove()
        jq.alert(msgzk.ILLEGAL_RESPONSE, {icon:'ERROR', button: {RELOAD: function() { _safeRedirect(); }}})
    }

    /** Fix response cannot be parsed (defensive behaviour). */
    var _origEvalJSON = jq.evalJSON
    jq.evalJSON = function(jsonResponse) {
        try {
            return _origEvalJSON.apply(this, arguments)
        } catch (e) {
            _showErrorDialog()
            throw e;
        }
    };

    /** Fix: 160811hk1433tp issue: idling 30mins+, clicking on any element (initiating zkau). */
    var _origConfirmRetry = zAu.confirmRetry;
    zAu.confirmRetry = function(msgCode, msg2) {
        if (msgCode === 'FAILED_TO_RESPONSE') {
            try {
                jq(window).off('unload')
            } catch (e) {}
            if (window.onbeforeunload) window.onbeforeunload = null
          
            _showErrorDialog();
            return false
        }
        return _origConfirmRetry.apply(this, arguments);
    }

    /** Fix: 160811hk1433tp issue: idling 30mins+, zkcomet restarts instantly. */
    zk.afterLoad('zkex.cmsp', function() {
        function _onCustRespReady() {
            var req = this._req;
            try {
                if (req && req.readyState == 4) {
                    this._req = null;

                    var cerr, rqst, v;
                    if ((cerr = req.getResponseHeader('ZK-Comet-Error')) == 'Disabled'
                    || cerr == 'DesktopUnavailable'
                    || (rqst = req.status) == 404 || rqst == 405 || rqst == 410
                    || (v = req.getResponseHeader('ZK-Error')) == '404' || v == '410') {
                        v = zAu.getPushErrorURI(rqst == 404 || v == '404' ? 404 : 410);
                        if (typeof v == 'string') zUtl.go(v);
                        this.stop();

                    } else if (rqst == 200) {
                        this._nfail = 0;
                        if (req.getResponseHeader('ZK-Comet') == 'echo' || cerr == 'Hibernate')
                            zAu.cmd0.echo(this.desktop);

                        if (cerr != 'Hibernate') {
                            if ((v = cerr == 'Busy')) {
                                this._send();
                                var self = this;
                                setTimeout(function () { if (self.desktop) zAu.cmd0.echo(self.desktop); }, 200);
                            } else {
                                // fix: prevent _asend if response contains plain HTML.
                                try {
                                    if (req && req.response && req.response.indexOf) {
                                        if (req.response.indexOf("<html") != -1 || req.response.indexOf("<HTML") != -1) {
                                            _safeRedirect()
                                            return
                                        }
                                    }
                                } catch (e) {
                                    // die silently
                                    return
                                }
                                //just asend if there is no sso related errors
                                this._asend(0);
                            }
                        }

                    } else if (rqst == 502) {
                        this._send();
                        var self = this;
                        setTimeout(function () { zAu.cmd0.echo(self.desktop); }, 200);
                    } else
                        this._retry(this._retryDelay);
                }
            } catch (e) {
                this._retry(this._retryDelay);
            }
        }

        function _onAbort() {
            this.stop();
        }

        var _xSPush = {};
        zk.override(zkex.cmsp.SPush.prototype, _xSPush, {
            _send: function () {
                if (zk.ausending) {
                    zk.afterAuResponse(this.proxy(this._send));
                    return;
                }
                this._asending = false;
                var req = this._req = zAu.ajaxSettings.xhr(),
                    dt = this.desktop;
                try {
                    req.onreadystatechange = this.proxy(_onCustRespReady);
                    if (zk.ie && zk.ie == 9) {
                        var self = this;
                        req.ontimeout = function (e) {
                            self._retry(self._retryDelay);
                        };
                    }
                    req.onabort = this.proxy(_onAbort);
                    req.open('POST', this.cometURI(dt), true);
                    if (this._ajaxTimeout >= 0) {
                        req.timeout = this._ajaxTimeout;
                    }
                    req.setRequestHeader('Content-Type', zAu.ajaxSettings.contentType);
                    req.send(null);
                } catch (e) {
                    this._req = null;
                    this._asend(this._retryDelay);
                }
            }
        });
    });
});

(function () {
    var listeners = {}

    var MutationObserver = (function() {
        var prefixes = ['', 'WebKit', 'Moz', 'O', 'Ms', 'Js']
        for (var i=0; i < prefixes.length; i++)
            if (prefixes[i] + 'MutationObserver' in window)
                return window[prefixes[i] + 'MutationObserver']
        return false;
    })();

    function callback(mutations) {
        for (key in listeners) listeners[key](mutations)
    }

ppi.domevents = {
    _observer: null,

    register: function(id, fn, target, config) {
        if (listeners[id]) return

        if (!MutationObserver) {
            var agent = 'Unknown',
                uas = {ie:'IE',ff: 'Firefox',chrome:'Chrome',safari:'Safari',gecko:'Gecko'}
            for (var ua in uas) if (zk[ua]) { agent = uas[ua] + ' ' + zk[ua]; break; }
            jq.alert(msgzk.UNSUPPORTED_BROWSER + agent, {icon:'ERROR'})
            return
        }

        if (!ppi.domevents._observer)
            ppi.domevents._observer = new MutationObserver(function(mutations) {
                callback(mutations)
            })

        listeners[id] = fn
        var cfg = config ? config : { childList: true, subtree: false }
        ppi.domevents._observer.observe(target, cfg)
    },

    unregister: function(id) {
        if (listeners[id]) delete listeners[id]
        var active = false
        for (key in listeners) {
            active = true
            break
        }
        if (!active && ppi.domevents._observer) {
            ppi.domevents._observer.disconnect()
            ppi.domevents._observer = null;
        }
    },

    initializeExternalBrandBar: function(src) {
        if (!src) return

        function _checkForBrandbar() {
            var banner = $('.am-tb-banner')
            if (banner.length)
                zAu.cmd1.resizeWgt(zk.$($('.z-page')[0].id))
            return !!banner.length
        }

        var id = 'brandbar'
        ppi.domevents.register(id, function(mutations) { if (_checkForBrandbar()) ppi.domevents.unregister(id) }, document.body)

        var js = document.createElement('script')
        js.setAttribute('type', 'text/javascript')
        js.setAttribute('charset', 'UTF-8')
        js.setAttribute('src', src) 
        document.getElementsByTagName('head')[0].appendChild(js)

        // check if MutationObserver is not up but BrandBar is already there
        setTimeout(function() { if (_checkForBrandbar()) ppi.domevents.unregister(id) })
    }
}
})();
zk.afterMount(function() {
    // created by zk-support:
    // https://potix.freshdesk.com/support/tickets/3095
    var clientInfoCache = {};
    zAu.cmd0.clientInfo = function(dtid) { 
        // replace the original clientInfo method
        zAu._cInfoReg = true;
        var orient = '', dpr = 1;
        if (zk.mobile) {
            _portrait = {'0': true, '180' : true};
            if ((jq.innerWidth() > jq.innerHeight() && zAu.cmd0._initDefault)
                    || (!(jq.innerWidth() > jq.innerHeight()) && !_portrait[window.orientation]))
                _portrait = {'-90': true, '90': true};
            orient = _portrait[window.orientation] ? 'portrait' : 'landscape';
        } else {
            orient = jq.innerWidth() > jq.innerHeight() ? 'landscape' : 'portrait';
        }
        if (window.devicePixelRatio)
            dpr = window.devicePixelRatio;
        if (!(clientInfoCache.screenWidth == screen.width
                && clientInfoCache.screenHeight == screen.height
                && clientInfoCache.innerWidth == jq.innerWidth()
                && clientInfoCache.innerHeight == jq.innerHeight()
                && clientInfoCache.innerX == jq.innerX()
                && clientInfoCache.innerY == jq.innerY()
                && clientInfoCache.dpr == dpr && clientInfoCache.orient == orient)) {
            zAu.send(new zk.Event(zk.Desktop.$(dtid), 'onClientInfo', [
                    new Date().getTimezoneOffset(), screen.width,
                    screen.height, screen.colorDepth, jq.innerWidth(),
                    jq.innerHeight(), jq.innerX(), jq.innerY(),
                    dpr.toFixed(1), orient ], {
                implicit : true,
                rtags : {
                    onClientInfo : 1
                }
            }));
            clientInfoCache.screenWidth = screen.width
            clientInfoCache.screenHeight = screen.height;
            clientInfoCache.innerWidth = jq.innerWidth();
            clientInfoCache.innerHeight = jq.innerHeight();
            clientInfoCache.innerX = jq.innerX();
            clientInfoCache.innerY = jq.innerY();
            clientInfoCache.dpr = dpr;
            clientInfoCache.orient = orient;
        } 
    }
});

}finally{zk.setLoaded(zk._p.n);}});zk.setLoaded('ppi',1);zk.load('zul.tab,ppi',function(){if(zk._p=zkpi('ppi.tab'))try{

(function() {
// Add switch tab event
var _xTab = {};
zk.override(zul.tab.Tab.prototype, _xTab, {
    _sel : function(notify, init) {
        if (this.desktop && !init && notify) {
            zAu.send(new zk.Event(this, 'onSwitchTab'));
        } else {
            // call the original method
            _xTab._sel.apply(this, arguments);
        }
    }
})

var _xTabs = {};
zk.override(zul.tab.Tabs.prototype, _xTabs, {
     _doScroll: function (to, move) {
        if (!this._doingScroll)
            this._doingScroll = {};
        if (move <= 0 || this._doingScroll[to])
            return;
        var step,
            self = this,
            tabs = this.$n();

        this._doingScroll[to] = move;
        
        step = move <= 60 ? 5 : (5 * (zk.parseInt(move / 60) + 1));
        
        var goscroll = function (tabs, to, step) {
            switch (to) {
            case 'right':
                tabs.scrollLeft += step;
                break;
            case 'left':
                tabs.scrollLeft -= step;
                break;
            case 'up':
                tabs.scrollTop -= step;
                break;
            default:
                tabs.scrollTop += step;
            }
            tabs.scrollLeft = (tabs.scrollLeft <= 0 ? 0 : tabs.scrollLeft);
            tabs.scrollTop = (tabs.scrollTop <= 0 ? 0 : tabs.scrollTop);
        };
        var run = setInterval(function () {
            if (!move) {
                delete self._doingScroll[to];
                clearInterval(run);
                // refresh scroll classes after the scrolling animation has finished
                self.getTabbox()._refreshScrollClasses();
                return;
            } else {
                move < step ? goscroll(tabs, to, move) : goscroll(tabs, to, step);
                move -= step;
                move = move < 0 ? 0 : move;
            }
        }, 10);
    },
    _showbutton: function (show) {
        var tabbox = this.getTabbox();
        if (tabbox.isTabscroll()) {
            var cls = tabbox.$s('scroll');
            jq(tabbox).removeClass(cls);
            if (show) {
                
                if (!tabbox.isVertical() && !tabbox.inAccordionMold()) {
                    var tb = tabbox.toolbar;
                    tabbox.$n('left').style.height = tabbox.$n('right').style.height = '';
                    if (tb)
                        tb.$n().style.height = '';
                }

                jq(tabbox).addClass(cls);
                // refresh scrolling classes after buttons are shown and the tabs are rendered with their full width
                setTimeout(function() {tabbox._refreshScrollClasses();})
            }
        }
    },
    _scrollcheck: function () {
        _xTabs._scrollcheck.apply(this, arguments);
        // fixes the wrong scroll classes after resize from ancestor widget
        this.getTabbox()._refreshScrollClasses();
    }
})

// add disabled-scroll css class to scroll buttons if the user cannot scroll any further to that direction
// Potix #4013, Trac #3225
// fix defect tab scrolling on Chrome with browser zoom deactivated
// Potix #4327, Trac #3887
var DISABLE_SCROLL_CLASS = 'disabled-scroll';
var oTabbox = {};
zk.override(zul.tab.Tabbox.prototype, oTabbox, {
    _doClick: function(evt, direction) {
        if (!this.tabs || !this.tabs.nChildren) return; 

        var cave = this.tabs.$n('cave'),
            allTab =  jq(cave).children(),
            move = 0,
            tabbox = this,
            head = this.tabs.$n(),
            isVert = tabbox.isVertical(),
            scrollLength = (isVert ? head.scrollTop : head.scrollLeft),
            offsetLength = (isVert ? head.offsetHeight : head.offsetWidth),
            plus = scrollLength + offsetLength;
        var $tabs = jq(this.getTabs());
        var $tab = jq('.z-tab', $tabs);
        var $tabToScroll = null;
        
        switch (direction) {
        case 'right':
            //find first (partially) invisible tab, the tab's right position is larger than its parent's width
            $tab.each(function() {
                if (jq(this).position().left + jq(this).outerWidth() - 1 > $tabs.innerWidth()){
                    $tabToScroll = jq(this);
                    return false;
                }
            });
            if ($tabToScroll != null){
                $tabs.scrollLeft($tabs.scrollLeft() + $tabToScroll.position().left + $tabToScroll.outerWidth() - $tabs.innerWidth());
            }
            this._refreshScrollClasses();
            break;
        case 'left':
            //find first (partially) invisible tab
            jq($tab.toArray().reverse()).each(function() {
                if (jq(this).position().left < 0){
                    $tabToScroll = jq(this);
                    return false;
                }
            });
            if ($tabToScroll != null){
                $tabs.scrollLeft($tabs.scrollLeft() + $tabToScroll.position().left);
            }
            this._refreshScrollClasses();
            break;
        case 'up':
            for (var i = 0, count = allTab.length; i < count; i++) {
                if (allTab[i].offsetTop >= scrollLength) {
                    var preli = jq(allTab[i]).prev('li')[0];
                    if (!preli) return;
                    move = scrollLength - preli.offsetTop ;
                    this.tabs._doScroll('up', move);
                    return;
                };
            };
            var preli = allTab[allTab.length-1];
            if (!preli) return;
            move = scrollLength - preli.offsetTop ;
            this.tabs._doScroll('up', move);
            break;
        case 'down':
            for (var i = 0, count = allTab.length; i < count; i++) {
                if (allTab[i].offsetTop + allTab[i].offsetHeight > plus) {
                    move = allTab[i].offsetTop + allTab[i].offsetHeight - scrollLength - offsetLength;
                    if (!move || isNaN(move)) return ;
                    this.tabs._doScroll('down', move);
                    return;
                };
            }
            break;
        }
    },
    bind_: function (desktop, skipper, after) {
        oTabbox.bind_.apply(this, arguments);
        // display disabled style on either left or right scroll initially
        if (this.getSelectedIndex() == 0){
            jq(this.$n('left')).addClass(DISABLE_SCROLL_CLASS);
        } else if (this.getSelectedIndex() == this.getTabs().nChildren - 1){
            jq(this.$n('right')).addClass(DISABLE_SCROLL_CLASS);
        };

        // tabs are always scrolled all the way to the left on zoom change/resize
        var self = this;
        this.listen({onAfterSize: function() {
            jq(self.$n('right')).removeClass(DISABLE_SCROLL_CLASS);
            var $left = jq(self.$n('left'));
            if (!$left.hasClass(DISABLE_SCROLL_CLASS)) {
                $left.addClass(DISABLE_SCROLL_CLASS);
            }
        }});
    },
    _refreshScrollClasses: function() {
        var $tabs = jq(this.getTabs());
        var $tab = jq('.z-tab', $tabs);
        var $tabToScroll = null;
        
        $tab.each(function() {
            if (jq(this).position().left + jq(this).outerWidth() - 1 > $tabs.innerWidth()){
                $tabToScroll = jq(this);
            }
        });

        jq(this.$n('right')).removeClass(DISABLE_SCROLL_CLASS);
        jq(this.$n('left')).removeClass(DISABLE_SCROLL_CLASS);

        if ($tabToScroll == null){
            jq(this.$n('right')).addClass(DISABLE_SCROLL_CLASS);
        }

        if ($tabs.scrollLeft() == 0){
            jq(this.$n('left')).addClass(DISABLE_SCROLL_CLASS);
        }
    }
});
})();

}finally{zk.setLoaded(zk._p.n);}});zk.setLoaded('ppi.tab',1);zk.load('zul.wgt,zk.fmt',function(){if(zk._p=zkpi('zkmax.wgt'))try{

(function () {
	
	var _dragShows = {};
	
	function _preventDefault(evnt) {
		evnt.stop();
	}
	
	function _copyEffect(evnt) {
		evnt.originalEvent.dataTransfer.dropEffect = 'copy';
	}
	
	function _noneEffect(evnt) {
		evnt.originalEvent.dataTransfer.dropEffect = 'none';
	}
	
	function _stopAndCopyEffect(evnt) {
		_preventDefault(evnt);
		_copyEffect(evnt);
	}
	
	function _stopAndNoneEffect(evnt) {
		_preventDefault(evnt);
		_noneEffect(evnt);
	}
	
	function _banBrowserDrop() {
		jq(document).bind('dragenter dragover', _stopAndNoneEffect);
	}
	
	function _unBanBrowserDrop() {
		jq(document).unbind('dragenter dragover', _stopAndNoneEffect);
	}
	
	function _detectionBrowser(wgt) {
		jq(document).bind('dragenter dragover', wgt.proxy(wgt._dragShow))
			.bind('dragleave drop', wgt.proxy(wgt._dragHide));
	}
	
	function _undetectionBrowser(wgt) {
		jq(document).unbind('dragenter dragover', wgt.proxy(wgt._dragShow))
			.unbind('dragleave drop', wgt.proxy(wgt._dragHide));
	}

zkmax.wgt.Dropupload = zk.$extends(zul.Widget, {
	_sid: 0,
	_detection: 'browser',
	_content: '',
	_native: false,
	_anchorUuid: '',
	$init: function () {
		this.$supers('$init', arguments);
		this._uploaders = {};
		this._last = {};
		this._error = true;
	},
	$define: {
		
		
		maxsize: null,
		
		
		viewerClass: null,
		
		
		detection: null,
		
		
		content: [function (v) {
			return v ? v : ''; 
		}, null],
	
		
		
		anchorUuid: function (v) {
			this._shallSyncSize = true;
		}
	},

	
	setNative: function (value) {
		this._native = value;
	},

	
	isNative: function () {
		return this._native;
	},
		
	bind_: function (desktop, skipper, after) {
		this.$supers('bind_', arguments);

		
		var detection = this.getDetection(),
			self = jq(this.$n());
		
		self.bind('drop', this.proxy(this._dropAction));
		
		switch (detection) {
		case 'self':
			self.bind('dragenter dragover', this.proxy(this._showContent))
				.bind('dragleave', this.proxy(this._hideContent));
			_banBrowserDrop();
			this._setContentVisible(false);
			break;
		case 'browser':
			
			self.bind('dragenter dragover', _copyEffect)
				.bind('dragleave', _noneEffect);
			_detectionBrowser(this);
			this.hide();
			break;
		case 'none':
			self.bind('dragenter dragover', _stopAndCopyEffect);
			_banBrowserDrop();
			break;
		default:
			var thiz = this;
			after.push(function () {
				var area = jq(thiz.$f(detection));
				if (area.length) { 
					self.bind('dragenter dragover', _stopAndCopyEffect);
					area.bind('dragenter dragover', thiz.proxy(thiz._showContentNoneDrop))
						.bind('dragleave', thiz.proxy(thiz._hideContent));
					_banBrowserDrop();
					thiz._setContentVisible(false);
				} else {
					
					self.bind('dragenter dragover', _copyEffect)
						.bind('dragleave', _noneEffect);
					_detectionBrowser(thiz);
					thiz.hide();
				}
			});
		}

	},
	
	unbind_: function () {
		var detection = this.getDetection(),
			self = jq(this.$n()).unbind('drop', this.proxy(this._dropAction));
		
		var area = jq(this.$f(detection));
		if (detection == 'self') {
			self.unbind('dragenter dragover', this.proxy(this._showContent))
				.unbind('dragleave', this.proxy(this._hideContent));
			_unBanBrowserDrop();
		} else if (detection == 'none') {
			self.unbind('dragenter dragover', _stopAndCopyEffect);
			_unBanBrowserDrop();
		} else if (detection == 'browser' || !area.length) {
			self.unbind('dragenter dragover', _copyEffect)
				.unbind('dragleave', _noneEffect);
			_undetectionBrowser(this);
		} else {
			self.unbind('dragenter dragover', _stopAndCopyEffect);
			area.unbind('dragenter dragover', this.proxy(this._showContentNoneDrop))
				.unbind('dragleave', this.proxy(this._hideContent));
			_unBanBrowserDrop();
		}

		if (_dragShows[this.uuid]) {
			delete _dragShows[this.uuid];
		}
		
		this.$supers('unbind_', arguments);
	},
	
	_showContent: function (evnt) {
		_stopAndCopyEffect(evnt);
		this._setContentVisible(true);
		this._shallContentHide = false;
	},
	
	_showContentNoneDrop: function (evnt) {
		_stopAndNoneEffect(evnt);
		this._setContentVisible(true);
		this._shallContentHide = false;
	},
	
	_hideContent: function (evnt) {
		_preventDefault(evnt);
		if (!this._shallContentHide) {
			var self = this;
			setTimeout(function () {
				if (self && self.desktop && self._shallContentHide)
					self._setContentVisible(false);
			}, 50);
		}
		this._shallContentHide = true;
	},
	
	_setContentVisible: function (flag) {
		var label = jq(this.$n()).children();
		flag ? label.show() : label.hide();
	},
		
	_dragHide: function (evnt) {
		_preventDefault(evnt);
		delete _dragShows[this.uuid];
		if (!this._shallHide) {
			var self = this;
			setTimeout(function () {
				if (self && self.desktop && self._shallHide) {
					self.setVisible(false);
				}
			}, 50);
		}
		this._shallHide = true;
	},
	
	_dragShow: function (evnt) {
		_preventDefault(evnt);
		if (evnt.originalEvent.dataTransfer.types.length) {
			var fileType = evnt.originalEvent.dataTransfer.types[0];
			if (fileType != 'Files' 
					&& fileType != 'public.file-url' 
					&& fileType != 'application/x-moz-file') { 
				_banBrowserDrop();
				return; 
			}
			_unBanBrowserDrop();
		}
		
		if (evnt.originalEvent.dataTransfer.dropEffect != 'copy') {
			_noneEffect(evnt);
		}

		this._shallHide = false;
		_dragShows[this.uuid] = this;
		
		var n = this.$n(),
			last = this._last,
			auuid = this._anchorUuid,
			size = 0, key;
		
		for (key in last) {
			if (last[key]) size++;
		}
		
		var shallSyncSize = this._shallSyncSize;
		if (n) {
			if (auuid) {
				var lastAnchor = this._lastAnchor || {};
				var $anchor = jq(auuid, zk),
					$n = jq(n),
					ofs = $anchor.offset(),
					nofs = $n.offset();
				
				var newWidth = $anchor.width(),
					newHeight = $anchor.height(),
					newTop = ofs.top, newLeft = ofs.left;
				
				
				if (lastAnchor['w'] != newWidth || lastAnchor['h'] != newHeight
					|| lastAnchor['t'] != newTop || lastAnchor['l'] != newLeft) {
					shallSyncSize = true;
					
					lastAnchor['w'] = newWidth;
					lastAnchor['h'] = newHeight;
					lastAnchor['t'] = newTop;
					lastAnchor['l'] = newLeft;
					this._lastAnchor = lastAnchor;
				}
				
				if (shallSyncSize) {
					n.style.position = 'fixed';
					n.style.zIndex = '10000';
					if (size == 0) {
						var nWidth = $n.width(), nHeight = $n.height();
						last['w'] = nWidth ? nWidth + 'px' : '';
						last['h'] = nHeight ? nHeight + 'px' : '';
						last['t'] = nofs.top ? nofs.top + 'px' : '';
						last['l'] = nofs.left ? nofs.left + 'px' : '';
					}
					
					if ($anchor.size() > 0) {
						n.style.width = newWidth + 'px';
						n.style.height = newHeight + 'px';
						n.style.top = newTop + 'px';
						n.style.left = newLeft + 'px';
					}
				}
			} else if (shallSyncSize) {
				
				n.style.position = 'static';
				n.style.zIndex = 'auto';
					
				n.style.width = last['w'] || '100px';
				n.style.height = last['h'] || '100px';
				n.style.top = last['t'] || 'auto';
				n.style.left = last['l'] || 'auto';
				
				this._last = {};
			}
			
			this._shallSyncSize = false;
		}
		
		this.setVisible(true);
	},
	
	_dropAction: function (evnt) {
		_preventDefault(evnt);
		
		
		for (var id in _dragShows)
			this._dragHide.call(_dragShows[id], evnt);
		
		if (this.getDetection() == 'self') {
			this._setContentVisible(false);
		}

		var files = evnt.originalEvent.dataTransfer.files;

		
		var maxsize = this._maxsize > 0 ? this._maxsize * 1024 : -1;
		if (maxsize != -1) {
			var alert = [];
			for (var i = 0; i < files.length; i++) {
				var file = files[i],
					size = file.size,
					kbFileSize = size / 1024,
					kbMaxSize = maxsize / 1024,
					kb = ' ' + msgzk.KBYTES,
					mb = ' ' + msgzk.MBYTES,
					fileKBytes = Math.round(kbFileSize) + kb,
					maxKBytes = Math.round(kbMaxSize) + kb;
				if (size > maxsize) {
					alert.push(file.name);
					alert.push(zk.fmt.Text.format(msgzul.UPLOAD_ERROR_EXCEED_MAXSIZE,
						fileKBytes, maxKBytes, size, maxsize, fileKBytes, maxKBytes,
						Math.round(kbFileSize / 1024) + mb, Math.round(kbMaxSize / 1024) + mb));
				}
			}
			if (alert.length > 0) {
				zk.load('zul.wnd,zul.wgt,zul.box', function () {
					var chds = [];
					for (var i = 0; i < alert.length; i += 2) {
						chds.push(new zul.wgt.Label({
							id: 'msg' + i,
							value: alert[i],
							style: 'font-weight: bold'
						}));
						chds.push(new zul.wgt.Label({
							id: 'msg' + i + 1,
							value: alert[i + 1]
						}));
						if (!(i == alert.length - 2)) chds.push(new zul.wgt.Separator({bar: true}));
					}
					var wnd = new zul.wnd.Window({
						id: 'aualert',
						closable: true,
						width: '250pt',
						title: zk.appName,
						border: 'normal',
						children: [
							new zul.box.Box({
								mold: 'horizontal',
								children: [
									new zul.wgt.Div({sclass: 'z-messagebox-icon z-messagebox-error'}),
									new zul.wgt.Div({
										id: 'content',
										sclass: 'z-messagebox',
										width: '210pt',
										style: 'overflow:auto',
										children: [
											new zul.box.Box({
												mold: 'vertical',
												children: chds
											})
										]
									})
								]
							}),
							new zul.wgt.Separator({bar: true}),
							new zul.box.Box({
								mold: 'horizontal',
								style: 'margin-left:auto; margin-right:auto',
								children: [
									new zul.wgt.Button({
										label: 'OK',
										listeners: {
											onClick: function (evt) {
												this.$o().detach();
											}
										}
									})
								]
							})
						],
						mode: 'modal'
					});
					var p = zk.Desktop.$();
					if (p && (p = p.firstChild) && p.desktop)
						p.appendChild(wnd);
					else
						jq(document.body).append(wnd);
				});
				return;
			}
		}
		
		
		var items = evnt.originalEvent.dataTransfer.items;
		for (var i = 0; i < files.length; i++) {
			var skipFolder = false;
			if (items) {
				var entry = items[i];
				if (entry.getAsEntry) { 
					skipFolder = entry.getAsEntry().isDirectory;
				} else if (entry.webkitGetAsEntry) { 
					skipFolder = entry.webkitGetAsEntry().isDirectory;
				}
			}
			
			if (!skipFolder) {
				this._sid++;
				var uploader = new zkmax.wgt.DropUploader(this, this._genKey(), files[i]);
				this._uploaders[uploader.id] = uploader;
			}
		}
		this.checkFinish();
	},

	_genKey: function () {
		return this.uuid + '_uplder_' + this._sid;
	},
	
	cancel: function (viewId) {
		delete this._uploaders[viewId];
		this.checkFinish();
	},
	
	checkFinish: function () {
		var finish = false;
		for (var id in this._uploaders) {
			if (!(finish = this._uploaders[id].isFinish())) {
				this._uploaders[id].start();
				break;
			}
			var resText = this._uploaders[id].xhr.responseText;
			resText.startsWith('error') ? jq.alert(resText.substring(6), {icon: 'ERROR'}) : this._error = false;
			delete this._uploaders[id]; 
		}
		if (finish && !this._error) {
			this.fire('onUpload');
			this._error = true;
		}
	},
	
	domContent_: function () {
		return '<div id="' + this.uuid + '-content">'
				+ this.getContent() +	'</div>';
	}
});



function _addDUM(uploader) {
	var flman = zkmax.wgt.DropUploadViewer.fileManager;
	if (!flman || !flman.desktop) {
		if (flman) flman.detach();
		zkmax.wgt.DropUploadViewer.fileManager = flman = new zkmax.wgt.DropUploadManager();
		uploader.getWidget().getPage().appendChild(flman);
	}
	flman.removeFile(uploader);
	flman.addFile(uploader);
}

function _initDUM(uploader) {
	if (zkmax.wgt.DropUploadManager)
		return _addDUM(uploader);

	zk.load('zul.wgt,zul.box', function () {
		
		zkmax.wgt.DropUploadManager = zk.$extends(zul.wgt.Popup, {
			$init: function () {
				this.$supers('$init', arguments);
				this._files = {};
				this.setSclass('z-fileupload-manager');
			},
			onFloatUp: function (ctl) {
				if (!this.isVisible())
					return;
				this.setTopmost();
			},
			
			getFileItem: function (id) {
				return this._files[id] || zk.Widget.$(id);
			},
			
			addFile: function (uploader) {
				var id = uploader.id,
					flnm = uploader.file.name,
					prog = this.getFileItem(id);
				if (!prog) {
					prog = new zul.wgt.Div({
						uuid: id,
						children: [new zul.wgt.Label({
							value: flnm + ':'
						}), new zul.box.Box({
							mold: 'horizontal',
							children: [new zkmax.wgt.Dropuploadprogress({
								id: id,
								sclass: 'z-fileupload-progress'
							})
							, new zul.wgt.Div({
								sclass: 'z-fileupload-remove z-icon-times',
								listeners: {
									onClick: function () {
										uploader.cancel();
									}
								}
							})]
						}), new zul.wgt.Label({id: id + '_total'}), new zul.wgt.Separator()]
					});
					
					try {
						this.appendChild(prog);
					} catch (e) {}
					this._files[id] = prog;
				}
				return prog;
			},
			
			updateFile: function (uploader, val, total) {
				var id = uploader.id,
					prog = this.getFileItem(id);
				if (!prog) return;
				prog.$f(id).setValue(val);
				prog.$f(id + '_total').setValue(total);
			},
			
			removeFile: function (uploader) {
				var id = uploader.id,
					prog = this.getFileItem(id);
				if (prog)
					prog.detach();
				delete this._files[id];
				var close = true;
				for (var p in this._files)
					if (!(close = false))
						break;
				
				if (close)
					this.close();
			},
			
			open: function (wgt, position) {
				this.$super('open', wgt, null, position || 'after_start', {
					sendOnOpen: false,
					disableMask: true
				});
			}
		});
		_addDUM(uploader);
	});
}




zkmax.wgt.DropUploadViewer = zk.$extends(zk.Object, {
	
	$init: function (uploader, file) {
		this.uploader = uploader;
		_initDUM(uploader);
	},
	
	
	update: function (sent, total) {
		var fileManager = zkmax.wgt.DropUploadViewer.fileManager;
		if (fileManager) {
			if (!fileManager.isOpen()) {
				fileManager.open(this.uploader.getWidget());
			}
			
			fileManager.updateFile(this.uploader, sent * 100 / total, msgzk.FILE_SIZE + Math.round(total / 1024) + msgzk.KBYTES);
		}
	},
	destroy: function () {
		var fileManager = zkmax.wgt.DropUploadViewer.fileManager;
		if (fileManager) {
			fileManager.removeFile(this.uploader);
		}
	}
});


zkmax.wgt.DropUploader = zk.$extends(zk.Object, {
	_status: 100,
	
	
	$init: function (upload, id, file) {
		this.upload = upload;
		this.id = id;
		this.file = file;
		
		var viewer, self = this;
		if (upload.getViewerClass()) {
			zk.$import(upload.getViewerClass(), function (cls) {
				viewer = new cls(self, file);
			});
		} else {
			
			viewer = new zkmax.wgt.DropUploadViewer(this, file);
		}
		this.viewer = viewer;
		
		this.xhr = new XMLHttpRequest();
		this.xhr.upload.onprogress = this.progressFunc();
		this.xhr.onload = this.completeFunc();
		
		this.xhr.onerror = this.errorFunc();
	},
	
	
	getWidget: function () {
		return this.upload;
	},
	
	
	isFinish: function () {
		return this._status == 300;
	},
	
	start: function () {
		if (this._status == 200) return;	
		
		this._status = 200;
		var formData = new FormData();
		formData.append('file', this.file);

		var upload = this.upload;
		var dt = upload.desktop;
		var ajaxUri = zk.ajaxURI('/dropupload', {desktop: dt,au: true})
				+ '?uuid=' + upload.uuid
				+ '&dtid=' + dt.id
				+ '&native=' + upload._native;
		
		var xhr = this.xhr;
		xhr.open('POST', ajaxUri, true);
		xhr.send(formData);
	},
	
	cancel: function () {
		if (this.xhr) {
			this.xhr.abort();
		}
		this.viewer.destroy();
		this.upload.cancel(this.id);
	},
	
	progressFunc: function () {
		var viewer = this.viewer;
		return function (evnt) {	
			viewer.update(evnt.loaded, evnt.total);
		};
	},
	
	completeFunc: function () {
		var uploader = this;
		return function (evnt) {
			uploader._status = 300;
			uploader.viewer.destroy();
			uploader.upload.checkFinish();
		};
	},

	
	errorFunc: function () {
		var uploader = this;
		return function (evnt) {
			uploader.cancel();
		};
	}
});


zkmax.wgt.Dropuploadprogress = zk.$extends(zul.wgt.Progressmeter, {
	_fixImgWidth: _zkf = function () {
		var n = this.$n(),
		img = this.$n('img');
		if (img) {
			if (zk(n).isRealVisible()) 
				var $img = jq(img);
			$img.animate({
				width: Math.round((n.clientWidth * this._value) / 100) + 'px'
			}, $img.zk.getAnimationSpeed(100));
		}
	}
});
})();
zkreg('zkmax.wgt.Dropupload');zk._m={};
zk._m['default']=function (out) {
	out.push( '<div', this.domAttrs_(), ' >', this.domContent_(), '</div>' );
}
;zkmld(zk._p.p.Dropupload,zk._m);
}finally{zk.setLoaded(zk._p.n);}});zk.setLoaded('zkmax.wgt',1);zk.load('zul,zk',function(){if(zk._p=zkpi('ppi.upload'))try{

/**
 * Resets the shallSyncSize property for the given Dropupload-widget
 * 
 * @memberOf ppi.upload
 * @static
 */
ppi.upload.reactivateDropuploadResize = function(uuid) {
    var wgt = zk.$(uuid);
    if (wgt) wgt._shallSyncSize = true;
};
(function() {
var _zulUploader = {};
zk.override(zul.Uploader.prototype, _zulUploader, {
    // Trac #2082, ZK #3142
    start : function() {
        var wgt = this._wgt, frameId = this.id + '_ifm';

        document.body.appendChild(this._parent);
        if (!jq('#' + frameId).length)
            jq.newFrame(frameId);
        
        // START PATCH: FF not responding from upload of network resources
        if (zk.ie9) { // old impl only for IE9
            this._form.target = frameId;
            this._form.submit();
        } else {
            // start xhr upload
            jq(this._form).ajaxForm({
                uploadProgress: function(event, position, total, percentComplete) {
                    wgt.ajaxPercentComplete = percentComplete;
                    wgt.ajaxTotal = total;
                },
                success: function(responseText, statusText) {
                    jq('#' + frameId).html(responseText);
                }
            });
            jq(this._form).submit();
        }
        // END PATCH
        this._form.style.display = "none";

        var self = this,
            data = 'cmd=uploadInfo&dtid=' + wgt.desktop.id
                + '&wid=' + wgt.uuid + '&sid=' + this._sid;

        if (zul.Uploader._tmupload)
            clearInterval(zul.Uploader._tmupload);

        function t() {
            jq.ajax({
                type: 'POST',
                url: zk.ajaxURI('/upload', {desktop: wgt.desktop, au: true}),
                data: data,
                dataType: 'text',
                success: function(data) {
                    var d = data.split(',');
                    if (data.startsWith('error:')) {
                        // FEATURE: we do not want to show an error box whenever the user aborts an upload
                        if (!data.contains('Upload aborted')) {
                            // END FEATURE
                            self._echo = true;
                            zul.Uploader.clearInterval(self.id);
                            if (wgt) {
                                self.cancel();
                                zul.Upload.error(data.substring(6, data.length), wgt.uuid, self._sid);
                            }
                        }
                    
                    } else {
                        // START PATCH: FF not responding from upload of network resources
                        progress = wgt.ajaxPercentComplete || d[0] || 0;
                        // END PATCH
                        if (!self.update(zk.parseInt(progress), zk.parseInt(d[1])))
                            zul.Uploader.clearInterval(self.id);
                    }
                },
                complete: function (req, status) {
                    var v;
                    if ((v = req.getResponseHeader('ZK-Error')) == '404'
                    || v == '410' || status == 'error'
                    || status == 404 || status == 405 || status == 410) {
                        zul.Uploader.clearInterval(self.id);
                        var wgt = self.getWidget();
                        if (wgt) {
                            self.cancel();
                            zul.Upload.error(msgzk.FAILED_TO_RESPONSE, wgt.uuid, self._sid);
                        }
                        return;
                    }
                }
            });

            // START PATCH: FF not responding from upload of network resources
            // update view outside of request/reply to avoid unresponsive firefox when loading from a network location
            var progress = wgt.ajaxPercentComplete;
            var total = wgt.ajaxTotal;
            if (progress && !self.update(zk.parseInt(progress), total))
                zul.Uploader.clearInterval(self.id);
            // END PATCH
        }
        
        t.id = this.id;
        zul.Uploader.clearInterval = function(id) {
            if (t.id == id) {
                clearInterval(zul.Uploader._tmupload);
                zul.Uploader._tmupload = undefined;
            }
        };

        // BUG: adjust standard interval to the same as Dropupload (ZK-3268)
        zul.Uploader._tmupload = setInterval(t, 100);
        
        // FEATURE: remove autodisable to be able to cancel upload (ZK-3270)
        zul.wgt.ADBS.autodisable(wgt);
    }
});

//ZK #3579/ZK-3272
zk.afterLoad('zk.fmt', function() {
    zk.fmt.Text.stuff = zk.fmt.Text.format;

    zk.fmt.Text.formatFileSize = function (bytes) {
        var divider = 1024;

        if(Math.abs(bytes) < divider) {
            return bytes + msgzk.BYTES;
        }

        var units = [msgzk.KBYTES, msgzk.MBYTES, msgzk.GBYTES, msgzk.TBYTES]
        var unit = -1;

        do {
            bytes /= divider;
            ++unit;
        } while(Math.abs(bytes) >= divider && unit < units.length - 1);

        return +bytes.toFixed(1) + ' ' + units[unit];
    }

    zk.fmt.Text.format = function (msg) {
        if (msg == msgzul.UPLOAD_ERROR_EXCEED_MAXSIZE)
            return zk.fmt.Text.stuff(msg, zk.fmt.Text.formatFileSize(arguments[3]), zk.fmt.Text.formatFileSize(arguments[4]));
        else
            return zk.fmt.Text.stuff.apply(this, arguments);
    }
});

zk.afterLoad('zkmax.wgt', function() {
    // #3579/ZK-3272, former Trac #2134, ZK #3096/#3086
    var _origDropuploadImpl = {};
    zk.override(zkmax.wgt.Dropupload.prototype, _origDropuploadImpl, {
        _dropAction : function(evt) {
            evt.stop();
            if (Object.keys(this._uploaders).length > 0) {
                return;
            }
            // no multidrop allowed even before file size check
            if (evt.originalEvent.dataTransfer.files.length > 1) {
                jq.alert(msgzk.MESSAGE_FILE_UPLOAD_NO_MULTIDROP, {icon:'ERROR'});
                this.setVisible(false);
                return;
            }
            _origDropuploadImpl._dropAction.apply(this, arguments);
        },
        _dragShow : function (evnt) {
            if (Object.keys(this._uploaders).length === 0) {
                _origDropuploadImpl._dragShow.apply(this, arguments);
            }
        }
    });

    // Support-Ticket 170228sk1219tp, Potix #4374
    var _origDropuploaderImpl = {};
    zk.override(zkmax.wgt.DropUploader.prototype, _origDropuploaderImpl, {
        start: function () {
            if (this._status == 200) return;    
            
            this._status = 200;
            var formData = new FormData();
            formData.append('file', this.file);

            var upload = this.upload;
            var dt = upload.desktop;
            var ajaxUri = zk.ajaxURI('/dropupload', {desktop: dt,au: true});
            
            if (ajaxUri.indexOf('?') > -1) {
                ajaxUri += '&uuid=' + upload.uuid
                            + '&dtid=' + dt.id
                            + '&native=' + upload._native;
            } else {
                ajaxUri += '?uuid=' + upload.uuid
                            + '&dtid=' + dt.id
                            + '&native=' + upload._native;
            }
            
            var xhr = this.xhr;
            xhr.open('POST', ajaxUri, true);
            xhr.send(formData);
        }
    });
});
})();

/**
 * Viewer for file upload which was initiated via drag and drop over an existing component
 * @extends zk.Object
 */
ppi.upload.DropuploadViewer = zk.$extends(zk.Object, {

    /** @field */
    _widgets : null,

    /** @field */
    _uplder : null,
    
    /** @field */
    _dropUpload : null,

    /** @constructor */
    $init : function(uplder, fileobj) {
        this._uplder = uplder;
        var dropUpload = zk.$('#' + uplder.upload.uuid);
        this.dropUpload = dropUpload;
        
        // Try to retrieve the filename from path.
        var filename = fileobj.name; // for Dropupload
        if (!filename && jq.type(fileobj) === 'string') {
            // for standard Upload
            filename = fileobj.replace(/^.*[\\\/]/, '');
        }
        
        this._widgets = {
            progressBar : new zul.wgt.Progressmeter({id : dropUpload.uuid + '-progress-bar'}),
            percCont : new zul.wgt.Div({
                id : dropUpload.uuid + '-progress-percent-div',
                children : [ new zul.wgt.Html({
                    content : [ '<span class="z-label" id="', dropUpload.uuid, '-progress-percent">0%</span>' ].join("")
                }) ]
            }),
            text : new zul.wgt.Label({
                id : dropUpload.uuid + '-progress-text',
                sclass : 'message-dialog-text',
                value : zk.fmt.Text.stuff(dropUpload._dialogTextWithFilename, filename)
            })
        };
        this._widgets.progressBarContainer = new zul.wgt.Div({
            id : dropUpload.uuid + "-progressBarContainer",
            sclass : 'progressBarContainer',
            children : [ this._widgets.percCont,
                         this._widgets.progressBar]
        });
        this._widgets.progress = new zul.wgt.Div({
            id : dropUpload.uuid + "-progress",
            sclass : 'popup-content',
            children : [ this._widgets.text,
                         this._widgets.progressBarContainer ]
        });
        this._widgets.dialog = new zul.wnd.Window({
            id : dropUpload.uuid + '-dialog',
            closable : true,
            border : true,
            mode : 'modal',
            sclass : 'progress-dialog standard-dialog nobutton',
            children : [ new zul.wgt.Caption({
                             id :'caption',
                             label : dropUpload._dialogTitle,
                             hflex : 'min'}),
                         this._widgets.progress ]})
        
        var anchor = zk.$(uplder.getWidget().getAnchorUuid());

        // first, render
        this._widgets.dialog.show();
        anchor.getPage().appendChild(this._widgets.dialog);

        // then modify parameters
        jq(this._widgets.progressBar.$n()).data('animationspeed', 100);
        
        var self = this;
        this._widgets.dialog.listen({
            onClose : function(evt) {
                self._uplder.cancel();
                
                // stop event, because the standard window onClose behavior will cause a js error
                // trying to detach it, which we already do in the destroy() function of this viewer
                evt.stop();
            }
        });
        
        zAu.cmd1.resizeWgt(this._widgets.dialog);
    },

    /**
     * Updates the upload information process box
     * @param already uploaded file size in bytes
     * @param total total file size in bytes
     * 
     * @memberOf ppi.upload.DropuploadViewer
     */
    update : function(sent, total) {
        var percent;
        var sentBytes = sent;
        try {
            if (sent <= 100) {
                percent = sent;
                sentBytes = Math.round(total * sent / 100);
            } else {
                var effectiveTotal = total;
                if (total === undefined) {
                    effectiveTotal = 1;
                }
                percent = Math.round(sent * 100 / effectiveTotal);
            }
            percent = percent === '' ? '0' : percent;

            this._widgets.progressBar.setValue(percent);
        } catch (e) {
            percent = '?';
        }
        jq('#' + this.dropUpload.uuid + '-progress-percent').html(
            zk.fmt.Text.formatFileSize(sentBytes)
            + '/' + zk.fmt.Text.formatFileSize(total)
            + ' (' + percent + '%)');
    },

    /**
     * Called when upload is finished.
     * @memberOf ppi.upload.DropuploadViewer
     */
    destroy : function() {
        // detach modal window if finished or canceled
        var self = this;
        ppi.util.doOnAfterResponse(function() {self._widgets.dialog.detach();});
        // necessary for dropupload reset in case of errors
        if (this._uplder.getWidget()) {
            this._uplder.getWidget().fire('onUploadAreaChanged', {}, {toServer:true})
        }
    }
});
zkreg('ppi.upload.DropuploadViewer');// FileUpload-Handle for TransfersNewUploadController
ppi.upload.TransfersDialogViewer = zk.$extends(zk.Object, {

    /** @field */
    updated : null,

    /** @constructor */
    $init : function(uplder, fileobj) {
        this._uplder = uplder;
        var viewerId = uplder.id;
        this._widgetFileUpload = zk.$('$fileUploadArea');

        var fileUploadAreaContent = this._widgetFileUpload.firstChild;
        if (fileUploadAreaContent && fileUploadAreaContent.isVisible())
            fileUploadAreaContent.hide();

        this._jqFileUploadArea = jq('#' + this._widgetFileUpload.uuid);
        this._jqFileUploadArea.addClass('uploading');
        // We have to cancel the uploader here, since it's interval update causes error otherwise
        var parentDialog = this._jqFileUploadArea.closest('.file-upload-dialog').zk.$();
        parentDialog.listen({"onClose" : function(event) { uplder.cancel(); } });

        if (uplder.upload)
            jq('#' + uplder.upload.uuid).hide();

        var htmlFileName = null;

        // Try to retrieve the filename from path.
        var filename = fileobj.name; // for Dropupload
        if (!filename && jq.type(fileobj) === 'string') {
            // for standard Upload
            filename = fileobj.replace(/^.*[\\\/]/, '');
        }
        if (filename)
            htmlFileName = new zul.wgt.Html({ content : [ '<span id="', viewerId, '-fname">', filename, ', </span>' ].join("") });

        var htmlProgress = new zul.wgt.Html({
            content : [ '<span class="z-label">', msgzk.FILE_SIZE, '</span>', '<span class="z-label" id="', viewerId,
                    '-sent">0 ', msgzk.KBYTES, '</span> / ', '<span id="', viewerId, '-total">0 ', msgzk.KBYTES,
                    '</span>' ].join("")
        });

        this._widgetProgressbar = new zul.wgt.Progressmeter({id: viewerId + "-progressbar"});
        this._widgetProgressbar.appendChild(new zul.wgt.Div({
            id : viewerId + '-percent-container',
            sclass : 'upload-percentage',
            children : new zul.wgt.Html({ content : [ '<span id="', viewerId, '-percent">0%</span>' ].join("") })
        }));

        this._widgetFileUpload.appendChild(new zul.box.Vlayout({
            id : viewerId,
            sclass : 'upload-progress',
            children : [ new zul.box.Hlayout({
                id : viewerId + "-progress",
                sclass : "upload-progress",
                spacing : "6px",
                children : [ htmlFileName, htmlProgress ]
            }), this._widgetProgressbar ]
        }));
        
        // $n() is only accessible after attached to the DOM
        this._widgetProgressbar.$n().setAttribute('data-animationspeed', 100);
    },

    /**
     * Updates the upload information process box
     * @param already uploaded file size in bytes
     * @param total total file size in bytes
     * 
     * @memberOf ppi.upload.TransfersDialogViewer
     */
    update : function(sent, total) {
        var percent;
        var sentBytes = sent;
        try {
            if (sent <= 100) {
                percent = sent;
                sentBytes = Math.round(total * sent / 100);
            } else {
                var effectiveTotal = total;
                if (total === undefined) {
                    effectiveTotal = 1;
                }
                percent = Math.round(sent * 100 / effectiveTotal);
            }
            percent = percent == '' ? '0' : percent;

            this._widgetProgressbar.setValue(percent);
        } catch (e) {
            percent = '?';
        }
        jq('#' + this._uplder.id + '-percent').html(percent + '%');
        jq('#' + this._uplder.id + '-sent').html(zk.fmt.Text.formatFileSize(sentBytes));

        if (!this.updated) {
            if (total == undefined && !this.updated) {
                jq('#' + this._uplder.id + '-total').html('? B');
            } else {
                jq('#' + this._uplder.id + '-total').html(zk.fmt.Text.formatFileSize(total));
                this.updated = true;
            }
        }
    },

    /**
     * Called when upload is finished.
     * @memberOf ppi.upload.TransfersDialogViewer
     */
    destroy : function(finished) {
        this._widgetProgressbar.setValue(100);
        this._widgetFileUpload.$f(this._uplder.id).hide();
        this._jqFileUploadArea.removeClass('uploading');

        if (!finished) {
            var fileUploadAreaContent = this._widgetFileUpload.firstChild;
            if (fileUploadAreaContent && !fileUploadAreaContent.isVisible()) {
                fileUploadAreaContent.show();
            }
        }
        if (this._uplder.getWidget()) {
            this._uplder.getWidget().fire('onUploadAreaChanged', {}, {toServer:true})
        }
    }
});

zkreg('ppi.upload.TransfersDialogViewer');
}finally{zk.setLoaded(zk._p.n);}});zk.setLoaded('ppi.upload',1);zk.load('zul.grid',function(){if(zk._p=zkpi('ppi.grid'))try{

(function() {
    function keepBetween(number, min, max) {
        return Math.max(Math.min(number, max), min)
    }

/**
 * Widget class for the client-side LargeGrid.
 * @class ppi.grid.LargeGrid
 */
ppi.grid.LargeGrid = zk.$extends(zul.grid.Grid, {
    
    // Server -> Client 
    setClearAfterSearch: function(args) {
            this._clearAfterSearch(false, args[0], args[1], args[2]);
    },
    setCommands: function(cmd) {
        for(var i = 0; i < cmd.length; i++) {
            if (this[cmd[i]]) {
                this[cmd[i]]();
            }
        }
    },
    setNewPosition: function(pos) {
        this._scrollIfNeeded(pos);
    },
    setReset: function(args) {
        this._reset(args[0], args[1]);
    },
    setServerInit: function(args) {
        this._init(args[0], args[1], args.length == 3 ? args[2] : null);
    },
    
    updateButtons_: function(){
        var wgt = this,
        buttonUp = zk.Widget.$(wgt.$f('pageUp')),
        buttonDown = zk.Widget.$(wgt.$f('pageDown')),
        buttonHome = zk.Widget.$(wgt.$f('home')),
        buttonEnd = zk.Widget.$(wgt.$f('end')),
        slider = wgt.$f('slider');
        
        if(!slider){
            return;
        }
        var curpos =slider.getCurpos();
        
        var isMin = 0 === curpos || !wgt.isScrollable();
        var isMax = slider.getMaxpos() === curpos || !wgt.isScrollable();
        
        buttonUp.setDisabled(false);
        buttonDown.setDisabled(false);
        buttonHome.setDisabled(false);
        buttonEnd.setDisabled(false);
        
        buttonUp.setDisabled(isMin);
        buttonDown.setDisabled(isMax);
        buttonHome.setDisabled(isMin);
        buttonEnd.setDisabled(isMax);
    },
    
    
    /**
     * Binds all components on rendering
     * 
     * @protected
     * @memberOf ppi.grid.LargeGrid
     */
    bind_: function(desktop, skipper, after) {
        this.$supers('bind_', arguments);
        // initial parameters
        this._rowHeight = 32; //default height in ZK's Breeze theme
        this._pageStart = 0;
        this._wheelStep = 5;
        this._curpos = 0;
        this._pageViewStart = 0;
        jq(this.ebodytbl).css('position', 'relative');

        var wgt = this,
        spaceOwner = wgt.$o(),
        slider = wgt.$f('slider'),
        buttonUp = wgt.$f('pageUp'),
        buttonDown = wgt.$f('pageDown'),
        buttonHome = wgt.$f('home'),
        buttonEnd = wgt.$f('end');
        
        
        
        wgt._slider = slider;
        wgt._min = slider.getMinpos();
        wgt._max = slider.getMaxpos();
        
        // register event on slider and buttons
        slider.listen({'onScroll': function() {
            wgt._doScrollToView();
            wgt.updateButtons_();
        }});
        buttonUp.listen({'onClick': function() {
            wgt.scrollByOffset(-1 * wgt._displayedRows);
            wgt.updateButtons_();
        }});
        buttonDown.listen({'onClick': function() {
            wgt.scrollByOffset(wgt._displayedRows);
            wgt.updateButtons_();
        }});
        buttonHome.listen({'onClick': function() {
            wgt.scrollToPosition(wgt._min);
            wgt.updateButtons_();
        }});
        buttonEnd.listen({'onClick': function() {
            wgt.scrollToPosition(wgt._max);
            wgt.updateButtons_();
        }});
        
        // change slider tool-tip position
        var oldDrag = slider._startDrag;
        slider._startDrag = function(dg) {
            slider.$n('btn').title = '';
            jq(document.body)
                .append('<div id="zul_slidetip" class="z-slider-popup"'
                + 'style="position:absolute;display:none;z-index:60000;'
                + 'background-color:white;border: 1px outset">1</div>');
            
            slider.slidetip = jq('#zul_slidetip')[0];
            if (slider.slidetip) {
                var slideStyle = slider.slidetip.style;
                if (zk.webkit) { //give initial position to avoid browser scrollbar
                    slideStyle.top = '0px';
                    slideStyle.left = '0px';
                }
                slideStyle.display = 'block';
                zk(slider.slidetip).position(slider.$n(), 'top_left');
                slideStyle.right = (jq(document.body).width() - jq('#zul_slidetip').position().left) + 5 + 'px';
                slideStyle.left = '';
                slider.slidetip.innerHTML = slider._curpos +1;
            }
        };
        
        // register mouse wheel on grid
        jq(this.ebody).mousewheel(function(evt, delta, deltaX, deltaY) {
            evt.stopPropagation();
            wgt.scrollByOffset(deltaY < 0 ? wgt._wheelStep : -wgt._wheelStep);
            wgt.updateButtons_();
            return false;
        });
        
        // listen on control keys
        this.setCtrlKeys('#pgup#pgdn#up#down^#home^#end');
        var anchor = zk.ie < 11 || zk.gecko ? document.createElement("a") : document.createElement("button");
        anchor.href = 'javascript:;';
        anchor.id = wgt.uuid + '-cust-a';
        anchor.style.position = 'absolute';
        anchor.style.left = '-1000px';
        anchor.style.top = '-1000px';
        var thisDom = this.$n();
        thisDom.insertBefore(anchor, thisDom.childNodes[0]);
        this.domListen_(anchor, 'onFocus', 'doFocus_')
            .domListen_(this, 'onClick', 'doClick_')
            .domListen_(anchor, 'onKeyDown', 'doKeyDown_')
            .domListen_(anchor, 'onBlur', 'doBlur_');
    },

    /**
     * Initializes the widget
     * 
     * @private
     * @memberOf ppi.grid.LargeGrid
     */
    _init: function(size, msg, id) {
        this._modelSize = size;
        this._busyMsg = msg;
        this._busyComp = id;
        if (size > 0) {
            this._makeBusy();
        }
    },
    
    /**
     * Sets the slider position and focuses the grid (called from server)
     * 
     * @private
     * @memberOf ppi.grid.LargeGrid
     */
    _activate: function() {
        this._slider.setCurpos(this._curpos);
        var widget = this;
        setTimeout(function(){
            jq(widget.$n('cust-a')).focus();
        }, 100);
    },
    
    /**
     * modelsize and total size
     * 
     * @private
     * @memberOf ppi.grid.LargeGrid
     */
    _reset: function(modelSize, totalSize) {
        this._modelSize = modelSize;
        this._max = totalSize;
    },
    
    /**
     * show busy screen if timeout is exceeded
     * 
     * @private
     * @memberOf ppi.grid.LargeGrid
     */
    _makeBusy: function() {
        var callback = (function (id, msg) {
            var compId = id;
            var busyMsg = msg;
            return function() {
                zAu.cmd0.showBusy(compId, busyMsg);
            }
        })(this._busyComp, this._busyMsg);
        ppi.util.registerTimeoutCallback('busy'+this._busyComp, callback, 200);
    },
    
    /**
     * requests the server to render the rows
     * 
     * @private
     * @memberOf ppi.grid.LargeGrid
     */
    _loadMore: function(pageViewStart, evt) {
        
        if(this._pageViewStart == pageViewStart) {
            return;
        }
        this._loading = true;
        // jq(this.ebodytbl).animate({opacity: 0.5}, 200);
        // alternative: block full macro component
        this._makeBusy();
        var loadStartPos = Math.floor(pageViewStart + this._displayedRows / 2 - this._modelSize / 2);
        loadStartPos = this._calcPossibleLoadStart(loadStartPos);
        this.fire('onLoadRows', {pageStart: loadStartPos}, {toServer: true});
        this._pageStart = loadStartPos;
        this._pageViewStart = pageViewStart;
    },
    
    /**
     * corrects all value which are size dependend (row height, displayed rows, page increment)
     * @memberOf ppi.grid.LargeGrid
     */
    onSize: function() {
        this.$supers('onSize', arguments);
        this._rowHeight = this._getFirstRowHeight();
        this._displayedRows = Math.floor(jq(this.ebody).height() / this._rowHeight);
        this._slider.setPageIncrement(this._displayedRows);
        var newMax = this._max - this._displayedRows + 1;
        var checkedMax = newMax > 0 ? newMax : 0;
        this._slider.setMaxpos(checkedMax);
        this.updateButtons_();
    },
    
    /**
     * retrieves the height of the first row
     * 
     * @private
     * @memberOf ppi.grid.LargeGrid
     */
    _getFirstRowHeight: function() {
        if (!this.ebodyrows) {
            return this._rowHeight;
        }
        var tbody = this.ebodyrows,
            children = jq(tbody).children();
        
        return children.length == 0 ? this._rowHeight : jq(children[0]).height();
    },
    
    /**
     * sets the correct offset from top and clears any busy screens
     * 
     * @private
     * @memberOf ppi.grid.LargeGrid
     */
    _adjustAfterLoading: function() {
        jq(this.ebodytbl).css('top', (this._pageStart - this._pageViewStart) * this._rowHeight + 'px');
        this._loading = false;
        // jq(this.ebodytbl).stop(true).css('opacity', 1);
        ppi.util.unregisterTimeoutCallback('busy'+this._busyComp);
        zAu.cmd0.clearBusy(this._busyComp);
        var wgt = this;
    },
    
    /**
     * saves or loads instance variables before/after search
     * 
     * @private
     * @memberOf ppi.grid.LargeGrid
     */
    _clearAfterSearch: function(load, first, max, viewSize) {
        if(load) {
            var save = this._savedData;
            this._slider.setCurpos(save[0]);
            this._pageStart = save[1];
            this._pageViewStart = save[2];
            this._max = save[3];
            this._modelSize = save[4];
        } else {
            if(first) {
                var save = [];
                save.push(this._slider.getCurpos());
                save.push(this._pageStart);
                save.push(this._pageViewStart);
                save.push(this._max);
                save.push(this._modelSize);
                this._savedData = save;
            }
            this._slider.setCurpos(0);
            this._pageStart = 0;
            this._pageViewStart = 0;
            this._max = max;
            this._modelSize = viewSize;
        }
        zAu.cmd1.resizeWgt(this);
        this._adjustAfterLoading();
    }, 
    
    /**
     * Calculates the possible start position of the grid.
     * 
     * @param pos current position
     * @returns new start position
     * 
     * @private
     * @memberOf ppi.grid.LargeGrid
     */
    _calcPossibleViewStart: function(pos) {
        return keepBetween(pos, this._min, this._max - this._displayedRows + 1);
    },
    
    /**
     * Calculates possible start for loading new data
     * 
     * @param pos current position
     * @returns position at which new data must be fetched
     * 
     * @private
     * @memberOf ppi.grid.LargeGrid
     */
    _calcPossibleLoadStart: function(pos) {
        return keepBetween(pos, this._min, this._max - this._modelSize + 1);
    },
    
    /**
     * Scrolls down a little bit
     * 
     * @private
     * @memberOf ppi.grid.LargeGrid
     */
    _doScrollToView: function () {
        if(this._loading || !this.isScrollable())
            return;
        
        this._rowHeight = this._getFirstRowHeight();
        var slider = this._slider,
            curpos = slider.getCurpos(),
            pageViewStart = this._calcPossibleViewStart(curpos),
            pageViewEnd = pageViewStart + this._displayedRows - 1;
        this._curpos = curpos;
        if(pageViewStart >= this._pageStart && pageViewEnd < this._pageStart + this._modelSize) {
            jq(this.ebodytbl).stop(true).animate({'top' : (this._pageStart - pageViewStart) * this._rowHeight + 'px'}, 250);
            this._pageViewStart = pageViewStart;
            return;
        }
        this._loadMore(pageViewStart);
    },
    scrollByOffset: function(offset) {
        if (this._loading)
            return;
        
        var slider = this._slider,
            newpos = slider.getCurpos() + Math.floor(offset);
        this.scrollToPosition(newpos);
    },
    scrollToPosition: function(pos) {
        if (this._loading || !this.isScrollable())
            return;
        
        if (pos < this._min) {
            pos = this._min;
        } else if (pos > this._max - this._displayedRows) {
            pos = this._max - this._displayedRows + 1;
        }
        
        var slider = this._slider;
        slider.setCurpos(pos);
        this._doScrollToView();
    },
    /**
     * scrolls to the specified index if necessary (e.g. the row is currently not visible)
     * 
     * @private
     * @memberOf ppi.grid.LargeGrid
     */
    _scrollIfNeeded: function(pos) {
        if(pos < this._pageViewStart || pos >= this._pageViewStart + this._displayedRows) {
            this.scrollToPosition(pos);
        }
    },
    /**
     * Handles key down event
     * @param evt key event data
     * 
     * @protected
     * @memberOf ppi.grid.LargeGrid
     */
    doKeyDown_: function(evt) {
        var time = evt.domEvent.timeStamp;
        if(time==this.lastTime) {
            return;
        }
        this.lastTime = time;
        var data = evt.data;
        
        switch (data.keyCode) {
        case 33: this.scrollByOffset(-this._displayedRows);
        break;
        case 34: this.scrollByOffset(this._displayedRows);
        break;
        case 36: if (evt.ctrlKey) this.scrollToPosition(this._min);
        break;
        case 35: if (evt.ctrlKey) this.scrollToPosition(this._max);
        break;
        }
    },
    /**
     * Handles click event
     * @param evt click event data
     * 
     * @protected
     * @memberOf ppi.grid.LargeGrid
     */
    doClick_ : function (evt) {
        if (jq.isAncestor(this.$n(), evt.domTarget))
            jq(this.$n('cust-a')).focus();
    },
    /**
     * Handles focus event
     * @param evt focus event data
     * 
     * @protected
     * @memberOf ppi.grid.LargeGrid
     */
    doFocus_ : function (evt) {
        if (evt.domTarget == this.$n('cust-a'))
            this._focused = true;
    },
    /**
     * Handles key down event
     * @param evt key event
     * 
     * @protected
     * @memberOf ppi.grid.LargeGrid
     */
    doBlur_ : function (evt) {
        if (evt.domTarget == this.$n('cust-a'))
            this._focused = false;
    },
    isListen : function (evt, opts) {
        if (evt == 'onCtrlKey')
            return this._focused
    },
    isScrollable : function() {
        return this._max > this._displayedRows;
    }
});
})();
zkreg('ppi.grid.LargeGrid');
zk.afterLoad('zul.grid', function() {
    var xGrid_ = {};
    zk.override(zul.grid.Grid.prototype, xGrid_, {
        _rodLoadingRenderRow: function(padId, top) {
            var id = this.uuid + '-' + padId + (top?'-top':'-btm')
            var html = '<tr';
            if (!zk.webkit) html += ' style="height:' + this._rodLoadingSize + 'px"'
            html += '><td class="grid-rod-' + (top?'top"':'bottom"')
            if (zk.webkit) html += ' style="height:' + this._rodLoadingSize + 'px"'
            html +='><div id="' + id + '" class="grid-rod-message">'
            html +='<span class="grid-rod-loading-indicator" />'+ zUtl.encodeXML(this._rodLoadingMsg) + '</div></td></tr>'
            return html;
        },
        domPad_: function(c, d) {
            if (this._grid$rod)
                c.push('<div id="',this.uuid,d,'" style="font-size:0px;display:none;overflow:hidden;width:100%;"></div>')
        },
        // NICE ds: Replace with CSS3 (http://www.mediaevent.de/css/generated.html) if IE9 dies, finally.
        _setPadSize: function (pad, padId, sz) { //set pad size
            var maxsz = zk.ie < 11 ? 1193046 : 16777216
            this._padsz[padId] = sz;
            var s = pad.style;
            s.display = sz ? 'block' : 'none'; //display:none is used for IE 6/7

            var html = '<table id="' + this.uuid + '-' + padId + '-tbl" width="100%">'
            var btmvis = false;
            if (sz > this._rodLoadingSize) {
                sz -= this._rodLoadingSize;
                html += this._rodLoadingRenderRow(padId, true);
            }
            if (sz > this._rodLoadingSize) {
                sz -= this._rodLoadingSize;
                btmvis = true;
            }
            if (!zk.webkit)
                while (true) {
                    html += '<tr style="height:' + Math.min(sz, maxsz) + 'px"><td></td></tr>';
                    sz -= maxsz;
                    if (sz <= 0) 
                        break;
                }
            else
                while (true) {
                    html += '<tr><td style="height:' + Math.min(sz, maxsz) + 'px"></td></tr>';
                    sz -= maxsz;
                    if (sz <= 0) 
                        break;
                }
            html += '</table>';

            if (btmvis)
                html += this._rodLoadingRenderRow(padId, false);

            jq(pad).empty().append(html);
        },
        
        // TODO V4.0 Ticket ccw/ds: #3509 - raus wenn fixed (Potix #3973)
        _initPadSizes : function() {
            if (this.inPagingMold()) return; //in paging mold, no padding at all!

            var rows, erows;
            if (!(rows = this.rows) || !(erows = rows.$n()))
                return;

            var nrow = rows.nChildren,
                rowshgh = this._rowshgh = erows.offsetHeight,
                tsize = this._totalSize,
                ebody = this.ebody;
            this._avgrowhgh = nrow > 0 ? ((rowshgh / nrow) | 0) : 21;
            this._ibottom = this._offset + nrow;
            if (this._topPad < 0)
                this._topPad = this._offset * this._avgrowhgh;
            if (this._offset == 0)
                this._topPad = 0;
            var tpadhgh = this._topPad;
            var bpadhgh = tsize * this._avgrowhgh - rowshgh - tpadhgh;
            if (bpadhgh < 0) {
                if (this._offset > 0) tpadhgh += bpadhgh;
                bpadhgh = 0;
                if (tpadhgh < 0) tpadhgh = 0;
            } else if (tsize <= this._ibottom)
                bpadhgh = 0;

            if (this._topPad != tpadhgh)
                this.fire('onTopPad', {topPad: this._topPad = tpadhgh});

            this._setPadSize(this.$n('tpad'), 'tpad', tpadhgh);
            this._setPadSize(this.$n('bpad'), 'bpad', bpadhgh);

            this._totalhgh = ebody.scrollHeight;
            this._itop = this._offset;
            var ebodyhgh = ebody.offsetHeight;
            this._limit = (((ebodyhgh + this._bufpad * 2) / this._avgrowhgh) | 0) + 2;

            if (!this._viewtop && !this._lasttop && this._currentTop > 0)
                this._viewtop = this._lasttop = this._currentTop;

            // Bug ZK-353 sync _lastoffset if not initialized yet.
            if (!this._lastoffset && this._offset > 0)
                this._lastoffset = this._offset;

            if (this._totalSize > this._ibottom) {
                var viewtop = this._viewtop = this._currentTop,
                    viewbottom = viewtop + ebodyhgh,
                    lasterow = (rows.lastChild) ? rows.lastChild.$n() : undefined,  // ZK-3526 rows.lastChild is undefined when model get cleared
                    rowbottom = (lasterow) ? this._rowScrollBottom(lasterow) : tpadhgh;
                if (viewbottom > rowbottom && this._lastoffset == this._itop) {//still space to hold extra rows and no scrolling during the data loading
                    this.fire('onDataLoading', {offset: this._itop, limit: this._limit});
                }
            }

            // should restore scroll position for native scroll bar
            if (this._currentTop > 0)
                ebody.scrollTop = this._currentTop;
            
            // ZK-490: should sync bar after reset model
            this.refreshBar_();
        }
    });

    var xRow_ = {};
    zk.override(zul.grid.Row.prototype, xRow_, {
        ignoreDrag_ : function(pt, evt, drag) {
            if (jq(drag.node).find('input')[0].id == evt.domTarget.id) {
                return true;
            }
            return false;
        },

        cloneDrag_ : function(drag, ofs) {
            var draggedElement;

            if (jq(drag.node).is('.grid-row-checked')) {
                var draggedElement = document.createElement('div');
                document.body.appendChild(draggedElement);
                draggedElement.id = 'row-dnd-ghost';

                var rows = jq(drag.node).parent().find('.grid-row-checked');
                jq(draggedElement).append(jq("<div/>").text(msgzk.DRAG_OF_MULTIPLE_ELEMENTS).append(rows.length).html())

                for (var i = 0; i < rows.length; i++) {
                    var row = jq(rows[i]);
                    var clone = row.clone()[0];
                    row.addClass('z-dragged');
                    jq(draggedElement).append(clone);
                }
            } else {
                draggedElement = jq(drag.node).clone()[0];
                draggedElement.id = "row_dnd_ghost";
                document.body.appendChild(draggedElement);

                jq(drag.node).addClass('z-dragged');
            }
            zk.copy(draggedElement.style, { position: "absolute", left: ofs[0] + "px", top: ofs[1] + "px" });
            jq(draggedElement).addClass("z-drag-ghost");
            drag._orgcursor = document.body.style.cursor;
            document.body.style.cursor = "pointer";

            return draggedElement;
        },

        uncloneDrag_ : function(drag) {
            document.body.style.cursor = drag._orgcursor || '';
        },

        getDragOptions_ : function(map) {
            map.endeffect = function(drag, evt) {
                if (drag) {
                    var drop;
                    if (drop = drag._lastDrop) {
                        drag._lastDrop = null;
                        drop.dropEffect_();
                    }
                    drag._lastDropTo = null;
                }
                var pt = [evt.pageX, evt.pageY],
                    wgt = zk.DnD.getDrop(drag, pt, evt);
                if (wgt) wgt.onDrop_(drag, evt);
                    else jq(drag.node).parent().find('.z-dragged').removeClass('z-dragged');
            }
            return map;
        }
    })
});

(function() {
    function _getBody(uuid) {
        var grid = $('#' + uuid);
        if (grid) return grid.find('.z-grid-body');
    }

    function _moveInputFocusToRow(newRowIndex, columnIndex, rows) {
        var inputToFocus = rows.getChildAt(newRowIndex).getChildAt(columnIndex);
        
        if (!inputToFocus) {
            // row not there because of rod
            return false;
        }
        inputToFocus = inputToFocus.firstChild;
        
        // look for first input widget
        inputToFocus = _getInputWidgetFromChildren(inputToFocus);
        if (inputToFocus !== undefined) {
            inputToFocus.focus();
            jq(inputToFocus).select();
            return true;
        } else {
            return false;
        }
    }
    
    function _getInputWidgetFromChildren(parent) {
        if (parent === undefined) {
            return undefined;
        }
        if (parent.$instanceof(zul.inp.InputWidget)) {
            return parent;
        }
        
        var i = 0;
        while (parent.getChildAt(i) !== undefined) {
            var child = parent.getChildAt(i);
            var result = _getInputWidgetFromChildren(child);
            if (result !== undefined) {
                return result;
            }
            i++;
        }
    }

    function _handleUpDownMovementOnGrid(evt) {
        var code = evt.keyCode;
        var rows = this;
        var focusedElement = zk.$(jq(':focus'));
        
        if (!focusedElement || !focusedElement.$instanceof(zul.inp.InputWidget)) {
            return;
        }
        
        //works, even if parent is z-span, should be fine with z-cell.
        var columnFocus = focusedElement;
        while (columnFocus !== undefined
                && columnFocus.parent !== undefined
                && !columnFocus.parent.$instanceof(zul.grid.Row)) {
            columnFocus = columnFocus.parent;
        }
        var indexOfColumnFocus = columnFocus.getChildIndex();
        
        // der Parent von rowFocus ist die gesuchte Row
        var rowFocus = columnFocus.parent;
        var indexOfRowFocus = rowFocus.getChildIndex();

        var targetRow;
        var success;
        if (code === 38 && indexOfRowFocus > 0) {
            targetRow = indexOfRowFocus - 1;
            do {
                success = _moveInputFocusToRow(targetRow, indexOfColumnFocus, rows);
                targetRow--;
            } while (targetRow >= 0 && !success);
            if (success) {
                evt.stop();
            }
        } else if (code === 40 && indexOfRowFocus < rows.nChildren - 1) {
            targetRow = indexOfRowFocus + 1;
            do {
                success = _moveInputFocusToRow(targetRow, indexOfColumnFocus, rows);
                targetRow++;
            } while (targetRow < rows.nChildren && !success);
            if (success) {
                evt.stop();
            }
        }
    }

    function _cancelPendingBusy(wgt) {
        if (wgt && wgt._currentBusy) {
            wgt._currentBusy.cancelTimeout();
            delete wgt._currentBusy;
        }
    }

ppi.grid.showBusyDelayed = function (uuid, message, delay) {
    var wgt = zk.$(uuid);
    if (!wgt) return;
    _cancelPendingBusy(wgt);
    var timeoutId = setTimeout(zAu.cmd0.showBusy.bind(this, uuid, message), delay);
    wgt._currentBusy = {
        cancelTimeout : function () {
            clearTimeout(timeoutId);
        }
    }
}
ppi.grid.clearBusyDelayed = function (uuid) {
    _cancelPendingBusy(zk.$(uuid));
    zAu.cmd0.clearBusy(uuid);
}
ppi.grid.scrollTop = function(uuid) {
    var body = _getBody(uuid);
    if (body) body.scrollTop(0)
}
/** Scrolling for ROD */
ppi.grid.scrollToRowApproximately = function (uuid, index) {
    var wgt = zk.$(uuid);
    if (!wgt) return;
    ppi.util.doOnAfterResponse(function() {
        if (wgt.ebody) wgt.ebody.scrollTop = index * wgt._avgrowhgh;
    })
}
/** Scrolling for non-ROD only! */
ppi.grid.scrollToRowExactly = function(uuid, index) {
    setTimeout(function() {
        var wgt = zk.$(uuid);
        if (!wgt) return;
        var height = 0;
        var rows = wgt.ebodyrows;
        for (i = 0; i < index && i < rows.childElementCount; i++) {
            height += jq(rows.childNodes[i]).height();
        }
        jq(wgt.ebody).animate({scrollTop: height}, 250);
    }, 50);
}
ppi.grid.scrollToLastExactly = function(uuid) {
    var wgt = zk.$(uuid);
    if (!wgt) return;
    var body = $(wgt.$n('body'));
    var height = 0;
    body.children().each(function() {
        height += $(this).height();
    });
    body.scrollTop(height);
}
ppi.grid.enableUpDownMovementOnGrid = function(uuid) {
    var grid = zk.$(uuid);
    if (grid && grid.rows) {
        grid.rows.setCtrlKeys('#up#down#pgdn^#home^#end#home#end#pgup^p^f#f5^p^f#f5#del^a');
        grid.rows.listen({ onCtrlKey: _handleUpDownMovementOnGrid });
    }
}
})();

}finally{zk.setLoaded(zk._p.n);}});zk.setLoaded('ppi.grid',1);