[TASK] Migrate EXT:backend LoginRefresh to TypeScript 75/57475/7
authorFrank Naegler <frank.naegler@typo3.org>
Thu, 5 Jul 2018 11:25:09 +0000 (13:25 +0200)
committerAndreas Fernandez <a.fernandez@scripting-base.de>
Fri, 6 Jul 2018 18:47:22 +0000 (20:47 +0200)
Resolves: #82595
Releases: master
Change-Id: Iebec35d3f1be6b70e3976a04c9820c9277a845e3
Reviewed-on: https://review.typo3.org/57475
Tested-by: TYPO3com <no-reply@typo3.com>
Reviewed-by: Frank Naegler <frank.naegler@typo3.org>
Reviewed-by: Anja Leichsenring <aleichsenring@ab-softlab.de>
Tested-by: Anja Leichsenring <aleichsenring@ab-softlab.de>
Reviewed-by: Andreas Fernandez <a.fernandez@scripting-base.de>
Tested-by: Andreas Fernandez <a.fernandez@scripting-base.de>
Build/package.json
Build/types/TYPO3/index.d.ts
Build/yarn.lock
typo3/sysext/backend/Resources/Private/TypeScript/LoginRefresh.ts [new file with mode: 0644]
typo3/sysext/backend/Resources/Public/JavaScript/LoginRefresh.js

