[TASK] Migrate ContextMenu to TypeScript 65/56865/2
authorAndreas Fernandez <a.fernandez@scripting-base.de>
Sat, 5 May 2018 20:20:24 +0000 (22:20 +0200)
committerFrank Naegler <frank.naegler@typo3.org>
Tue, 8 May 2018 11:54:35 +0000 (13:54 +0200)
Resolves: #82579
Releases: master
Change-Id: I58165fff637ca72e921057e9441f6bc73f175808
Reviewed-on: https://review.typo3.org/56865
Tested-by: TYPO3com <no-reply@typo3.com>
Reviewed-by: Tymoteusz Motylewski <t.motylewski@gmail.com>
Tested-by: Tymoteusz Motylewski <t.motylewski@gmail.com>
Reviewed-by: Frank Naegler <frank.naegler@typo3.org>
Tested-by: Frank Naegler <frank.naegler@typo3.org>
typo3/sysext/backend/Resources/Private/TypeScript/ContextMenu.ts [new file with mode: 0644]
typo3/sysext/backend/Resources/Public/JavaScript/ContextMenu.js

diff --git a/typo3/sysext/backend/Resources/Private/TypeScript/ContextMenu.ts b/typo3/sysext/backend/Resources/Private/TypeScript/ContextMenu.ts
new file mode 100644 (file)
index 0000000..7a5a0a9
--- /dev/null
@@ -0,0 +1,353 @@
+/*
+ * 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 ContextMenuActions = require('./ContextMenuActions');
+
+interface MousePosition {
+  X: number;
+  Y: number;
+}
+
+interface ActiveRecord {
+  uid: number;
+  table: string;
+}
+
+interface MenuItem {
+  type: string;
+  icon: string;
+  label: string;
+  additionalAttributes?: { [key: string]: string };
+  childItems?: MenuItems;
+  callbackAction?: string;
+}
+
+interface MenuItems {
+  [key: string]: MenuItem;
+}
+
+/**
+ * Module: TYPO3/CMS/Backend/ContextMenu
+ * Container used to load the context menu via AJAX to render the result in a layer next to the mouse cursor
+ */
+class ContextMenu {
+  private mousePos: MousePosition = {X: null, Y: null};
+  private delayContextMenuHide: boolean = false;
+  private record: ActiveRecord = {uid: null, table: null};
+
+  /**
+   * @param {MenuItem} item
+   * @returns {string}
+   */
+  private static drawActionItem(item: MenuItem): string {
+    const attributes: { [key: string]: string } = item.additionalAttributes || {};
+    let attributesString = '';
+    for (let attribute in attributes) {
+      if (attributes.hasOwnProperty(attribute)) {
+        attributesString += ' ' + attribute + '="' + attributes[attribute] + '"';
+      }
+    }
+
+    return '<a class="list-group-item"'
+      + ' data-callback-action="' + item.callbackAction + '"'
+      + attributesString + '><span class="list-group-item-icon">' + item.icon + '</span> ' + item.label + '</a>';
+  }
+
+  /**
+   * @param {JQuery} $element
+   * @param {number} x
+   * @param {number} y
+   * @returns {boolean}
+   */
+  private static within($element: JQuery, x: number, y: number): boolean {
+    const offset = $element.offset();
+    return (
+      y >= offset.top &&
+      y < offset.top + $element.height() &&
+      x >= offset.left &&
+      x < offset.left + $element.width()
+    );
+  }
+
+  /**
+   * Manipulates the DOM to add the divs needed for context menu the bottom of the <body>-tag
+   */
+  private static initializeContextMenuContainer(): void {
+    if ($('#contentMenu0').length === 0) {
+      const code = '<div id="contentMenu0" class="context-menu"></div>'
+        + '<div id="contentMenu1" class="context-menu" style="display: block;"></div>';
+      $('body').append(code);
+    }
+  }
+
+  constructor() {
+    this.initializeEvents();
+  }
+
+  private initializeEvents(): void {
+    $(document).on('click contextmenu', '.t3js-contextmenutrigger', (e: JQueryEventObject): void => {
+      const $me = $(e.currentTarget);
+      // if there is an other "inline" onclick setting, context menu is not triggered
+      // usually this is the case for the foldertree
+      if ($(e.currentTarget).prop('onclick') && event.type === 'click') {
+        return;
+      }
+
+      e.preventDefault();
+      this.show(
+        $me.data('table'),
+        $me.data('uid'),
+        $me.data('context'),
+        $me.data('iteminfo'),
+        $me.data('parameters')
+      );
+    });
+
+    // register mouse movement inside the document
+    $(document).on('mousemove', this.storeMousePositionEvent);
+  }
+
+  /**
+   * Main function, called from most context menu links
+   *
+   * @param {string} table Table from where info should be fetched
+   * @param {number} uid The UID of the item
+   * @param {string} context Context of the item
+   * @param {string} enDisItems Items to disable / enable
+   * @param {string} addParams Additional params
+   */
+  private show(table: string, uid: number, context: string, enDisItems: string, addParams: string): void {
+    this.record = {table: table, uid: uid};
+
+    let parameters = '';
+
+    if (typeof table !== 'undefined') {
+      parameters += 'table=' + encodeURIComponent(table);
+    }
+    if (typeof uid !== 'undefined') {
+      parameters += (parameters.length > 0 ? '&' : '') + 'uid=' + uid;
+    }
+    if (typeof context !== 'undefined') {
+      parameters += (parameters.length > 0 ? '&' : '') + 'context=' + context;
+    }
+    if (typeof enDisItems !== 'undefined') {
+      parameters += (parameters.length > 0 ? '&' : '') + 'enDisItems=' + enDisItems;
+    }
+    if (typeof addParams !== 'undefined') {
+      parameters += (parameters.length > 0 ? '&' : '') + 'addParams=' + addParams;
+    }
+    this.fetch(parameters);
+  }
+
+  /**
+   * Make the AJAX request
+   *
+   * @param {string} parameters Parameters sent to the server
+   */
+  private fetch(parameters: string): void {
+    let url = TYPO3.settings.ajaxUrls.contextmenu;
+    if (parameters) {
+      url += ((url.indexOf('?') === -1) ? '?' : '&') + parameters;
+    }
+    $.ajax(url).done((response: MenuItems): void => {
+      if (typeof response !== 'undefined' && Object.keys(response).length > 0) {
+        this.populateData(response, 0);
+      }
+    });
+  }
+
+  /**
+   * Fills the context menu with content and displays it correctly
+   * depending on the mouse position
+   *
+   * @param {Array<MenuItem>} items The data that will be put in the menu
+   * @param {number} level The depth of the context menu
+   */
+  private populateData(items: MenuItems, level: number): void {
+    ContextMenu.initializeContextMenuContainer();
+
+    const $obj = $('#contentMenu' + level);
+
+    if ($obj.length && (level === 0 || $('#contentMenu' + (level - 1)).is(':visible'))) {
+      const elements = this.drawMenu(items, level);
+      $obj.html('<div class="list-group">' + elements + '</div>');
+
+      $('a.list-group-item', $obj).click((event: JQueryEventObject): void => {
+        event.preventDefault();
+        const $me = $(event.currentTarget);
+
+        if ($me.hasClass('list-group-item-submenu')) {
+          this.openSubmenu(level, $me);
+          return;
+        }
+
+        const callbackName = $me.data('callback-action');
+        const callbackModule = $me.data('callback-module');
+        // var clickItem = $(this);
+        if ($me.data('callback-module')) {
+          require([callbackModule], (callbackModuleCallback: any): void => {
+            callbackModuleCallback[callbackName].bind($me)(this.record.table, this.record.uid);
+          });
+        } else if (ContextMenuActions && typeof (ContextMenuActions as any)[callbackName] === 'function') {
+          (ContextMenuActions as any)[callbackName].bind($me)(this.record.table, this.record.uid);
+        } else {
+          console.log('action: ' + callbackName + ' not found');
+        }
+        this.hideAll();
+      });
+
+      $obj.css(this.getPosition($obj)).show();
+    }
+  }
+
+  /**
+   * @param {number} level
+   * @param {JQuery} $item
+   */
+  private openSubmenu(level: number, $item: JQuery): void {
+    const $obj = $('#contentMenu' + (level + 1)).html('');
+    $item.next().find('.list-group').clone(true).appendTo($obj);
+    $obj.css(this.getPosition($obj)).show();
+  }
+
+  private getPosition($obj: JQuery): {[key: string]: string} {
+    let x = this.mousePos.X;
+    let y = this.mousePos.Y;
+    const dimsWindow = {
+      width: $(window).width() - 20, // saving margin for scrollbars
+      height: $(window).height()
+    };
+
+    // dimensions for the context menu
+    const dims = {
+      width: $obj.width(),
+      height: $obj.height()
+    };
+
+    const relative = {
+      X: this.mousePos.X - $(document).scrollLeft(),
+      Y: this.mousePos.Y - $(document).scrollTop()
+    };
+
+    // adjusting the Y position of the layer to fit it into the window frame
+    // if there is enough space above then put it upwards,
+    // otherwise adjust it to the bottom of the window
+    if (dimsWindow.height - dims.height < relative.Y) {
+      if (relative.Y > dims.height) {
+        y -= (dims.height - 10);
+      } else {
+        y += (dimsWindow.height - dims.height - relative.Y);
+      }
+    }
+    // adjusting the X position like Y above, but align it to the left side of the viewport if it does not fit completely
+    if (dimsWindow.width - dims.width < relative.X) {
+      if (relative.X > dims.width) {
+        x -= (dims.width - 10);
+      } else if ((dimsWindow.width - dims.width - relative.X) < $(document).scrollLeft()) {
+        x = $(document).scrollLeft();
+      } else {
+        x += (dimsWindow.width - dims.width - relative.X);
+      }
+    }
+
+    return {left: x + 'px', top: y + 'px'};
+  }
+
+  /**
+   * fills the context menu with content and displays it correctly
+   * depending on the mouse position
+   *
+   * @param {MenuItems} items The data that will be put in the menu
+   * @param {Number} level The depth of the context menu
+   * @return {string}
+   */
+  private drawMenu(items: MenuItems, level: number): string {
+    let elements: string = '';
+    for (let key in items) {
+      if (items.hasOwnProperty(key)) {
+        const item = items[key];
+        if (item.type === 'item') {
+          elements += ContextMenu.drawActionItem(item);
+        } else if (item.type === 'divider') {
+          elements += '<a class="list-group-item list-group-item-divider"></a>';
+        } else if (item.type === 'submenu' || item.childItems) {
+          elements += '<a class="list-group-item list-group-item-submenu">'
+            + '<span class="list-group-item-icon">' + item.icon + '</span> '
+            + item.label + '&nbsp;&nbsp;<span class="fa fa-caret-right"></span>'
+            + '</a>';
+
+          const childElements = this.drawMenu(item.childItems, 1);
+          elements += '<div class="context-menu contentMenu' + (level + 1) + '" style="display:none;">'
+            + '<div class="list-group">' + childElements + '</div>'
+            + '</div>';
+        }
+      }
+    }
+    return elements;
+  }
+
+  /**
+   * event handler function that saves the
+   * actual position of the mouse
+   * in the context menu object
+   *
+   * @param {JQueryEventObject} event The event object
+   */
+  private storeMousePositionEvent = (event: JQueryEventObject): void => {
+    this.mousePos = {X: event.pageX, Y: event.pageY};
+    this.mouseOutFromMenu('#contentMenu0');
+    this.mouseOutFromMenu('#contentMenu1');
+  }
+
+  /**
+   * hides a visible menu if the mouse has moved outside
+   * of the object
+   *
+   * @param {string} obj The identifier of the object to hide
+   */
+  private mouseOutFromMenu(obj: string): void {
+    const $element = $(obj);
+
+    if ($element.length > 0 && $element.is(':visible') && !ContextMenu.within($element, this.mousePos.X, this.mousePos.Y)) {
+      this.hide(obj);
+    } else if ($element.length > 0 && $element.is(':visible')) {
+      this.delayContextMenuHide = true;
+    }
+  }
+
+  /**
+   * @param {string} obj
+   */
+  private hide(obj: string): void {
+    this.delayContextMenuHide = false;
+    window.setTimeout(
+      (): void => {
+        if (!this.delayContextMenuHide) {
+          $(obj).hide();
+        }
+      },
+      500
+    );
+  }
+
+  /**
+   * Hides all context menus
+   */
+  private hideAll(): void {
+    this.hide('#contentMenu0');
+    this.hide('#contentMenu1');
+  }
+}
+
+export = new ContextMenu();
index 0b4dc8b..f4ae419 100644 (file)
  *
  * The TYPO3 project - inspiring people to share!
  */
