// --
// Copyright (C) 2001-2020 OTRS AG, https://otrs.com/
// --
// This software comes with ABSOLUTELY NO WARRANTY. For details, see
// the enclosed file COPYING for license information (GPL). If you
// did not receive this file, see https://www.gnu.org/licenses/gpl-3.0.txt.
// --

/*eslint-disable no-window*/

"use strict";

var Core = Core || {};
Core.UI = Core.UI || {};

/**
 * @namespace Core.UI.Popup
 * @memberof Core.UI
 * @author OTRS AG
 * @description
 *      Popup windows.
 */
Core.UI.Popup = (function (TargetNS) {
    /**
     * @private
     * @name OpenPopups
     * @memberof Core.UI.Popup
     * @member {Object}
     * @description
     *      All open popups.
     */
    var OpenPopups = {},

    /**
     * @private
     * @name PopupProfiles
     * @memberof Core.UI.Popup
     * @member {Object}
     * @description
     *      Defined popup profiles.
     */
        PopupProfiles,

    /**
     * @private
     * @name PopupDefaultProfile
     * @memberof Core.UI.Popup
     * @member {Object}
     * @description
     *      Default profile.
     */
        PopupDefaultProfile = 'Default',

    /**
     * @private
     * @name RegisterPopupTimeOut
     * @memberof Core.UI.Popup
     * @member {Object}
     * @description
     *      Time to wait before a popup is registered at the parent window.
     */
        RegisterPopupTimeOut = 1000,

    /**
     * @private
     * @name WindowMode
     * @memberof Core.UI.Popup
     * @member {String}
     * @description
     *      Defines the mode to open popups.
     *      If the screen size is <1024px (ScreenL), we do not open popup windows,
     *      but add fullsize iframes to the page.
     *      Mode can be 'Popup' or 'Iframe'.
     */
        WindowMode = 'Popup';

    if (!Core.Debug.CheckDependency('Core.UI.Dialog', 'Core.Config', 'Core.Config')) {
        return false;
    }

    PopupProfiles = {
        'Default': {
            WindowURLParams: "dependent=yes,location=no,menubar=no,resizable=yes,scrollbars=yes,status=no,toolbar=no",
            Left: 100,
            Top: 100,
            Width: 1040,
            Height: 700
        }
    };

    /**
     * @private
     * @name CheckOpenPopups
     * @memberof Core.UI.Popup
     * @function
     * @description
     *      Check which popup windows are still open.
     */
    function CheckOpenPopups() {
        $.each(OpenPopups, function (Key, Value) {
            if (Value.closed) {
                delete OpenPopups[Key];
            }
        });
    }

    /**
     * @private
     * @name CheckOpenPopups
     * @memberof Core.UI.Popup
     * @function
     * @returns {Object} Open popups of given type.
     * @param {String} Type - The type of a window, e.g. 'Action'.
     * @description
     *      Check which popup window are still open.
     */
    function GetPopupObjectByType(Type) {
        return OpenPopups[Type];
    }

    /**
     * @private
     * @name GetWindowParentObject
     * @memberof Core.UI.Popup
     * @function
     * @returns {Object} Parent window object.
     * @description
     *      Get the window parent object of the current popup.
     */
    function GetWindowParentObject() {
        // we have a normal popup, opener is defined
        // In Chrome/Webkit/Android window.opener is null instead of undefined
        // In IE (Win Phone) window.opener is undefined
        // typeof null === object
        if (window.opener !== null && typeof window.opener !== 'undefined') {
            // Special handling for IE11
            // Because of permission problems, IE11 returns a valid window object
            // but without the needed JS object "Core". window.parent contains that element
            if (typeof window.opener.Core !== 'undefined') {
                return window.opener;
            }
            else {
                return window.parent;
            }
        }
        else {
            return window.parent;
        }
    }

    /**
     * @private
     * @name CurrentIsPopupWindow
     * @memberof Core.UI.Popup
     * @function
     * @returns {String} Returns the type of popup if one, undefined otherwise.
     * @description
     *      Checks if current window is an OTRS popup.
     */
    function CurrentIsPopupWindow() {
        var PopupType;

        if (window.name.match(/OTRSPopup_([^_]+)_.+/)) {
            PopupType = RegExp.$1;
        }

        return PopupType;
    }

    /**
     * @name CurrentIsPopupWindow
     * @memberof Core.UI.Popup
     * @function
     * @returns {String} Returns the type of popup if one, undefined otherwise.
     * @description
     *      Checks if current window is an OTRS popup.
     */
    TargetNS.CurrentIsPopupWindow = function () {
        return CurrentIsPopupWindow();
    };

    /**
     * @name ProfileAdd
     * @memberof Core.UI.Popup
     * @function
     * @param {String} Key - Name of the Profile (UID).
     * @param {String} Values - Profile string as expected by window.open(),
     *                         e. g. "dependent=yes,height=700,left=100,top=100,location=no,menubar=no,resizable=yes,scrollbars=yes,status=no,toolbar=no,width=1000".
     * @description
     *      Adds a popup profile.
     */
    TargetNS.ProfileAdd = function (Key, Values) {
        PopupProfiles[Key] = Values;
    };

    /**
     * @name ProfileList
     * @memberof Core.UI.Popup
     * @function
     * @returns {Object} PopupProfiles object.
     * @description
     *      Get the list of registered popup profiles.
     */
    TargetNS.ProfileList = function () {
        return PopupProfiles;
    };

    /**
     * @name RegisterPopupEvent
     * @memberof Core.UI.Popup
     * @function
     * @description
     *      Register the pop-up event for a window.
     */
    TargetNS.RegisterPopupEvent = function () {
        $(window).on('Popup', function (Event, Type, Param) {
            if (Type && typeof Type !== 'undefined') {
                if (Type === 'Reload') {
                    window.location.reload();
                }
                else if (Type === 'URL') {
                    if (Param && typeof Param.URL !== 'undefined') {
                        window.location.href = Param.URL;
                    }
                }
            }
        });
    };

    /**
     * @name FirePopupEvent
     * @memberof Core.UI.Popup
     * @function
     * @param {String} Type - The event type that will be launched.
     * @param {Object} Param  - The element that contain information about the new screen address.
     * @param {Boolean} ExecuteInMobileMode - Do not execute this on mobile devices.
     * @description
     *      This function starts the pop-up event.
     */
    TargetNS.FirePopupEvent = function (Type, Param, ExecuteInMobileMode) {
        if (ExecuteInMobileMode === false && !$('body').hasClass('Visible-ScreenXL') && (!localStorage.getItem("DesktopMode") || parseInt(localStorage.getItem("DesktopMode"), 10) <= 0)) {
            return;
        }

        $(window).off('beforeunload.Popup');
        Core.App.UnbindWindowUnloadEvent('Popup');
        $(window).trigger('Popup', [Type, Param]);
    };

    /**
     * @name CheckPopupsOnUnload
     * @memberof Core.UI.Popup
     * @function
     * @returns {String} A Warning text.
     * @description
     *      This review if some popup windows are open, then try to send a warning.
     */
    TargetNS.CheckPopupsOnUnload = function () {
        var Size = 0;
        CheckOpenPopups();
        $.each(OpenPopups, function (Key, Value) {
            // IE(7) treats windows in new tabs (opened with right-click) also as popups
            // Therefore we check if the popup is a real OTRS popup.
            // IE9 can't read the WindowType property from the window object,
            // so we check for the correct popup window name now.
            if (Value.name.match(/OTRSPopup_.+/)) {
                Size++;
            }
        });
        if (Size) {
            return Core.Language.Translate('If you now leave this page, all open popup windows will be closed, too!');
        }
    };

    /**
     * @name ClosePopupsOnUnload
     * @memberof Core.UI.Popup
     * @function
     * @description
     *      This function close the active popup windows.
     */
    TargetNS.ClosePopupsOnUnload = function () {
        CheckOpenPopups();
        $.each(OpenPopups, function (Key, Value) {
            // IE(7) treats windows in new tabs (opened with right-click) also as popups
            // Therefore we check if the popup is a real OTRS popup.
            // IE9 can't read the WindowType property from the window object,
            // so we check for the correct popup window name now.
            if (Value.name.match(/OTRSPopup_.+/)) {
                TargetNS.ClosePopup(Value);
            }
        });
    };

    /**
     * @name RegisterPopupAtParentWindow
     * @memberof Core.UI.Popup
     * @function
     * @param {Object} WindowObject - Real window object.
     * @param {String} Type - the window type.
     * @description
     *      This function set the type for a popup window.
     */
    TargetNS.RegisterPopupAtParentWindow = function (WindowObject) {
        var Type;

        /OTRSPopup_([^_]+)_.*/.exec(WindowObject.name);
        Type = RegExp.$1;

        if (typeof OpenPopups[Type] === 'undefined') {
            OpenPopups[Type] = WindowObject;
        }
        else {
            if (OpenPopups[Type] !== WindowObject) {
                OpenPopups[Type] = WindowObject;
            }
        }
    };

    /**
     * @name InitRegisterPopupAtParentWindow
     * @memberof Core.UI.Popup
     * @function
     * @description
     *      This function set a timeout and after that try to register a popup at a parent window.
     */
    TargetNS.InitRegisterPopupAtParentWindow = function () {
        window.setTimeout(function () {
            var PopupType = CurrentIsPopupWindow(),
                ParentWindow;

            if (typeof PopupType !== 'undefined') {
                ParentWindow = GetWindowParentObject();
            }

            // if the window has a name, it's most probably the popup itself, see bug#12185.
            // This can happen if the parent window gets reloaded while the popup window tries
            // to register itself to it. When there is no parent, GetWindowParentObject() will return
            // the current window which is the popup itself. This leads to a situation where the popup window
            // registers itself as popup in its own namespace, which will then lead to a confirmation
            // message asking the user if they really want to navigate away from the current page when trying
            // to close the popup by either submitting a form, closing it manually or using a close link.
            if (ParentWindow &&
                !ParentWindow.name &&
                ParentWindow.Core &&
                ParentWindow.Core.UI &&
                ParentWindow.Core.UI.Popup
            ) {
                try {
                    ParentWindow.Core.UI.Popup.RegisterPopupAtParentWindow(window, PopupType);
                }
                catch (Event) {
                    // no code here
                    $.noop(Event);
                }
            }
            Core.UI.Popup.InitRegisterPopupAtParentWindow();
        }, RegisterPopupTimeOut);
    };

    /**
     * @name GetPopupObject
     * @memberof Core.UI.Popup
     * @function
     * @returns {Object} The window object of the popup or undefined.
     * @param {String} Type - The type of a window, e.g. 'Action'.
     * @description
     *      Get window object by popup type.
     */
    TargetNS.GetPopupObject = function (Type) {
        return GetPopupObjectByType(Type);
    };

    /**
     * @name GetWindowMode
     * @memberof Core.UI.Popup
     * @function
     * @returns {String} The window mode ('Popup' or 'Iframe') or undefined.
     * @description
     *      Get the window mode.
     */
    TargetNS.GetWindowMode = function () {
        if (WindowMode === 'Popup' || WindowMode === 'Iframe') {
            return WindowMode;
        }
        else {
            return undefined;
        }
    };

    /**
     * @name SetWindowMode
     * @memberof Core.UI.Popup
     * @function
     * @param {String} Mode - The new window mode ('Popup' or 'Iframe').
     * @description
     *      Set the window mode.
     */
    TargetNS.SetWindowMode = function (Mode) {
        if (Mode === 'Popup' || Mode === 'Iframe') {
            WindowMode = Mode;
        }
        else {
            WindowMode = undefined;
        }
    };

    /**
     * @name Resize
     * @memberof Core.UI.Popup
     * @function
     * @param {String} Type - The type of a window, e.g. 'Action'.
     * @param {String} Width - Width in pixels.
     * @param {String} Height - Height in pixels.
     * @description
     *      This function resizes an opened window.
     */
    TargetNS.Resize = function (Type, Width, Height) {
        var PopupObject = GetPopupObjectByType(Type);
        // do not resize in iframe mode
        if (WindowMode === 'Iframe') {
            return;
        }
        if (typeof PopupObject !== 'undefined') {
            PopupObject.resizeTo(Width, Height);
        }
    };

    /**
     * @name OpenPopup
     * @memberof Core.UI.Popup
     * @function
     * @param {String} URL - The URL to be open in the new window.
     * @param {String} Type - The type of a window, e.g. 'Action'.
     * @param {String} Profile - The profile of a window, which defines the window parameters. Optional, default is 'Default'.
     * @param {Int}    Unlinked - Optional parameter, if it's 1, popup will not be linked with the parent
     * @description
     *      This function opens a popup window. Every popup is of a specific type and there can only be one window of a type at a time.
     */
    TargetNS.OpenPopup = function (URL, Type, Profile, Unlinked) {
        var PopupObject,
            PopupProfile,
            NewWindow,
            WindowName,
            ConfirmClosePopup = true,
            PopupFeatures;

        // If we are in a mobile environment on opening the popup, open it as an iframe
        if (Core.App.Responsive.IsSmallerOrEqual(Core.App.Responsive.GetScreenSize(), 'ScreenL') && (!localStorage.getItem("DesktopMode") || parseInt(localStorage.getItem("DesktopMode"), 10) <= 0)) {
            TargetNS.SetWindowMode('Iframe');
        }
        else {
            TargetNS.SetWindowMode('Popup');
        }

        CheckOpenPopups();
        if (URL) {
            PopupObject = GetPopupObjectByType(Type);

            // perform only if popups are not unlinked
            if (!Unlinked) {
                if (typeof PopupObject !== 'undefined') {
                    ConfirmClosePopup = window.confirm(Core.Language.Translate('A popup of this screen is already open. Do you want to close it and load this one instead?'));
                    if (ConfirmClosePopup) {
                        TargetNS.ClosePopup(PopupObject);
                    }
                }
            }

            // Only load new popup if the user accepted that the old popup is closed
            if (ConfirmClosePopup) {
                PopupProfile = PopupProfiles[Profile] ? Profile : PopupDefaultProfile;
                /*
                 * Special treatment for the window name. At least in some browsers, window names
                 *  are global, so only one popup window with a particular name may exist across
                 *  all browser tabs. To avoid conflicts with that, we use 'randomized' names
                 *  by including the current time in the name string. This name is also needed
                 *  to save the Type parameter.
                 */

                 /* if Unlined is passed and eq 1 add, diferent name of the popup
                 * it will ensure that popup is nor linked with the parent window
                 */
                if (Unlinked && Unlinked === 1) {
                    WindowName = 'PopupOTRS_' + Type + '_' + Date.parse(new Date());
                }
                else {
                    WindowName = 'OTRSPopup_' + Type + '_' + Date.parse(new Date());
                }

                if (WindowMode === 'Popup') {
                    PopupFeatures = PopupProfiles[PopupProfile].WindowURLParams;
                    // Get the position of the current screen on browsers which support it (non-IE) and
                    //  use it to open the popup on the same screen
                    PopupFeatures += ',left=' + ((window.screen.left || 0) + PopupProfiles[PopupProfile].Left);
                    PopupFeatures += ',width=' + PopupProfiles[PopupProfile].Width;

                    // Bug#11205 (http://bugs.otrs.org/show_bug.cgi?id=11205)
                    // On small screens (still wide enough to open a popup)
                    // it can happen, that the popup window is higher than the screen height
                    // In this case, reduce the popup height to fit into the screen
                    // We don't have to do that for the width, because a smaller screen width
                    // would result in a "responsive popup" aka iframe.
                    if (window.screen.availHeight < PopupProfiles[PopupProfile].Height + PopupProfiles[PopupProfile].Top) {
                        PopupFeatures += ',height=' + (window.screen.availHeight - PopupProfiles[PopupProfile].Top - 20);
                        // Adjust top position to have the same distance between top and bottom line.
                        PopupFeatures += ',top=' + ((window.screen.top || 0) + (PopupProfiles[PopupProfile].Top / 2));
                    }
                    else {
                        PopupFeatures += ',height=' + PopupProfiles[PopupProfile].Height;
                        PopupFeatures += ',top=' + ((window.screen.top || 0) + PopupProfiles[PopupProfile].Top);
                    }

                    NewWindow = window.open(URL, WindowName, PopupFeatures);

                    // check for popup blockers.
                    // currently, popup windows cannot easily be detected in chrome, because it will
                    //      load the entire content in an invisible window.
                    if (!NewWindow || NewWindow.closed || typeof NewWindow.closed === 'undefined') {
                        window.alert(Core.Language.Translate('Could not open popup window. Please disable any popup blockers for this application.'));
                    }
                    else {
                        OpenPopups[Type] = NewWindow;
                    }

                }
                else if (WindowMode === 'Iframe') {
                    // jump to the top
                    window.scrollTo(0, 0);

                    // prevent scrolling of the main window
                    $('html').addClass('NoScroll');

                    // add iframe overlay
                    $('body').append('<iframe data-popuptype="' + Type + '" name="' + WindowName + '" class="PopupIframe" src="' + URL + '"></iframe>');
                    $('iframe.PopupIframe').height($(window).height());
                }
            }
        }
    };

    /**
     * @name HasOpenPopups
     * @memberof Core.UI.Popup
     * @function
     * @returns {Boolean} True, if popups are open on the page, false otherwise.
     * @description
     *      Checks if there are open popups on the page.
     */
    TargetNS.HasOpenPopups = function() {
        var HasOpenPopups = false,
            Popup;

        CheckOpenPopups();
        for (Popup in OpenPopups) {
            if (OpenPopups.hasOwnProperty(Popup)) {
                HasOpenPopups = true;
                break;
            }
         }

        return HasOpenPopups;
    };

    /**
     * @name ClosePopup
     * @memberof Core.UI.Popup
     * @function
     * @param {String|Object} PopupType - The type of a popup or the window object. If not defined, the current popup windoe is closed.
     * @description
     *      This function closes an opened popup.
     *      If no parameter is given, we are in the popup itself and want to close it.
     *      If a parameter is defined we are in the parent window and want to close a specific popup window.
     *      There are four possible states for this function:
     *      1) In parent window, trying to close a "real" popup
     *      2) In parent window, trying to close an iframe-popup ("responsive mode")
     *      3) In the popup itself, closing itself ("real" popup)
     *      4) In the popup itself, closing itself (iframe-popup, "responsive mode")
     */
    TargetNS.ClosePopup = function (PopupType) {
        var PopupObject,
            ParentObject,
            PlaceOfExecution,
            LocalWindowMode;

        // If PopupType is defined, we are in the parent window and want to close a specific popup
        // Otherwise we are in the popup itself
        if (typeof PopupType === 'undefined') {
            PlaceOfExecution = 'Popup';

            // get PopupType of active popup
            PopupType = CurrentIsPopupWindow();

            // the PopupObject is the active window
            PopupObject = window;

            // the parent window object is reached differently, if popup or iframe
            ParentObject = GetWindowParentObject();
        }
        else {
            PlaceOfExecution = 'Parent';

            // if parameter PopupType is a string, we need to get the window object
            if (typeof PopupType === 'string') {
                // This only works in parent window
                PopupObject = GetPopupObjectByType(PopupType);
                ParentObject = window;
            }
            // in some circumstances the parameter PopupType is an window object instead of a string
            else {
                PopupObject = PopupType;

                // we can now find out the type of the popup based on the popup object
                if (PopupObject && typeof PopupObject.name !== 'undefined' && PopupObject.name.match(/OTRSPopup_([^_]+)_.+/)) {
                    PopupType = RegExp.$1;
                }

                // we are still in the parent window
                ParentObject = window;
            }
        }

        if (typeof PopupObject !== 'undefined' && typeof ParentObject !== 'undefined') {
            // Retrieve correct WindowMode (which is only correctly set in the parent window)
            if (PlaceOfExecution === 'Parent') {
                LocalWindowMode = TargetNS.GetWindowMode();
            }
            else if (PlaceOfExecution === 'Popup') {
                LocalWindowMode = ParentObject.Core.UI.Popup.GetWindowMode();
            }

            // if we are in a real popup, we can now savely close the popup.
            if (LocalWindowMode === 'Popup') {
                PopupObject.close();
            }
            // closing the Iframe is a little bit more complicated
            else if (LocalWindowMode === 'Iframe') {
                $('iframe.PopupIframe[data-popuptype=' + PopupType + ']', ParentObject.document).remove();
                $('html', ParentObject.document).removeClass('NoScroll');
            }
        }

        CheckOpenPopups();
    };

    /**
     * @name ExecuteInParentWindow
     * @memberof Core.UI.Popup
     * @function
     * @param {Function} FunctionToExecute - The callback function to execute in the parent window.
     * @param {Array} [FunctionParameters] - Optional function parameters as array.
     * @description
     *      Takes a callback function and hands it over to the parent window (to be executed there).
     *      This is needed to call a function in the parent window from the popup window. The popup window
     *      can be a real popup or an iframe, so it is not as easy as calling window.opener.
     *      IMPORTANT: The FunctionToExecute always needs the ParentWindowObject as first Parameter,
     *      which is used inside this function.
     */
    TargetNS.ExecuteInParentWindow = function (FunctionToExecute, FunctionParameters) {
        var ParentWindow = GetWindowParentObject();

        if (typeof ParentWindow === 'undefined' || ParentWindow === null) {
            return;
        }

        if (typeof FunctionParameters === 'undefined') {
            FunctionParameters = [ParentWindow];
        }
        else {
            FunctionParameters.unshift(ParentWindow);
        }

        if (typeof FunctionToExecute !== 'undefined' && $.isFunction(FunctionToExecute)) {
            // call the function with a new this context (first param)
            // and additional params where the first one is also the parent window object (for use within the function)
            FunctionToExecute.apply(ParentWindow, FunctionParameters);
        }
    };

    /**
     * @name Init
     * @memberof Core.UI.Popup
     * @function
     * @description
     *      The init function.
     */
    TargetNS.Init = function () {

        var PopupURL,
            PopupClose = Core.Config.Get('PopupClose');

        if (PopupClose === 'LoadParentURLAndClose') {
            PopupURL = Core.Config.Get('PopupURL');
            if (TargetNS.CurrentIsPopupWindow()) {
                TargetNS.ExecuteInParentWindow(function(WindowObject) {
                    WindowObject.Core.UI.Popup.FirePopupEvent('URL', { URL: Core.Config.Get('Baselink') + PopupURL });
                });
                TargetNS.ClosePopup();
            }
            else {
                window.location.href = Core.Config.Get('Baselink') + PopupURL;
            }
        }
        else if (PopupClose === 'ReloadParentAndClose') {
            TargetNS.ExecuteInParentWindow(function(WindowObject) {
                WindowObject.Core.UI.Popup.FirePopupEvent('Reload');
            });
            TargetNS.ClosePopup();
        }

        $(window).on('beforeunload.Popup', function () {
            return Core.UI.Popup.CheckPopupsOnUnload();
        });
        Core.App.BindWindowUnloadEvent('Popup', Core.UI.Popup.ClosePopupsOnUnload);
        Core.UI.Popup.RegisterPopupEvent();

        // if this window is a popup itself, register another function
        if (CurrentIsPopupWindow()) {
            Core.UI.Popup.InitRegisterPopupAtParentWindow();
            $('.CancelClosePopup').on('click', function () {
                TargetNS.ClosePopup();
            });
            $('.UndoClosePopup').on('click', function () {
                var RedirectURL = $(this).attr('href'),
                    ParentWindow = GetWindowParentObject();
                ParentWindow.Core.UI.Popup.FirePopupEvent('URL', { URL: RedirectURL });
                TargetNS.ClosePopup();
            });

            // add a class to the body element, if this popup is a real popup
            if (window.opener) {
                $('body').addClass('RealPopup');
            }
        }
    };

    Core.Init.RegisterNamespace(TargetNS, 'APP_GLOBAL');

    return TargetNS;
}(Core.UI.Popup || {}));

/*eslint-enable no-window*/