index f6277e8..d025812 100644 (file)
@@ -10,6 +10,7 @@
   "devDependencies": {
     "@claviska/jquery-minicolors": "^2.2.6",
     "@types/bootstrap": "^3.3.34",
+    "@types/chrome": "^0.0.69",
     "@types/ckeditor": "0.0.46",
     "@types/imagesloaded": "^4.1.1",
     "@types/jasmine": "^2.5.53",
index 81b9faa..189d264 100644 (file)
@@ -9,6 +9,7 @@ declare namespace TYPO3 {
   export let DebugConsole: any;
   export let Icons: any;
   export let InfoWindow: any;
+  export let LoginRefresh: any;
   export let ModuleMenu: any;
   export let Notification: any;
   export let Modal: any;
@@ -19,6 +20,7 @@ declare namespace TYPO3 {
   export let Utility: any;
   export const lang: any;
   export const settings: any;
+  export const configuration: any;
   export namespace CMS {
     export namespace Backend {
       export class FormEngineValidation {
index ce1e0cd..3a6ee40 100644 (file)
   dependencies:
     "@types/jquery" "*"
 
+"@types/chrome@^0.0.69":
+  version "0.0.69"
+  resolved "https://registry.yarnpkg.com/@types/chrome/-/chrome-0.0.69.tgz#1d45bb79dd712b05704e4411f9eff3f270e19651"
+  dependencies:
+    "@types/filesystem" "*"
+
 "@types/ckeditor@0.0.46":
   version "0.0.46"
   resolved "https://registry.yarnpkg.com/@types/ckeditor/-/ckeditor-0.0.46.tgz#9ab258c8344f3ff8a8847069dbab11fd5210bd61"
 
+"@types/filesystem@*":
+  version "0.0.28"
+  resolved "https://registry.yarnpkg.com/@types/filesystem/-/filesystem-0.0.28.tgz#3fd7735830f2c7413cb5ac45780bc45904697b0e"
+  dependencies:
+    "@types/filewriter" "*"
+
+"@types/filewriter@*":
+  version "0.0.28"
+  resolved "https://registry.yarnpkg.com/@types/filewriter/-/filewriter-0.0.28.tgz#c054e8af4d9dd75db4e63abc76f885168714d4b3"
+
 "@types/imagesloaded@^4.1.1":
   version "4.1.1"
   resolved "https://registry.yarnpkg.com/@types/imagesloaded/-/imagesloaded-4.1.1.tgz#25e25cca04042b655f7f99f30da332fa5d5c690f"
diff --git a/typo3/sysext/backend/Resources/Private/TypeScript/LoginRefresh.ts b/typo3/sysext/backend/Resources/Private/TypeScript/LoginRefresh.ts
new file mode 100644 (file)
index 0000000..f61ac61
--- /dev/null
@@ -0,0 +1,501 @@
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+import * as $ from 'jquery';
+import Typo3Notification = require('TYPO3/CMS/Backend/Notification');
+
+enum MarkupIdentifiers {
+  loginrefresh = 't3js-modal-loginrefresh',
+  lockedModal = 't3js-modal-backendlocked',
+  loginFormModal = 't3js-modal-backendloginform'
+}
+
+// hack is required, because the Notification definition is wrong
+declare var Notification: any;
+
+/**
+ * Module: TYPO3/CMS/Backend/LoginRefresh
+ * @exports TYPO3/CMS/Backend/LoginRefresh
+ */
+class LoginRefresh {
+  private options: any = {
+    modalConfig: {
+      backdrop: 'static'
+    }
+  };
+  private webNotification: Notification = null;
+  private intervalTime: number = 60;
+  private intervalId: number = null;
+  private backendIsLocked: boolean = false;
+  private isTimingOut: boolean = false;
+  private $timeoutModal: JQuery = null;
+  private $backendLockedModal: JQuery = null;
+  private $loginForm: JQuery = null;
+  private loginFramesetUrl: string = '';
+  private logoutUrl: string = '';
+
+  /**
+   * Initialize login refresh
+   */
+  public initialize(): void {
+    this.initializeTimeoutModal();
+    this.initializeBackendLockedModal();
+    this.initializeLoginForm();
+
+    this.startTask();
+
+    if (document.location.protocol === 'https:' && typeof Notification !== 'undefined' && Notification.permission !== 'granted') {
+      Notification.requestPermission();
+    }
+  }
+
+  /**
+   * Start the task
+   */
+  public startTask(): void {
+    if (this.intervalId !== null) {
+      return;
+    }
+    // set interval to 60 seconds
+    let interval: Number = this.intervalTime * 1000;
+    this.intervalId = setInterval(this.checkActiveSession, interval);
+  }
+
+  /**
+   * Stop the task
+   */
+  public stopTask(): void {
+    clearInterval(this.intervalId);
+    this.intervalId = null;
+  }
+
+  /**
+   * Set interval time
+   *
+   * @param {number} intervalTime
+   */
+  public setIntervalTime(intervalTime: number): void {
+    // To avoid the integer overflow in setInterval, we limit the interval time to be one request per day
+    this.intervalTime = Math.min(intervalTime, 86400);
+  }
+
+  /**
+   * Set the logout URL
+   *
+   * @param {string} logoutUrl
+   */
+  public setLogoutUrl(logoutUrl: string): void {
+    this.logoutUrl = logoutUrl;
+  }
+
+  /**
+   * Set login frameset url
+   */
+  public setLoginFramesetUrl(loginFramesetUrl: string): void {
+    this.loginFramesetUrl = loginFramesetUrl;
+  }
+
+  /**
+   * Shows the timeout dialog. If the backend is not focused, a Web Notification
+   * is displayed, too.
+   */
+  public showTimeoutModal(): void {
+    this.isTimingOut = true;
+    this.$timeoutModal.modal(this.options.modalConfig);
+    this.fillProgressbar(this.$timeoutModal);
+
+    if (document.location.protocol === 'https:' && typeof Notification !== 'undefined'
+      && Notification.permission === 'granted' && document.hidden) {
+      this.webNotification = new Notification(TYPO3.lang['mess.login_about_to_expire_title'], {
+        body: TYPO3.lang['mess.login_about_to_expire'],
+        icon: '/typo3/sysext/backend/Resources/Public/Images/Logo.png'
+      });
+      this.webNotification.onclick = () => {
+        window.focus();
+      };
+    }
+  }
+
+  /**
+   * Hides the timeout dialog. If a Web Notification is displayed, close it too.
+   */
+  public hideTimeoutModal(): void {
+    this.isTimingOut = false;
+    this.$timeoutModal.modal('hide');
+
+    if (typeof Notification !== 'undefined' && this.webNotification !== null) {
+      this.webNotification.close();
+    }
+  }
+
+  /**
+   * Shows the "backend locked" dialog.
+   */
+  public showBackendLockedModal(): void {
+    this.$backendLockedModal.modal(this.options.modalConfig);
+  }
+
+  /**
+   * Hides the "backend locked" dialog.
+   */
+  public hideBackendLockedModal(): void {
+    this.$backendLockedModal.modal('hide');
+  }
+
+  /**
+   * Shows the login form.
+   */
+  public showLoginForm(): void {
+    // log off for sure
+    $.ajax({
+      url: TYPO3.settings.ajaxUrls.logout,
+      method: 'GET',
+      success: () => {
+        TYPO3.configuration.showRefreshLoginPopup
+          ? this.showLoginPopup()
+          : this.$loginForm.modal(this.options.modalConfig);
+      }
+    });
+  }
+
+  /**
+   * Opens the login form in a new window.
+   */
+  public showLoginPopup(): void {
+    const vHWin = window.open(
+      this.loginFramesetUrl,
+      'relogin_' + Math.random().toString(16).slice(2),
+      'height=450,width=700,status=0,menubar=0,location=1'
+    );
+    if (vHWin) {
+      vHWin.focus();
+    }
+  }
+
+  /**
+   * Hides the login form.
+   */
+  public hideLoginForm(): void {
+    this.$loginForm.modal('hide');
+  }
+
+  /**
+   * Generates the modal displayed if the backend is locked.
+   */
+  protected initializeBackendLockedModal(): void {
+    this.$backendLockedModal = this.generateModal(MarkupIdentifiers.lockedModal);
+    this.$backendLockedModal.find('.modal-header h4').text(TYPO3.lang['mess.please_wait']);
+    this.$backendLockedModal.find('.modal-body').append(
+      $('<p />').text(TYPO3.lang['mess.be_locked'])
+    );
+    this.$backendLockedModal.find('.modal-footer').remove();
+
+    $('body').append(this.$backendLockedModal);
+  }
+
+  /**
+   * Generates the modal displayed on near session time outs
+   */
+  protected initializeTimeoutModal(): void {
+    this.$timeoutModal = this.generateModal(MarkupIdentifiers.loginrefresh);
+    this.$timeoutModal.addClass('modal-severity-notice');
+    this.$timeoutModal.find('.modal-header h4').text(TYPO3.lang['mess.login_about_to_expire_title']);
+    this.$timeoutModal.find('.modal-body').append(
+      $('<p />').text(TYPO3.lang['mess.login_about_to_expire']),
+      $('<div />', {class: 'progress'}).append(
+        $('<div />', {
+          class: 'progress-bar progress-bar-warning progress-bar-striped active',
+          role: 'progressbar',
+          'aria-valuemin': '0',
+          'aria-valuemax': '100'
+        }).append(
+          $('<span />', {class: 'sr-only'})
+        )
+      )
+    );
+    this.$timeoutModal.find('.modal-footer').append(
+      $('<button />', {
+        class: 'btn btn-default',
+        'data-action': 'logout'
+      }).text(TYPO3.lang['mess.refresh_login_logout_button']).on('click', () => {
+        top.location.href = this.logoutUrl;
+      }),
+      $('<button />', {
+        class: 'btn btn-primary t3js-active',
+        'data-action': 'refreshSession'
+      }).text(TYPO3.lang['mess.refresh_login_refresh_button']).on('click', () => {
+        $.ajax({
+          url: TYPO3.settings.ajaxUrls.login_timedout,
+          method: 'GET',
+          success: () => {
+            this.hideTimeoutModal();
+          }
+        });
+      })
+    );
+    this.registerDefaultModalEvents(this.$timeoutModal);
+
+    $('body').append(this.$timeoutModal);
+  }
+
+  /**
+   * Generates the login form displayed if the session has timed out.
+   */
+  protected initializeLoginForm(): void {
+    if (TYPO3.configuration.showRefreshLoginPopup) {
+      // dialog is not required if "showRefreshLoginPopup" is enabled
+      return;
+    }
+
+    this.$loginForm = this.generateModal(MarkupIdentifiers.loginFormModal);
+    this.$loginForm.addClass('modal-notice');
+    let refresh_login_title = String(TYPO3.lang['mess.refresh_login_title']).replace('%s', TYPO3.configuration.username);
+    this.$loginForm.find('.modal-header h4').text(refresh_login_title);
+    this.$loginForm.find('.modal-body').append(
+      $('<p />').text(TYPO3.lang['mess.login_expired']),
+      $('<form />', {
+        id: 'beLoginRefresh',
+        method: 'POST',
+        action: TYPO3.settings.ajaxUrls.login
+      }).append(
+        $('<div />', {class: 'form-group'}).append(
+          $('<input />', {
+            type: 'password',
+            name: 'p_field',
+            autofocus: 'autofocus',
+            class: 'form-control',
+            placeholder: TYPO3.lang['mess.refresh_login_password'],
+            'data-rsa-encryption': 't3-loginrefres-userident'
+          })
+        ),
+        $('<input />', {type: 'hidden', name: 'username', value: TYPO3.configuration.username}),
+        $('<input />', {type: 'hidden', name: 'userident', id: 't3-loginrefres-userident'})
+      )
+    );
+    this.$loginForm.find('.modal-footer').append(
+      $('<a />', {
+        href: this.logoutUrl,
+        class: 'btn btn-default'
+      }).text(TYPO3.lang['mess.refresh_exit_button']),
+      $('<button />', {type: 'button', class: 'btn btn-primary', 'data-action': 'refreshSession'})
+        .text(TYPO3.lang['mess.refresh_login_button'])
+        .on('click', () => {
+          this.$loginForm.find('form').submit();
+        })
+    );
+    this.registerDefaultModalEvents(this.$loginForm).on('submit', this.submitForm);
+    $('body').append(this.$loginForm);
+    if (require.specified('TYPO3/CMS/Rsaauth/RsaEncryptionModule')) {
+      require(['TYPO3/CMS/Rsaauth/RsaEncryptionModule'], function(RsaEncryption: any): void {
+        RsaEncryption.registerForm($('#beLoginRefresh').get(0));
+      });
+    }
+  }
+
+  /**
+   * Generates a modal dialog as template.
+   *
+   * @param {string} identifier
+   * @returns {JQuery}
+   */
+  protected generateModal(identifier: string): JQuery {
+    return $('<div />', {
+      id: identifier,
+      class: 't3js-modal ' + identifier + ' modal modal-type-default modal-severity-notice modal-style-light modal-size-small fade'
+    }).append(
+      $('<div />', {class: 'modal-dialog'}).append(
+        $('<div />', {class: 'modal-content'}).append(
+          $('<div />', {class: 'modal-header'}).append(
+            $('<h4 />', {class: 'modal-title'})
+          ),
+          $('<div />', {class: 'modal-body'}),
+          $('<div />', {class: 'modal-footer'})
+        )
+      )
+    );
+  }
+
+  /**
+   * Fills the progressbar attached to the given modal.
+   */
+  protected fillProgressbar($activeModal: JQuery): void {
+    if (!this.isTimingOut) {
+      return;
+    }
+
+    const max = 100;
+    let current = 0;
+    const $progressBar = $activeModal.find('.progress-bar');
+    const $srText = $progressBar.children('.sr-only');
+
+    const progress = setInterval(() => {
+      const isOverdue = (current >= max);
+      if (!this.isTimingOut || isOverdue) {
+        clearInterval(progress);
+
+        if (isOverdue) {
+          // show login form
+          this.hideTimeoutModal();
+          this.showLoginForm();
+        }
+
+        // reset current
+        current = 0;
+      } else {
+        current += 1;
+      }
+
+      const percentText = (current) + '%';
+      $progressBar.css('width', percentText);
+      $srText.text(percentText);
+    },                           300);
+  }
+
+  /**
+   * Creates additional data based on the security level and "submits" the form
+   * via an AJAX request.
+   *
+   * @param {JQueryEventObject} event
+   */
+  protected submitForm(event: JQueryEventObject): void {
+    event.preventDefault();
+
+    const $form = this.$loginForm.find('form');
+    const $passwordField = $form.find('input[name=p_field]');
+    const $useridentField = $form.find('input[name=userident]');
+    const passwordFieldValue = $passwordField.val();
+
+    if (passwordFieldValue === '' && $useridentField.val() === '') {
+      Typo3Notification.error(TYPO3.lang['mess.refresh_login_failed'], TYPO3.lang['mess.refresh_login_emptyPassword']);
+      $passwordField.focus();
+      return;
+    }
+
+    if (passwordFieldValue) {
+      $useridentField.val(passwordFieldValue);
+      $passwordField.val('');
+    }
+
+    const postData: any = {
+      login_status: 'login'
+    };
+    $.each($form.serializeArray(), function(i: number, field: any): void {
+      postData[field.name] = field.value;
+    });
+    $.ajax({
+      url: $form.attr('action'),
+      method: 'POST',
+      data: postData,
+      success: (response) => {
+        if (response.login.success) {
+          // User is logged in
+          this.hideLoginForm();
+        } else {
+          Typo3Notification.error(TYPO3.lang['mess.refresh_login_failed'], TYPO3.lang['mess.refresh_login_failed_message']);
+          $passwordField.focus();
+        }
+      }
+    });
+  }
+
+  /**
+   * Registers the (shown|hidden).bs.modal events.
+   * If a modal is shown, the interval check is stopped. If the modal hides,
+   * the interval check starts again.
+   * This method is not invoked for the backend locked modal, because we still
+   * need to check if the backend gets unlocked again.
+   *
+   * @param {JQuery} $modal
+   * @returns {JQuery}
+   */
+  protected registerDefaultModalEvents($modal: JQuery): JQuery {
+    $modal.on('hidden.bs.modal', () => {
+      this.startTask();
+    }).on('shown.bs.modal', () => {
+      this.stopTask();
+      // focus the button which was configured as active button
+      this.$timeoutModal.find('.modal-footer .t3js-active').first().focus();
+    });
+    return $modal;
+  }
+
+  /**
+   * Periodically called task that checks if
+   *
+   * - the user's backend session is about to expire
+   * - the user's backend session has expired
+   * - the backend got locked
+   *
+   * and opens a dialog.
+   */
+  protected checkActiveSession = (): void => {
+    $.ajax({
+      url: TYPO3.settings.ajaxUrls.login_timedout,
+      success: (response) => {
+        if (response.login.locked) {
+          if (!this.backendIsLocked) {
+            this.backendIsLocked = true;
+            this.showBackendLockedModal();
+          }
+        } else {
+          if (this.backendIsLocked) {
+            this.backendIsLocked = false;
+            this.hideBackendLockedModal();
+          }
+        }
+
+        if (!this.backendIsLocked) {
+          if (response.login.timed_out || response.login.will_time_out) {
+            response.login.timed_out
+              ? this.showLoginForm()
+              : this.showTimeoutModal();
+          }
+        }
+      }
+    });
+  }
+}
+
+let loginRefreshObject;
+try {
+  // fetch from opening window
+  if (window.opener && window.opener.TYPO3 && window.opener.TYPO3.LoginRefresh) {
+    loginRefreshObject = window.opener.TYPO3.LoginRefresh;
+  }
+
+  // fetch from parent
+  if (parent && parent.window.TYPO3 && parent.window.TYPO3.LoginRefresh) {
+    loginRefreshObject = parent.window.TYPO3.LoginRefresh;
+  }
+
+  // fetch object from outer frame
+  if (top && top.TYPO3 && top.TYPO3.LoginRefresh) {
+    loginRefreshObject = top.TYPO3.LoginRefresh;
+  }
+} catch (e) {
+  // This only happens if the opener, parent or top is some other url (eg a local file)
+  // which loaded the current window. Then the browser's cross domain policy jumps in
+  // and raises an exception.
+  // For this case we are safe and we can create our global object below.
+}
+
+if (!loginRefreshObject) {
+  loginRefreshObject = new LoginRefresh();
+
+  // attach to global frame
+  if (typeof TYPO3 !== 'undefined') {
+    TYPO3.LoginRefresh = loginRefreshObject;
+  }
+}
+
+export = loginRefreshObject;
index 3bd56d0..1f93224 100644 (file)
  *
  * The TYPO3 project - inspiring people to share!
  */
-
-/**
- * Module: TYPO3/CMS/Backend/LoginRefresh
- * Task that periodically checks if a blocking event in the backend occurred and
- * displays a proper dialog to the user.
- */
-define(['jquery', 'TYPO3/CMS/Backend/Notification', 'bootstrap'], function($, Typo3Notification) {
-  'use strict';
-
-  /**
-   *
-   * @type {{identifier: {loginrefresh: string, lockedModal: string, loginFormModal: string}, options: {modalConfig: {backdrop: string}}, webNotification: null, intervalTime: integer, intervalId: null, backendIsLocked: boolean, isTimingOut: boolean, $timeoutModal: string, $backendLockedModal: string, $loginForm: string, loginFramesetUrl: string, logoutUrl: string}}
-   * @exports TYPO3/CMS/Backend/LoginRefresh
-   */
-  var LoginRefresh = {
-    identifier: {
-      loginrefresh: 't3js-modal-loginrefresh',
-      lockedModal: 't3js-modal-backendlocked',
-      loginFormModal: 't3js-modal-backendloginform'
-    },
-    options: {
-      modalConfig: {
-        backdrop: 'static'
-      }
-    },
-    webNotification: null,
-    intervalTime: 60,
-    intervalId: null,
-    backendIsLocked: false,
-    isTimingOut: false,
-    $timeoutModal: '',
-    $backendLockedModal: '',
-    $loginForm: '',
-    loginFramesetUrl: '',
-    logoutUrl: ''
-  };
-
-  /**
-   * Starts the session check task (if not running already)
-   */
-  LoginRefresh.startTask = function() {
-    if (LoginRefresh.intervalId !== null) {
-      return;
-    }
-
-    // set interval to 60 seconds
-    var interval = 1000 * LoginRefresh.intervalTime;
-    LoginRefresh.intervalId = setInterval(LoginRefresh.checkActiveSession, interval);
-  };
-
-  /**
-   * Stops the session check task
-   */
-  LoginRefresh.stopTask = function() {
-    clearInterval(LoginRefresh.intervalId);
-    LoginRefresh.intervalId = null;
-  };
-
-  /**
-   * Generates a modal dialog as template.
-   *
-   * @param {String} identifier
-   * @returns {Object}
-   */
-  LoginRefresh.generateModal = function(identifier) {
-    return $('<div />', {
-      id: identifier,
-      class: 't3js-modal ' + identifier + ' modal modal-type-default modal-severity-notice modal-style-light modal-size-small fade'
-    }).append(
-      $('<div />', {class: 'modal-dialog'}).append(
-        $('<div />', {class: 'modal-content'}).append(
-          $('<div />', {class: 'modal-header'}).append(
-            $('<h4 />', {class: 'modal-title'})
-          ),
-          $('<div />', {class: 'modal-body'}),
-          $('<div />', {class: 'modal-footer'})
-        )
-      )
-    );
-  };
-
-  /**
-   * Set interval time
-   *
-   * @param {integer} intervalTime
-   */
-  LoginRefresh.setIntervalTime = function(intervalTime) {
-    // To avoid the integer overflow in setInterval, we limit the interval time to be one request per day
-    LoginRefresh.intervalTime = Math.min(intervalTime, 86400);
-  };
-
-  /**
-   * Set logout url
-   *
-   * @param {String} logoutUrl
-   */
-  LoginRefresh.setLogoutUrl = function(logoutUrl) {
-    LoginRefresh.logoutUrl = logoutUrl;
-  };
-
-  /**
-   * Generates the modal displayed on near session time outs
-   */
-  LoginRefresh.initializeTimeoutModal = function() {
-    LoginRefresh.$timeoutModal = LoginRefresh.generateModal(LoginRefresh.identifier.loginrefresh);
-    LoginRefresh.$timeoutModal.addClass('modal-severity-notice');
-    LoginRefresh.$timeoutModal.find('.modal-header h4').text(TYPO3.lang['mess.login_about_to_expire_title']);
-    LoginRefresh.$timeoutModal.find('.modal-body').append(
-      $('<p />').text(TYPO3.lang['mess.login_about_to_expire']),
-      $('<div />', {class: 'progress'}).append(
-        $('<div />', {
-          class: 'progress-bar progress-bar-warning progress-bar-striped active',
-          role: 'progressbar',
-          'aria-valuemin': '0',
-          'aria-valuemax': '100'
-        }).append(
-          $('<span />', {class: 'sr-only'})
-        )
-      )
-    );
-    LoginRefresh.$timeoutModal.find('.modal-footer').append(
-      $('<button />', {
-        class: 'btn btn-default',
-        'data-action': 'logout'
-      }).text(TYPO3.lang['mess.refresh_login_logout_button']).on('click', function() {
-        top.location.href = LoginRefresh.logoutUrl;
-      }),
-      $('<button />', {
-        class: 'btn btn-primary t3js-active',
-        'data-action': 'refreshSession'
-      }).text(TYPO3.lang['mess.refresh_login_refresh_button']).on('click', function() {
-        $.ajax({
-          url: TYPO3.settings.ajaxUrls['login_timedout'],
-          method: 'GET',
-          success: function() {
-            LoginRefresh.hideTimeoutModal();
-          }
-        });
-      })
-    );
-    LoginRefresh.registerDefaultModalEvents(LoginRefresh.$timeoutModal);
-
-    $('body').append(LoginRefresh.$timeoutModal);
-  };
-
-  /**
-   * Shows the timeout dialog. If the backend is not focused, a Web Notification
-   * is displayed, too.
-   */
-  LoginRefresh.showTimeoutModal = function() {
-    LoginRefresh.isTimingOut = true;
-    LoginRefresh.$timeoutModal.modal(LoginRefresh.options.modalConfig);
-    LoginRefresh.fillProgressbar(LoginRefresh.$timeoutModal);
-
-    if (document.location.protocol === 'https:' && typeof Notification !== 'undefined' && Notification.permission === 'granted' && !LoginRefresh.isPageActive()) {
-      LoginRefresh.webNotification = new Notification(TYPO3.lang['mess.login_about_to_expire_title'], {
-        body: TYPO3.lang['mess.login_about_to_expire'],
-        icon: '/typo3/sysext/backend/Resources/Public/Images/Logo.png'
-      });
-      LoginRefresh.webNotification.onclick = function() {
-        window.focus();
-      };
-    }
-  };
-
-  /**
-   * Hides the timeout dialog. If a Web Notification is displayed, close it too.
-   */
-  LoginRefresh.hideTimeoutModal = function() {
-    LoginRefresh.isTimingOut = false;
-    LoginRefresh.$timeoutModal.modal('hide');
-
-    if (typeof Notification !== 'undefined' && LoginRefresh.webNotification !== null) {
-      LoginRefresh.webNotification.close();
-    }
-  };
-
-  /**
-   * Generates the modal displayed if the backend is locked.
-   */
-  LoginRefresh.initializeBackendLockedModal = function() {
-    LoginRefresh.$backendLockedModal = LoginRefresh.generateModal(LoginRefresh.identifier.lockedModal);
-    LoginRefresh.$backendLockedModal.find('.modal-header h4').text(TYPO3.lang['mess.please_wait']);
-    LoginRefresh.$backendLockedModal.find('.modal-body').append(
-      $('<p />').text(TYPO3.lang['mess.be_locked'])
-    );
-    LoginRefresh.$backendLockedModal.find('.modal-footer').remove();
-
-    $('body').append(LoginRefresh.$backendLockedModal);
-  };
-
-  /**
-   * Shows the "backend locked" dialog.
-   */
-  LoginRefresh.showBackendLockedModal = function() {
-    LoginRefresh.$backendLockedModal.modal(LoginRefresh.options.modalConfig);
-  };
-
-  /**
-   * Hides the "backend locked" dialog.
-   */
-  LoginRefresh.hideBackendLockedModal = function() {
-    LoginRefresh.$backendLockedModal.modal('hide');
-  };
-
-  /**
-   * Generates the login form displayed if the session has timed out.
-   */
-  LoginRefresh.initializeLoginForm = function() {
-    if (TYPO3.configuration.showRefreshLoginPopup) {
-      // dialog is not required if "showRefreshLoginPopup" is enabled
-      return;
-    }
-
-    LoginRefresh.$loginForm = LoginRefresh.generateModal(LoginRefresh.identifier.loginFormModal);
-    LoginRefresh.$loginForm.addClass('modal-notice');
-    var refresh_login_title = String(TYPO3.lang['mess.refresh_login_title']).replace('%s', TYPO3.configuration.username);
-    LoginRefresh.$loginForm.find('.modal-header h4').text(refresh_login_title);
-    LoginRefresh.$loginForm.find('.modal-body').append(
-      $('<p />').text(TYPO3.lang['mess.login_expired']),
-      $('<form />', {
-        id: 'beLoginRefresh',
-        method: 'POST',
-        action: TYPO3.settings.ajaxUrls['login']
-      }).append(
-        $('<div />', {class: 'form-group'}).append(
-          $('<input />', {
-            type: 'password',
-            name: 'p_field',
-            autofocus: 'autofocus',
-            class: 'form-control',
-            placeholder: TYPO3.lang['mess.refresh_login_password'],
-            'data-rsa-encryption': 't3-loginrefres-userident'
-          })
-        ),
-        $('<input />', {type: 'hidden', name: 'username', value: TYPO3.configuration.username}),
-        $('<input />', {type: 'hidden', name: 'userident', id: 't3-loginrefres-userident'})
-      )
-    );
-    LoginRefresh.$loginForm.find('.modal-footer').append(
-      $('<a />', {
-        href: LoginRefresh.logoutUrl,
-        class: 'btn btn-default'
-      }).text(TYPO3.lang['mess.refresh_exit_button']),
-      $('<button />', {type: 'button', class: 'btn btn-primary', 'data-action': 'refreshSession'})
-        .text(TYPO3.lang['mess.refresh_login_button'])
-        .on('click', function(e) {
-          LoginRefresh.$loginForm.find('form').submit();
-        })
-    );
-    LoginRefresh.registerDefaultModalEvents(LoginRefresh.$loginForm).on('submit', LoginRefresh.submitForm);
-    $('body').append(LoginRefresh.$loginForm);
-    if (require.specified('TYPO3/CMS/Rsaauth/RsaEncryptionModule')) {
-      require(['TYPO3/CMS/Rsaauth/RsaEncryptionModule'], function(RsaEncryption) {
-        RsaEncryption.registerForm($('#beLoginRefresh').get(0));
-      });
-    }
-  };
-
-  /**
-   * Shows the login form.
-   */
-  LoginRefresh.showLoginForm = function() {
-    // log off for sure
-    $.ajax({
-      url: TYPO3.settings.ajaxUrls['logout'],
-      method: 'GET',
-      success: function() {
-        if (TYPO3.configuration.showRefreshLoginPopup) {
-          LoginRefresh.showLoginPopup();
-        } else {
-          LoginRefresh.$loginForm.modal(LoginRefresh.options.modalConfig);
-        }
-      },
-      failure: function() {
-        alert('something went wrong');
-      }
-    });
-  };
-
-  /**
-   * Set login frameset url
-   */
-  LoginRefresh.setLoginFramesetUrl = function(loginFramesetUrl) {
-    LoginRefresh.loginFramesetUrl = loginFramesetUrl;
-  };
-
-  /**
-   * Opens the login form in a new window.
-   */
-  LoginRefresh.showLoginPopup = function() {
-    var vHWin = window.open(LoginRefresh.loginFramesetUrl, 'relogin_' + Math.random().toString(16).slice(2), 'height=450,width=700,status=0,menubar=0,location=1');
-    if (vHWin) {
-      vHWin.focus();
-    }
-  };
-
-  /**
-   * Hides the login form.
-   */
-  LoginRefresh.hideLoginForm = function() {
-    LoginRefresh.$loginForm.modal('hide');
-  };
-
-  /**
-   * Fills the progressbar attached to the given modal.
-   */
-  LoginRefresh.fillProgressbar = function($activeModal) {
-    if (!LoginRefresh.isTimingOut) {
-      return;
-    }
-
-    var max = 100,
-      current = 0,
-      $progressBar = $activeModal.find('.progress-bar'),
-      $srText = $progressBar.children('.sr-only');
-
-    var progress = setInterval(function() {
-      var isOverdue = (current >= max);
-
-      if (!LoginRefresh.isTimingOut || isOverdue) {
-        clearInterval(progress);
-
-        if (isOverdue) {
-          // show login form
-          LoginRefresh.hideTimeoutModal();
-          LoginRefresh.showLoginForm();
-        }
-
-        // reset current
-        current = 0;
-      } else {
-        current += 1;
-      }
-
-      var percentText = (current) + '%';
-      $progressBar.css('width', percentText);
-      $srText.text(percentText);
-    }, 300);
-  };
-
-  /**
-   * Creates additional data based on the security level and "submits" the form
-   * via an AJAX request.
-   *
-   * @param {Event} event
-   */
-  LoginRefresh.submitForm = function(event) {
-    event.preventDefault();
-
-    var $form = LoginRefresh.$loginForm.find('form'),
-      $passwordField = $form.find('input[name=p_field]'),
-      $useridentField = $form.find('input[name=userident]'),
-      passwordFieldValue = $passwordField.val();
-
-    if (passwordFieldValue === '' && $useridentField.val() === '') {
-      Typo3Notification.error(TYPO3.lang['mess.refresh_login_failed'], TYPO3.lang['mess.refresh_login_emptyPassword']);
-      $passwordField.focus();
-      return;
-    }
-
-    if (passwordFieldValue) {
-      $useridentField.val(passwordFieldValue);
-      $passwordField.val('');
-    }
-
-    var postData = {
-      login_status: 'login'
-    };
-    $.each($form.serializeArray(), function(i, field) {
-      postData[field.name] = field.value;
-    });
-    $.ajax({
-      url: $form.attr('action'),
-      method: 'POST',
-      data: postData,
-      success: function(response) {
-        var result = response.login;
-        if (result.success) {
-          // User is logged in
-          LoginRefresh.hideLoginForm();
-        } else {
-          Typo3Notification.error(TYPO3.lang['mess.refresh_login_failed'], TYPO3.lang['mess.refresh_login_failed_message']);
-          $passwordField.focus();
-        }
-      }
-    });
-  };
-
-  /**
-   * Registers the (shown|hidden).bs.modal events.
-   * If a modal is shown, the interval check is stopped. If the modal hides,
-   * the interval check starts again.
-   * This method is not invoked for the backend locked modal, because we still
-   * need to check if the backend gets unlocked again.
-   *
-   * @param {Object} $modal
-   * @returns {Object}
-   */
-  LoginRefresh.registerDefaultModalEvents = function($modal) {
-    $modal.on('hidden.bs.modal', function() {
-      LoginRefresh.startTask();
-    }).on('shown.bs.modal', function() {
-      LoginRefresh.stopTask();
-      // focus the button which was configured as active button
-      LoginRefresh.$timeoutModal.find('.modal-footer .t3js-active').first().focus();
-    });
-
-    return $modal;
-  };
-
-  /**
-   * Checks if the user is in focus of the backend.
-   * Thanks to http://stackoverflow.com/a/19519701
-   */
-  LoginRefresh.isPageActive = function() {
-    var stateKey, eventKey, keys = {
-      hidden: 'visibilitychange',
-      webkitHidden: 'webkitvisibilitychange',
-      mozHidden: 'mozvisibilitychange',
-      msHidden: 'msvisibilitychange'
-    };
-
-    for (stateKey in keys) {
-      if (stateKey in document) {
-        eventKey = keys[stateKey];
-        break;
-      }
-    }
-    return function(c) {
-      if (c) {
-        document.addEventListener(eventKey, c);
-      }
-      return !document[stateKey];
-    }();
-  };
-
-  /**
-   * Periodically called task that checks if
-   *
-   * - the user's backend session is about to expire
-   * - the user's backend session has expired
-   * - the backend got locked
-   *
-   * and opens a dialog.
-   */
-  LoginRefresh.checkActiveSession = function() {
-    $.ajax({
-      url: TYPO3.settings.ajaxUrls['login_timedout'],
-      success: function(response) {
-        if (response.login.locked) {
-          if (!LoginRefresh.backendIsLocked) {
-            LoginRefresh.backendIsLocked = true;
-            LoginRefresh.showBackendLockedModal();
-          }
-        } else {
-          if (LoginRefresh.backendIsLocked) {
-            LoginRefresh.backendIsLocked = false;
-            LoginRefresh.hideBackendLockedModal();
-          }
-        }
-
-        if (!LoginRefresh.backendIsLocked) {
-          if (response.login.timed_out || response.login.will_time_out) {
-            if (response.login.timed_out) {
-              LoginRefresh.showLoginForm();
-            } else {
-              LoginRefresh.showTimeoutModal();
-            }
-          }
-        }
-      }
-    });
-  };
-
-  LoginRefresh.initialize = function() {
-    LoginRefresh.initializeTimeoutModal();
-    LoginRefresh.initializeBackendLockedModal();
-    LoginRefresh.initializeLoginForm();
-
-    LoginRefresh.startTask();
-
-    if (document.location.protocol === 'https:' && typeof Notification !== 'undefined' && Notification.permission !== 'granted') {
-      Notification.requestPermission();
-    }
-  };
-
-  // expose to global
-  TYPO3.LoginRefresh = LoginRefresh;
-
-  return LoginRefresh;
-});
+define(["require","exports","jquery","TYPO3/CMS/Backend/Notification"],function(o,t,i,e){"use strict";var n,a;(a=n||(n={})).loginrefresh="t3js-modal-loginrefresh",a.lockedModal="t3js-modal-backendlocked",a.loginFormModal="t3js-modal-backendloginform";var s,l=function(){function t(){var o=this;this.options={modalConfig:{backdrop:"static"}},this.webNotification=null,this.intervalTime=60,this.intervalId=null,this.backendIsLocked=!1,this.isTimingOut=!1,this.$timeoutModal=null,this.$backendLockedModal=null,this.$loginForm=null,this.loginFramesetUrl="",this.logoutUrl="",this.checkActiveSession=function(){i.ajax({url:TYPO3.settings.ajaxUrls.login_timedout,success:function(t){t.login.locked?o.backendIsLocked||(o.backendIsLocked=!0,o.showBackendLockedModal()):o.backendIsLocked&&(o.backendIsLocked=!1,o.hideBackendLockedModal()),o.backendIsLocked||(t.login.timed_out||t.login.will_time_out)&&(t.login.timed_out?o.showLoginForm():o.showTimeoutModal())}})}}return t.prototype.initialize=function(){this.initializeTimeoutModal(),this.initializeBackendLockedModal(),this.initializeLoginForm(),this.startTask(),"https:"===document.location.protocol&&"undefined"!=typeof Notification&&"granted"!==Notification.permission&&Notification.requestPermission()},t.prototype.startTask=function(){if(null===this.intervalId){var o=1e3*this.intervalTime;this.intervalId=setInterval(this.checkActiveSession,o)}},t.prototype.stopTask=function(){clearInterval(this.intervalId),this.intervalId=null},t.prototype.setIntervalTime=function(o){this.intervalTime=Math.min(o,86400)},t.prototype.setLogoutUrl=function(o){this.logoutUrl=o},t.prototype.setLoginFramesetUrl=function(o){this.loginFramesetUrl=o},t.prototype.showTimeoutModal=function(){this.isTimingOut=!0,this.$timeoutModal.modal(this.options.modalConfig),this.fillProgressbar(this.$timeoutModal),"https:"===document.location.protocol&&"undefined"!=typeof Notification&&"granted"===Notification.permission&&document.hidden&&(this.webNotification=new Notification(TYPO3.lang["mess.login_about_to_expire_title"],{body:TYPO3.lang["mess.login_about_to_expire"],icon:"/typo3/sysext/backend/Resources/Public/Images/Logo.png"}),this.webNotification.onclick=function(){window.focus()})},t.prototype.hideTimeoutModal=function(){this.isTimingOut=!1,this.$timeoutModal.modal("hide"),"undefined"!=typeof Notification&&null!==this.webNotification&&this.webNotification.close()},t.prototype.showBackendLockedModal=function(){this.$backendLockedModal.modal(this.options.modalConfig)},t.prototype.hideBackendLockedModal=function(){this.$backendLockedModal.modal("hide")},t.prototype.showLoginForm=function(){var o=this;i.ajax({url:TYPO3.settings.ajaxUrls.logout,method:"GET",success:function(){TYPO3.configuration.showRefreshLoginPopup?o.showLoginPopup():o.$loginForm.modal(o.options.modalConfig)}})},t.prototype.showLoginPopup=function(){var o=window.open(this.loginFramesetUrl,"relogin_"+Math.random().toString(16).slice(2),"height=450,width=700,status=0,menubar=0,location=1");o&&o.focus()},t.prototype.hideLoginForm=function(){this.$loginForm.modal("hide")},t.prototype.initializeBackendLockedModal=function(){this.$backendLockedModal=this.generateModal(n.lockedModal),this.$backendLockedModal.find(".modal-header h4").text(TYPO3.lang["mess.please_wait"]),this.$backendLockedModal.find(".modal-body").append(i("<p />").text(TYPO3.lang["mess.be_locked"])),this.$backendLockedModal.find(".modal-footer").remove(),i("body").append(this.$backendLockedModal)},t.prototype.initializeTimeoutModal=function(){var o=this;this.$timeoutModal=this.generateModal(n.loginrefresh),this.$timeoutModal.addClass("modal-severity-notice"),this.$timeoutModal.find(".modal-header h4").text(TYPO3.lang["mess.login_about_to_expire_title"]),this.$timeoutModal.find(".modal-body").append(i("<p />").text(TYPO3.lang["mess.login_about_to_expire"]),i("<div />",{class:"progress"}).append(i("<div />",{class:"progress-bar progress-bar-warning progress-bar-striped active",role:"progressbar","aria-valuemin":"0","aria-valuemax":"100"}).append(i("<span />",{class:"sr-only"})))),this.$timeoutModal.find(".modal-footer").append(i("<button />",{class:"btn btn-default","data-action":"logout"}).text(TYPO3.lang["mess.refresh_login_logout_button"]).on("click",function(){top.location.href=o.logoutUrl}),i("<button />",{class:"btn btn-primary t3js-active","data-action":"refreshSession"}).text(TYPO3.lang["mess.refresh_login_refresh_button"]).on("click",function(){i.ajax({url:TYPO3.settings.ajaxUrls.login_timedout,method:"GET",success:function(){o.hideTimeoutModal()}})})),this.registerDefaultModalEvents(this.$timeoutModal),i("body").append(this.$timeoutModal)},t.prototype.initializeLoginForm=function(){var t=this;if(!TYPO3.configuration.showRefreshLoginPopup){this.$loginForm=this.generateModal(n.loginFormModal),this.$loginForm.addClass("modal-notice");var e=String(TYPO3.lang["mess.refresh_login_title"]).replace("%s",TYPO3.configuration.username);this.$loginForm.find(".modal-header h4").text(e),this.$loginForm.find(".modal-body").append(i("<p />").text(TYPO3.lang["mess.login_expired"]),i("<form />",{id:"beLoginRefresh",method:"POST",action:TYPO3.settings.ajaxUrls.login}).append(i("<div />",{class:"form-group"}).append(i("<input />",{type:"password",name:"p_field",autofocus:"autofocus",class:"form-control",placeholder:TYPO3.lang["mess.refresh_login_password"],"data-rsa-encryption":"t3-loginrefres-userident"})),i("<input />",{type:"hidden",name:"username",value:TYPO3.configuration.username}),i("<input />",{type:"hidden",name:"userident",id:"t3-loginrefres-userident"}))),this.$loginForm.find(".modal-footer").append(i("<a />",{href:this.logoutUrl,class:"btn btn-default"}).text(TYPO3.lang["mess.refresh_exit_button"]),i("<button />",{type:"button",class:"btn btn-primary","data-action":"refreshSession"}).text(TYPO3.lang["mess.refresh_login_button"]).on("click",function(){t.$loginForm.find("form").submit()})),this.registerDefaultModalEvents(this.$loginForm).on("submit",this.submitForm),i("body").append(this.$loginForm),o.specified("TYPO3/CMS/Rsaauth/RsaEncryptionModule")&&o(["TYPO3/CMS/Rsaauth/RsaEncryptionModule"],function(o){o.registerForm(i("#beLoginRefresh").get(0))})}},t.prototype.generateModal=function(o){return i("<div />",{id:o,class:"t3js-modal "+o+" modal modal-type-default modal-severity-notice modal-style-light modal-size-small fade"}).append(i("<div />",{class:"modal-dialog"}).append(i("<div />",{class:"modal-content"}).append(i("<div />",{class:"modal-header"}).append(i("<h4 />",{class:"modal-title"})),i("<div />",{class:"modal-body"}),i("<div />",{class:"modal-footer"}))))},t.prototype.fillProgressbar=function(o){var t=this;if(this.isTimingOut)var i=0,e=o.find(".progress-bar"),n=e.children(".sr-only"),a=setInterval(function(){var o=i>=100;!t.isTimingOut||o?(clearInterval(a),o&&(t.hideTimeoutModal(),t.showLoginForm()),i=0):i+=1;var s=i+"%";e.css("width",s),n.text(s)},300)},t.prototype.submitForm=function(o){var t=this;o.preventDefault();var n=this.$loginForm.find("form"),a=n.find("input[name=p_field]"),s=n.find("input[name=userident]"),l=a.val();if(""===l&&""===s.val())return e.error(TYPO3.lang["mess.refresh_login_failed"],TYPO3.lang["mess.refresh_login_emptyPassword"]),void a.focus();l&&(s.val(l),a.val(""));var r={login_status:"login"};i.each(n.serializeArray(),function(o,t){r[t.name]=t.value}),i.ajax({url:n.attr("action"),method:"POST",data:r,success:function(o){o.login.success?t.hideLoginForm():(e.error(TYPO3.lang["mess.refresh_login_failed"],TYPO3.lang["mess.refresh_login_failed_message"]),a.focus())}})},t.prototype.registerDefaultModalEvents=function(o){var t=this;return o.on("hidden.bs.modal",function(){t.startTask()}).on("shown.bs.modal",function(){t.stopTask(),t.$timeoutModal.find(".modal-footer .t3js-active").first().focus()}),o},t}();try{window.opener&&window.opener.TYPO3&&window.opener.TYPO3.LoginRefresh&&(s=window.opener.TYPO3.LoginRefresh),parent&&parent.window.TYPO3&&parent.window.TYPO3.LoginRefresh&&(s=parent.window.TYPO3.LoginRefresh),top&&top.TYPO3&&top.TYPO3.LoginRefresh&&(s=top.TYPO3.LoginRefresh)}catch(o){}return s||(s=new l,"undefined"!=typeof TYPO3&&(TYPO3.LoginRefresh=s)),s});
\ No newline at end of file