-
-/**
- * Module: TYPO3/CMS/Backend/ContextMenu
- * Javascript container used to load the context menu via AJAX
- * to render the result in a layer next to the mouse cursor
- */
-define(['jquery', 'TYPO3/CMS/Backend/ContextMenuActions'], function($, ContextMenuActions) {
-
-  /**
-   *
-   * @type {{mousePos: {X: null, Y: null}, delayContextMenuHide: boolean}}
-   * @exports TYPO3/CMS/Backend/ContextMenu
-   */
-  var ContextMenu = {
-    mousePos: {
-      X: null,
-      Y: null
-    },
-    delayContextMenuHide: false,
-    record: {
-      uid: null,
-      table: null
-    }
-  };
-
-  /**
-   * Initialize events
-   */
-  ContextMenu.initializeEvents = function() {
-    $(document).on('click contextmenu', '.t3js-contextmenutrigger', function(event) {
-      // if there is an other "inline" onclick setting, context menu is not triggered
-      // usually this is the case for the foldertree
-      if ($(this).prop('onclick') && event.type === 'click') {
-        return;
-      }
-      event.preventDefault();
-      ContextMenu.show(
-        $(this).data('table'),
-        $(this).data('uid'),
-        $(this).data('context'),
-        $(this).data('iteminfo'),
-        $(this).data('parameters')
-      );
-    });
-
-    // register mouse movement inside the document
-    $(document).on('mousemove', ContextMenu.storeMousePositionEvent);
-  };
-
-  /**
-   * Main function, called from most context menu links
-   *
-   * @param {String} table Table from where info should be fetched
-   * @param {(String|Number)} uid The UID of the item
-   * @param {String} context Context of the item
-   * @param {String} enDisItems Items to disable / enable
-   * @param {String} addParams Additional params
-   * @return void
-   */
-  ContextMenu.show = function(table, uid, context, enDisItems, addParams) {
-    ContextMenu.record = null;
-    ContextMenu.record = {table: table, uid: uid};
-
-    var parameters = '';
-
-    if (typeof table !== 'undefined') {
-      parameters += 'table=' + encodeURIComponent(table);
-    }
-    if (typeof uid !== 'undefined') {
-      parameters += (parameters.length > 0 ? '&' : '') + 'uid=' + uid;
-    }
-    if (typeof context !== 'undefined') {
-      parameters += (parameters.length > 0 ? '&' : '') + 'context=' + context;
-    }
-    if (typeof enDisItems !== 'undefined') {
-      parameters += (parameters.length > 0 ? '&' : '') + 'enDisItems=' + enDisItems;
-    }
-    if (typeof addParams !== 'undefined') {
-      parameters += (parameters.length > 0 ? '&' : '') + 'addParams=' + addParams;
-    }
-    this.fetch(parameters);
-  };
-
-  /**
-   * Make the AJAX request
-   *
-   * @param {array} parameters Parameters sent to the server
-   * @return void
-   */
-  ContextMenu.fetch = function(parameters) {
-    var url = TYPO3.settings.ajaxUrls['contextmenu'];
-    if (parameters) {
-      url += ((url.indexOf('?') == -1) ? '?' : '&') + parameters;
-    }
-    $.ajax(url).done(function(response) {
-      if (typeof response !== "undefined" && Object.keys(response).length > 0) {
-        ContextMenu.populateData(response, 0);
-      }
-    });
-  };
-
-  /**
-   * fills the context menu with content and displays it correctly
-   * depending on the mouse position
-   *
-   * @param {array} items The data that will be put in the menu
-   * @param {Number} level The depth of the context menu
-   */
-  ContextMenu.populateData = function(items, level) {
-    this.initializeContextMenuContainer();
-
-    level = parseInt(level, 10) || 0;
-    var $obj = $('#contentMenu' + level);
-
-    if ($obj.length && (level === 0 || $('#contentMenu' + (level - 1)).is(':visible'))) {
-      var elements = ContextMenu.drawMenu(items, level);
-      $obj.html('<div class="list-group">' + elements + '</div>');
-
-      $('a.list-group-item', $obj).click(function(event) {
-        event.preventDefault();
-
-        if ($(this).hasClass('list-group-item-submenu')) {
-          ContextMenu.openSubmenu(level, $(this));
-          return;
-        }
-
-        var callbackName = $(this).data('callback-action');
-        var callbackModule = $(this).data('callback-module');
-        var clickItem = $(this);
-        if (callbackModule) {
-          require([callbackModule], function(callbackModule) {
-            callbackModule[callbackName].bind(clickItem)(ContextMenu.record.table, ContextMenu.record.uid);
-          });
-        } else if (ContextMenuActions && ContextMenuActions[callbackName]) {
-          ContextMenuActions[callbackName].bind(clickItem)(ContextMenu.record.table, ContextMenu.record.uid);
-        } else {
-          console.log('action: ' + callbackName + ' not found');
-        }
-        ContextMenu.hideAll();
-      });
-
-      $obj.css(ContextMenu.getPosition($obj)).show();
-    }
-  };
-
-  ContextMenu.openSubmenu = function(level, $item) {
-    var $obj = $('#contentMenu' + (level + 1)).html('');
-    $item.next().find('.list-group').clone(true).appendTo($obj);
-    $obj.css(ContextMenu.getPosition($obj)).show();
-  };
-
-  ContextMenu.getPosition = function($obj) {
-    var x = this.mousePos.X;
-    var y = this.mousePos.Y;
-    var dimsWindow = {
-      width: $(window).width() - 20, // saving margin for scrollbars
-      height: $(window).height()
-    };
-
-    // dimensions for the context menu
-    var dims = {
-      width: $obj.width(),
-      height: $obj.height()
-    };
-
-    var relative = {
-      X: this.mousePos.X - $(document).scrollLeft(),
-      Y: this.mousePos.Y - $(document).scrollTop()
-    };
-
-    // adjusting the Y position of the layer to fit it into the window frame
-    // if there is enough space above then put it upwards,
-    // otherwise adjust it to the bottom of the window
-    if (dimsWindow.height - dims.height < relative.Y) {
-      if (relative.Y > dims.height) {
-        y -= (dims.height - 10);
-      } else {
-        y += (dimsWindow.height - dims.height - relative.Y);
-      }
-    }
-    // adjusting the X position like Y above, but align it to the left side of the viewport if it does not fit completely
-    if (dimsWindow.width - dims.width < relative.X) {
-      if (relative.X > dims.width) {
-        x -= (dims.width - 10);
-      } else if ((dimsWindow.width - dims.width - relative.X) < $(document).scrollLeft()) {
-        x = $(document).scrollLeft();
-      } else {
-        x += (dimsWindow.width - dims.width - relative.X);
-      }
-    }
-    return {left: x + 'px', top: y + 'px'};
-  };
-
-  /**
-   * fills the context menu with content and displays it correctly
-   * depending on the mouse position
-   *
-   * @param {array} items The data that will be put in the menu
-   * @param {Number} level The depth of the context menu
-   */
-  ContextMenu.drawMenu = function(items, level) {
-    var elements = '';
-    $.each(items, function(key, value) {
-      if (value.type === 'item') {
-        elements += ContextMenu.drawActionItem(value);
-      } else if (value.type === 'divider') {
-        elements += '<a class="list-group-item list-group-item-divider"></a>';
-      } else if (value.type === 'submenu' || value.childItems) {
-        elements += '<a class="list-group-item list-group-item-submenu"><span class="list-group-item-icon">' + value.icon + '</span> ' + value.label + '&nbsp;&nbsp;<span class="fa fa-caret-right"></span></a>';
-        var childElements = ContextMenu.drawMenu(value.childItems, 1);
-        elements += '<div class="context-menu contentMenu' + (level + 1) + '" style="display:none;"><div class="list-group">' + childElements + '</div></div>';
-
-      }
-    });
-    return elements;
-  };
-
-  ContextMenu.drawActionItem = function(value) {
-    var attributes = value.additionalAttributes || [];
-    $attributesString = '';
-    for (var attribute in attributes) {
-      $attributesString += ' ' + attribute + '="' + attributes[attribute] + '"';
-    }
-
-    return '<a class="list-group-item"'
-      + ' data-callback-action="' + value.callbackAction + '"'
-      + $attributesString + '><span class="list-group-item-icon">' + value.icon + '</span> ' + value.label + '</a>';
-  };
-  /**
-   * event handler function that saves the
-   * actual position of the mouse
-   * in the context menu object
-   *
-   * @param {Event} event The event object
-   */
-  ContextMenu.storeMousePositionEvent = function(event) {
-    ContextMenu.mousePos.X = event.pageX;
-    ContextMenu.mousePos.Y = event.pageY;
-    ContextMenu.mouseOutFromMenu('#contentMenu0');
-    ContextMenu.mouseOutFromMenu('#contentMenu1');
-  };
-
-  /**
-   * hides a visible menu if the mouse has moved outside
-   * of the object
-   *
-   * @param {Object} obj The object to hide
-   */
-  ContextMenu.mouseOutFromMenu = function(obj) {
-    var $element = $(obj);
-
-    if ($element.length > 0 && $element.is(':visible') && !this.within($element, this.mousePos.X, this.mousePos.Y)) {
-      this.hide($element);
-    } else if ($element.length > 0 && $element.is(':visible')) {
-      this.delayContextMenuHide = true;
-    }
-  };
-
-  /**
-   *
-   * @param {Object} $element
-   * @param {Number} x
-   * @param {Number} y
-   * @returns {Boolean}
-   */
-  ContextMenu.within = function($element, x, y) {
-    var offset = $element.offset();
-    return (
-      y >= offset.top &&
-      y < offset.top + $element.height() &&
-      x >= offset.left &&
-      x < offset.left + $element.width()
-    );
-  };
-
-  /**
-   * hides a context menu
-   *
-   * @param {Object} obj The context menu object to hide
-   */
-  ContextMenu.hide = function(obj) {
-    this.delayContextMenuHide = false;
-    window.setTimeout(function() {
-      if (!ContextMenu.delayContextMenuHide) {
-        $(obj).hide();
-      }
-    }, 500);
-  };
-
-  /**
-   * hides all context menus
-   */
-  ContextMenu.hideAll = function() {
-    this.hide('#contentMenu0');
-    this.hide('#contentMenu1');
-  };
-
-  /**
-   * manipulates the DOM to add the divs needed for context menu the bottom of the <body>-tag
-   */
-  ContextMenu.initializeContextMenuContainer = function() {
-    if ($('#contentMenu0').length === 0) {
-      var code = '<div id="contentMenu0" class="context-menu"></div><div id="contentMenu1" class="context-menu" style="display: block;"></div>';
-      $('body').append(code);
-    }
-  };
-
-  ContextMenu.initializeEvents();
-
-  return ContextMenu;
-});
+define(["require","exports","jquery","./ContextMenuActions"],function(t,e,n,i){"use strict";return new(function(){function e(){var t=this;this.mousePos={X:null,Y:null},this.delayContextMenuHide=!1,this.record={uid:null,table:null},this.storeMousePositionEvent=function(e){t.mousePos={X:e.pageX,Y:e.pageY},t.mouseOutFromMenu("#contentMenu0"),t.mouseOutFromMenu("#contentMenu1")},this.initializeEvents()}return e.drawActionItem=function(t){var e=t.additionalAttributes||{},n="";for(var i in e)e.hasOwnProperty(i)&&(n+=" "+i+'="'+e[i]+'"');return'<a class="list-group-item" data-callback-action="'+t.callbackAction+'"'+n+'><span class="list-group-item-icon">'+t.icon+"</span> "+t.label+"</a>"},e.within=function(t,e,n){var i=t.offset();return n>=i.top&&n<i.top+t.height()&&e>=i.left&&e<i.left+t.width()},e.initializeContextMenuContainer=function(){if(0===n("#contentMenu0").length){n("body").append('<div id="contentMenu0" class="context-menu"></div><div id="contentMenu1" class="context-menu" style="display: block;"></div>')}},e.prototype.initializeEvents=function(){var t=this;n(document).on("click contextmenu",".t3js-contextmenutrigger",function(e){var i=n(e.currentTarget);n(e.currentTarget).prop("onclick")&&"click"===event.type||(e.preventDefault(),t.show(i.data("table"),i.data("uid"),i.data("context"),i.data("iteminfo"),i.data("parameters")))}),n(document).on("mousemove",this.storeMousePositionEvent)},e.prototype.show=function(t,e,n,i,o){this.record={table:t,uid:e};var s="";void 0!==t&&(s+="table="+encodeURIComponent(t)),void 0!==e&&(s+=(s.length>0?"&":"")+"uid="+e),void 0!==n&&(s+=(s.length>0?"&":"")+"context="+n),void 0!==i&&(s+=(s.length>0?"&":"")+"enDisItems="+i),void 0!==o&&(s+=(s.length>0?"&":"")+"addParams="+o),this.fetch(s)},e.prototype.fetch=function(t){var e=this,i=TYPO3.settings.ajaxUrls.contextmenu;t&&(i+=(-1===i.indexOf("?")?"?":"&")+t),n.ajax(i).done(function(t){void 0!==t&&Object.keys(t).length>0&&e.populateData(t,0)})},e.prototype.populateData=function(o,s){var a=this;e.initializeContextMenuContainer();var u=n("#contentMenu"+s);if(u.length&&(0===s||n("#contentMenu"+(s-1)).is(":visible"))){var r=this.drawMenu(o,s);u.html('<div class="list-group">'+r+"</div>"),n("a.list-group-item",u).click(function(e){e.preventDefault();var o=n(e.currentTarget);if(o.hasClass("list-group-item-submenu"))a.openSubmenu(s,o);else{var u=o.data("callback-action"),r=o.data("callback-module");o.data("callback-module")?t([r],function(t){t[u].bind(o)(a.record.table,a.record.uid)}):i&&"function"==typeof i[u]?i[u].bind(o)(a.record.table,a.record.uid):console.log("action: "+u+" not found"),a.hideAll()}}),u.css(this.getPosition(u)).show()}},e.prototype.openSubmenu=function(t,e){var i=n("#contentMenu"+(t+1)).html("");e.next().find(".list-group").clone(!0).appendTo(i),i.css(this.getPosition(i)).show()},e.prototype.getPosition=function(t){var e=this.mousePos.X,i=this.mousePos.Y,o=n(window).width()-20,s=n(window).height(),a=t.width(),u=t.height(),r=this.mousePos.X-n(document).scrollLeft(),c=this.mousePos.Y-n(document).scrollTop();return s-u<c&&(c>u?i-=u-10:i+=s-u-c),o-a<r&&(r>a?e-=a-10:o-a-r<n(document).scrollLeft()?e=n(document).scrollLeft():e+=o-a-r),{left:e+"px",top:i+"px"}},e.prototype.drawMenu=function(t,n){var i="";for(var o in t)if(t.hasOwnProperty(o)){var s=t[o];if("item"===s.type)i+=e.drawActionItem(s);else if("divider"===s.type)i+='<a class="list-group-item list-group-item-divider"></a>';else if("submenu"===s.type||s.childItems){i+='<a class="list-group-item list-group-item-submenu"><span class="list-group-item-icon">'+s.icon+"</span> "+s.label+'&nbsp;&nbsp;<span class="fa fa-caret-right"></span></a>',i+='<div class="context-menu contentMenu'+(n+1)+'" style="display:none;"><div class="list-group">'+this.drawMenu(s.childItems,1)+"</div></div>"}}return i},e.prototype.mouseOutFromMenu=function(t){var i=n(t);i.length>0&&i.is(":visible")&&!e.within(i,this.mousePos.X,this.mousePos.Y)?this.hide(t):i.length>0&&i.is(":visible")&&(this.delayContextMenuHide=!0)},e.prototype.hide=function(t){var e=this;this.delayContextMenuHide=!1,window.setTimeout(function(){e.delayContextMenuHide||n(t).hide()},500)},e.prototype.hideAll=function(){this.hide("#contentMenu0"),this.hide("#contentMenu1")},e}())});
\ No newline at end of file