text: /***
   * A control to add a Grid Menu (hambuger menu on top-right of the grid)
   *
   * USAGE:
   *
   * Add the slick.gridmenu.(js|css) files and register it with the grid.
   *
   * To specify a menu in a column header, extend the column definition like so:
   * var gridMenuControl = new Slick.Controls.GridMenu(columns, grid, options);
   *
   * Available grid options, by defining a gridMenu object:
   *
   *  var options = {
   *    enableCellNavigation: true,
   *    gridMenu: {
   *      customTitle: "Custom Menus",                // default to empty string
   *      columnTitle: "Columns",                     // default to empty string
   *      iconImage: "../images/drag-handle.png",     // this is the Grid Menu icon (hamburger icon)
   *      iconCssClass: "fa fa-bars",                 // you can provide iconImage OR iconCssClass
   *      leaveOpen: false,                           // do we want to leave the Grid Menu open after a command execution? (false by default)
   *      menuWidth: 18,                              // width (icon) that will be use to resize the column header container (18 by default)
   *      contentMinWidth: 0,							            // defaults to 0 (auto), minimum width of grid menu content (command, column list)
   *      marginBottom: 15,                           // defaults to 15, margin to use at the bottom of the grid when using max-height (default)
   *      resizeOnShowHeaderRow: false,               // false by default
   *      showButton: true,                           // true by default - it allows the user to control if the
   *                                                          // default gridMenu button (located on the top right corner by default CSS)
   *                                                          // should be created or omitted
   *      useClickToRepositionMenu: true,             // true by default
   *
   *      // the last 2 checkboxes titles
   *      hideForceFitButton: false,                  // show/hide checkbox near the end "Force Fit Columns"
   *      hideSyncResizeButton: false,                // show/hide checkbox near the end "Synchronous Resize"
   *      forceFitTitle: "Force fit columns",         // default to "Force fit columns"
   *      syncResizeTitle: "Synchronous resize",      // default to "Synchronous resize"
   *
   *      customItems: [
   *        {
   *          // custom menu item options
   *        },
   *        {
   *          // custom menu item options
   *        }
   *      ]
   *    }
   *  };
   *
   *
   * Available menu options:
   *     hideForceFitButton:        Hide the "Force fit columns" button (defaults to false)
   *     hideSyncResizeButton:      Hide the "Synchronous resize" button (defaults to false)
   *     forceFitTitle:             Text of the title "Force fit columns"
   *     contentMinWidth:						minimum width of grid menu content (command, column list), defaults to 0 (auto)
   *     height:                    Height of the Grid Menu content, when provided it will be used instead of the max-height (defaults to undefined)
   *     menuWidth:                 Grid menu button width (defaults to 18)
   *     resizeOnShowHeaderRow:     Do we want to resize on the show header row event
   *     syncResizeTitle:           Text of the title "Synchronous resize"
   *     useClickToRepositionMenu:  Use the Click offset to reposition the Grid Menu (defaults to true), when set to False it will use the icon offset to reposition the grid menu
   *     menuUsabilityOverride:     Callback method that user can override the default behavior of enabling/disabling the menu from being usable (must be combined with a custom formatter)
   *     marginBottom:              Margin to use at the bottom of the grid menu, only in effect when height is undefined (defaults to 15)
   *
   * Available custom menu item options:
   *    action:                     Optionally define a callback function that gets executed when item is chosen (and/or use the onCommand event)
   *    title:                      Menu item text.
   *    divider:                    Whether the current item is a divider, not an actual command.
   *    disabled:                   Whether the item/command is disabled.
   *    hidden:                     Whether the item/command is hidden.
   *    tooltip:                    Item tooltip.
   *    command:                    A command identifier to be passed to the onCommand event handlers.
   *    cssClass:                   A CSS class to be added to the menu item container.
   *    iconCssClass:               A CSS class to be added to the menu item icon.
   *    iconImage:                  A url to the icon image.
   *    textCssClass:               A CSS class to be added to the menu item text.
   *    itemVisibilityOverride:     Callback method that user can override the default behavior of showing/hiding an item from the list
   *    itemUsabilityOverride:      Callback method that user can override the default behavior of enabling/disabling an item from the list
   *
   *
   * The plugin exposes the following events:
   *
   *    onAfterMenuShow:   Fired after the menu is shown.  You can customize the menu or dismiss it by returning false.
   *      * ONLY works with a jQuery event (as per slick.core code), so we cannot notify when it's a button event (when grid menu is attached to an external button, not the hamburger menu)
   *        Event args:
   *            grid:     Reference to the grid.
   *            column:   Column definition.
   *            menu:     Menu options.  Note that you can change the menu items here.
   *
   *    onBeforeMenuShow:   Fired before the menu is shown.  You can customize the menu or dismiss it by returning false.
   *      * ONLY works with a jQuery event (as per slick.core code), so we cannot notify when it's a button event (when grid menu is attached to an external button, not the hamburger menu)
   *        Event args:
   *            grid:     Reference to the grid.
   *            column:   Column definition.
   *            menu:     Menu options.  Note that you can change the menu items here.
   *
   *    onMenuClose:      Fired when the menu is closing.
   *        Event args:
   *            grid:     Reference to the grid.
   *            column:   Column definition.
   *            menu:     Menu options.  Note that you can change the menu items here.
   *
   *    onCommand:    Fired on menu item click for buttons with 'command' specified.
   *        Event args:
   *            grid:     Reference to the grid.
   *            column:   Column definition.
   *            command:  Button command identified.
   *            button:   Button options.  Note that you can change the button options in your
   *                      event handler, and the column header will be automatically updated to
   *                      reflect them.  This is useful if you want to implement something like a
   *                      toggle button.
   *
   *
   * @param options {Object} Options:
   *    buttonCssClass:   an extra CSS class to add to the menu button
   *    buttonImage:      a url to the menu button image (default '../images/down.gif')
   * @class Slick.Controls.GridMenu
   * @constructor
   */
  
    var $ = require("../slick.jquery");
    var Slick = require("../slick.core");
  
    function SlickGridMenu(columns, grid, options) {
      var _grid = grid;
      var _gridOptions;
      var _gridUid = (grid && grid.getUID) ? grid.getUID() : '';
      var _isMenuOpen = false;
      var _options = options;
      var _self = this;
      var $customTitleElm;
      var $columnTitleElm;
      var $customMenu;
      var $header;
      var $list;
      var $button;
      var $menu;
      var columnCheckboxes;
      var _defaults = {
        showButton: true,
        hideForceFitButton: false,
        hideSyncResizeButton: false,
        forceFitTitle: "Force fit columns",
        marginBottom: 15,
        menuWidth: 18,
        contentMinWidth: 0,
        resizeOnShowHeaderRow: false,
        syncResizeTitle: "Synchronous resize",
        useClickToRepositionMenu: true,
        headerColumnValueExtractor: function (columnDef) {
          return columnDef.name;
        }
      };
  
      // when a grid optionally changes from a regular grid to a frozen grid, we need to destroy & recreate the grid menu
      // we do this change because the Grid Menu is on the left container for a regular grid, it is however on the right container for a frozen grid
      grid.onSetOptions.subscribe(function (e, args) {
        if (args && args.optionsBefore && args.optionsAfter) {
          var switchedFromRegularToFrozen = args.optionsBefore.frozenColumn >= 0 && args.optionsAfter.frozenColumn === -1;
          var switchedFromFrozenToRegular = args.optionsBefore.frozenColumn === -1 && args.optionsAfter.frozenColumn >= 0;
          if (switchedFromRegularToFrozen || switchedFromFrozenToRegular) {
            recreateGridMenu();
          }
        }
      });
  
      function init(grid) {
        _gridOptions = grid.getOptions();
        createGridMenu();
  
        // subscribe to the grid, when it's destroyed, we should also destroy the Grid Menu
        grid.onBeforeDestroy.subscribe(destroy);
      }
  
      function setOptions(newOptions) {
        options = $.extend({}, options, newOptions);
      }
  
      function createGridMenu() {
        var gridMenuWidth = (_options.gridMenu && _options.gridMenu.menuWidth) || _defaults.menuWidth;
        if (_gridOptions && _gridOptions.hasOwnProperty('frozenColumn') && _gridOptions.frozenColumn >= 0) {
          $header = $('.' + _gridUid + ' .slick-header-right');
        } else {
          $header = $('.' + _gridUid + ' .slick-header-left');
        }
        $header.attr('style', 'width: calc(100% - ' + gridMenuWidth + 'px)');
  
        // if header row is enabled, we need to resize it's width also
        var enableResizeHeaderRow = (_options.gridMenu && _options.gridMenu.resizeOnShowHeaderRow != undefined) ? _options.gridMenu.resizeOnShowHeaderRow : _defaults.resizeOnShowHeaderRow;
        if (enableResizeHeaderRow && _options.showHeaderRow) {
          $('.' + _gridUid + '.slick-headerrow').attr('style', 'width: calc(100% - ' + gridMenuWidth + 'px)');
        }
  
        var showButton = (_options.gridMenu && _options.gridMenu.showButton !== undefined) ? _options.gridMenu.showButton : _defaults.showButton;
        if (showButton) {
          $button = $('<button class="slick-gridmenu-button"/>');
          if (_options.gridMenu && _options.gridMenu.iconCssClass) {
            $button.addClass(_options.gridMenu.iconCssClass);
          } else {
            var iconImage = (_options.gridMenu && _options.gridMenu.iconImage) ? _options.gridMenu.iconImage : "../images/drag-handle.png";
            $('<img src="' + iconImage + '"/>').appendTo($button);
          }
          $button.insertBefore($header);
          // add on click handler for the Grid Menu itself
          $button.on("click." + _gridUid, showGridMenu);
        }
  
        $menu = $('<div class="slick-gridmenu ' + _gridUid + '" style="display: none" />').appendTo(document.body);
        $('<button type="button" class="close" data-dismiss="slick-gridmenu" aria-label="Close"><span class="close" aria-hidden="true">&times;</span></button>').appendTo($menu);
  
        $customMenu = $('<div class="slick-gridmenu-custom" />');
        $customMenu.appendTo($menu);
  
        populateCustomMenus(_options, $customMenu);
        populateColumnPicker();
  
        // Hide the menu on outside click.
        $(document.body).on("mousedown." + _gridUid, handleBodyMouseDown);
  
        // destroy the picker if user leaves the page
        $(window).on("beforeunload", destroy);
  
      }
  
      /** Destroy the plugin by unsubscribing every events & also delete the menu DOM elements */
      function destroy() {
        _self.onAfterMenuShow.unsubscribe();
        _self.onBeforeMenuShow.unsubscribe();
        _self.onMenuClose.unsubscribe();
        _self.onCommand.unsubscribe();
        _self.onColumnsChanged.unsubscribe();
        _grid.onColumnsReordered.unsubscribe(updateColumnOrder);
        _grid.onBeforeDestroy.unsubscribe();
        _grid.onSetOptions.unsubscribe();
        $("div.slick-gridmenu." + _gridUid).remove();
        deleteMenu();
        $(window).off("beforeunload");
      }
  
      /** Delete the menu DOM element but without unsubscribing any events */
      function deleteMenu() {
        $(document.body).off("mousedown." + _gridUid, handleBodyMouseDown);
        $("div.slick-gridmenu." + _gridUid).hide();
        if ($button) {
          $button.remove();
        }
        if ($menu) {
          $menu.remove();
        }
        if ($customMenu) {
          $customMenu.remove();
        }
        if ($header) {
          $header.attr('style', 'width: 100%'); // put back original width
        }
        $customTitleElm = null;
        $columnTitleElm = null;
        $customMenu = null;
        $header = null;
        $list = null;
        $button = null;
        $menu = null;
      }
  
      function populateCustomMenus(options, $customMenu) {
        // Construct the custom menu items.
        if (!options.gridMenu || !options.gridMenu.customItems) {
          return;
        }
  
        // user could pass a title on top of the custom section
        if (_options.gridMenu && _options.gridMenu.customTitle) {
          $customTitleElm = $('<div class="title"/>').append(_options.gridMenu.customTitle);
          $customTitleElm.appendTo($customMenu);
        }
  
        for (var i = 0, ln = options.gridMenu.customItems.length; i < ln; i++) {
          var item = options.gridMenu.customItems[i];
          var callbackArgs = {
            "grid": _grid,
            "menu": $menu,
            "columns": columns,
            "visibleColumns": getVisibleColumns()
          };
  
          // run each override functions to know if the item is visible and usable
          var isItemVisible = runOverrideFunctionWhenExists(item.itemVisibilityOverride, callbackArgs);
          var isItemUsable = runOverrideFunctionWhenExists(item.itemUsabilityOverride, callbackArgs);
  
          // if the result is not visible then there's no need to go further
          if (!isItemVisible) {
            continue;
          }
  
          // when the override is defined, we need to use its result to update the disabled property
          // so that "handleMenuItemCommandClick" has the correct flag and won't trigger a command clicked event
          if (Object.prototype.hasOwnProperty.call(item, "itemUsabilityOverride")) {
            item.disabled = isItemUsable ? false : true;
          }
  
          var $li = $("<div class='slick-gridmenu-item'></div>")
            .data("command", item.command !== undefined ? item.command : "")
            .data("item", item)
            .on("click", handleMenuItemClick)
            .appendTo($customMenu);
  
          if (item.divider || item === "divider") {
            $li.addClass("slick-gridmenu-item-divider");
            continue;
          }
          if (item.disabled) {
            $li.addClass("slick-gridmenu-item-disabled");
          }
  
          if (item.hidden) {
            $li.addClass("slick-gridmenu-item-hidden");
          }
  
          if (item.cssClass) {
            $li.addClass(item.cssClass);
          }
  
          if (item.tooltip) {
            $li.attr("title", item.tooltip);
          }
  
          var $icon = $("<div class='slick-gridmenu-icon'></div>")
            .appendTo($li);
  
          if (item.iconCssClass) {
            $icon.addClass(item.iconCssClass);
          }
  
          if (item.iconImage) {
            $icon.css("background-image", "url(" + item.iconImage + ")");
          }
  
          var $text = $("<span class='slick-gridmenu-content'></span>")
            .text(item.title)
            .appendTo($li);
  
          if (item.textCssClass) {
            $text.addClass(item.textCssClass);
          }
          $icon = null;
          $li = null;
          $text = null;
        }
      }
  
      /** Build the column picker, the code comes almost untouched from the file "slick.columnpicker.js" */
      function populateColumnPicker() {
        _grid.onColumnsReordered.subscribe(updateColumnOrder);
        _options = $.extend({}, _defaults, _options);
  
        // user could pass a title on top of the columns list
        if (_options.gridMenu && _options.gridMenu.columnTitle) {
          $columnTitleElm = $('<div class="title"/>').append(_options.gridMenu.columnTitle);
          $columnTitleElm.appendTo($menu);
        }
  
        $menu.on("click", updateColumn);
        $list = $('<span class="slick-gridmenu-list" />');
      }
  
      /** Delete and then Recreate the Grid Menu (for example when we switch from regular to a frozen grid) */
      function recreateGridMenu() {
        deleteMenu();
        init(_grid);
      }
  
      function showGridMenu(e) {
        e.preventDefault();
  
        // empty both the picker list & the command list
        $list.empty();
        $customMenu.empty();
  
        populateCustomMenus(_options, $customMenu);
        updateColumnOrder();
        columnCheckboxes = [];
  
        var callbackArgs = {
          "grid": _grid,
          "menu": $menu,
          "allColumns": columns,
          "visibleColumns": getVisibleColumns()
        };
  
        // run the override function (when defined), if the result is false it won't go further
        if (_options && _options.gridMenu && !runOverrideFunctionWhenExists(_options.gridMenu.menuUsabilityOverride, callbackArgs)) {
          return;
        }
  
        // notify of the onBeforeMenuShow only works when it's a jQuery event (as per slick.core code)
        // this mean that we cannot notify when the grid menu is attach to a button event
        if (typeof e.isPropagationStopped === "function") {
          if (_self.onBeforeMenuShow.notify(callbackArgs, e, _self) == false) {
            return;
          }
        }
  
        var $li, $input, columnId, columnLabel, excludeCssClass;
        for (var i = 0; i < columns.length; i++) {
          columnId = columns[i].id;
          excludeCssClass = columns[i].excludeFromGridMenu ? "hidden" : "";
          $li = $('<li class="' + excludeCssClass + '" />').appendTo($list);
  
          $input = $("<input type='checkbox' id='" + _gridUid + "-gridmenu-colpicker-" + columnId + "' />").data("column-id", columns[i].id).appendTo($li);
          columnCheckboxes.push($input);
  
          if (_grid.getColumnIndex(columns[i].id) != null) {
            $input.attr("checked", "checked");
          }
  
          // get the column label from the picker value extractor (user can optionally provide a custom extractor)
          if (_options && _options.gridMenu && _options.gridMenu.headerColumnValueExtractor) {
            columnLabel = _options.gridMenu.headerColumnValueExtractor(columns[i], _gridOptions);
          } else {
            columnLabel = _defaults.headerColumnValueExtractor(columns[i], _gridOptions);
          }
  
          $("<label for='" + _gridUid + "-gridmenu-colpicker-" + columnId + "' />")
            .html(columnLabel)
            .appendTo($li);
        }
  
        if (_options.gridMenu && (!_options.gridMenu.hideForceFitButton || !_options.gridMenu.hideSyncResizeButton)) {
          $("<hr/>").appendTo($list);
        }
  
        if (!(_options.gridMenu && _options.gridMenu.hideForceFitButton)) {
          var forceFitTitle = (_options.gridMenu && _options.gridMenu.forceFitTitle) || _defaults.forceFitTitle;
          $li = $("<li />").appendTo($list);
          $input = $("<input type='checkbox' id='" + _gridUid + "-gridmenu-colpicker-forcefit' />").data("option", "autoresize").appendTo($li);
          $("<label for='" + _gridUid + "-gridmenu-colpicker-forcefit' />").text(forceFitTitle).appendTo($li);
  
          if (_grid.getOptions().forceFitColumns) {
            $input.attr("checked", "checked");
          }
        }
  
        if (!(_options.gridMenu && _options.gridMenu.hideSyncResizeButton)) {
          var syncResizeTitle = (_options.gridMenu && _options.gridMenu.syncResizeTitle) || _defaults.syncResizeTitle;
          $li = $("<li />").appendTo($list);
          $input = $("<input type='checkbox' id='" + _gridUid + "-gridmenu-colpicker-syncresize' />").data("option", "syncresize").appendTo($li);
          $("<label for='" + _gridUid + "-gridmenu-colpicker-syncresize' />").text(syncResizeTitle).appendTo($li);
  
          if (_grid.getOptions().syncColumnCellResize) {
            $input.attr("checked", "checked");
          }
        }
  
        var menuIconOffset = $(e.target).prop('nodeName') == "button" ? $(e.target).offset() : $(e.target).parent("button").offset(); // get button offset position
        if (!menuIconOffset) {
          menuIconOffset = $(e.target).offset(); // external grid menu might fall in this last case
        }
        var menuWidth = $menu.width();
        var useClickToRepositionMenu = (_options.gridMenu && _options.gridMenu.useClickToRepositionMenu !== undefined) ? _options.gridMenu.useClickToRepositionMenu : _defaults.useClickToRepositionMenu;
        var gridMenuIconWidth = (_options.gridMenu && _options.gridMenu.menuWidth) || _defaults.menuWidth;
        var contentMinWidth = (_options.gridMenu && _options.gridMenu.contentMinWidth) ? _options.gridMenu.contentMinWidth : _defaults.contentMinWidth;
        var currentMenuWidth = (contentMinWidth > menuWidth) ? contentMinWidth : (menuWidth + gridMenuIconWidth);
        var nextPositionTop = (useClickToRepositionMenu && e.pageY > 0) ? e.pageY : menuIconOffset.top + 10;
        var nextPositionLeft = (useClickToRepositionMenu && e.pageX > 0) ? e.pageX : menuIconOffset.left + 10;
        var menuMarginBottom = (_options.gridMenu && _options.gridMenu.marginBottom !== undefined) ? _options.gridMenu.marginBottom : _defaults.marginBottom;
  
        $menu
          .css("top", nextPositionTop + 10)
          .css("left", nextPositionLeft - currentMenuWidth + 10);
  
        if (contentMinWidth > 0) {
          $menu.css("min-width", contentMinWidth);
        }
  
        // set "height" when defined OR ELSE use the "max-height" with available window size and optional margin bottom
        if (_options.gridMenu && _options.gridMenu.height !== undefined) {
          $menu.css("height", _options.gridMenu.height);
        } else {
          $menu.css("max-height", $(window).height() - e.clientY - menuMarginBottom);
        }
  
        $menu.show();
        $list.appendTo($menu);
        _isMenuOpen = true;
  
        if (typeof e.isPropagationStopped === "function") {
          if (_self.onAfterMenuShow.notify(callbackArgs, e, _self) == false) {
            return;
          }
        }
        $input = null;
      }
  
      function handleBodyMouseDown(e) {
        if (($menu && $menu[0] != e.target && !$.contains($menu[0], e.target) && _isMenuOpen) || e.target.className == "close") {
          hideMenu(e);
        }
      }
  
      function handleMenuItemClick(e) {
        var command = $(this).data("command");
        var item = $(this).data("item");
  
        if (item.disabled || item.divider || item === "divider") {
          return;
        }
  
        if (command != null && command != '') {
          var callbackArgs = {
            "grid": _grid,
            "command": command,
            "item": item,
            "allColumns": columns,
            "visibleColumns": getVisibleColumns()
          };
          _self.onCommand.notify(callbackArgs, e, _self);
  
          // execute action callback when defined
          if (typeof item.action === "function") {
            item.action.call(this, e, callbackArgs);
          }
        }
  
        // does the user want to leave open the Grid Menu after executing a command?
        var leaveOpen = (_options.gridMenu && _options.gridMenu.leaveOpen) ? true : false;
        if (!leaveOpen && !e.isDefaultPrevented()) {
          hideMenu(e);
        }
  
        // Stop propagation so that it doesn't register as a header click event.
        e.preventDefault();
        e.stopPropagation();
      }
  
      function hideMenu(e) {
        if ($menu) {
          $menu.hide();
          _isMenuOpen = false;
  
          var callbackArgs = {
            "grid": _grid,
            "menu": $menu,
            "allColumns": columns,
            "visibleColumns": getVisibleColumns()
          };
          if (_self.onMenuClose.notify(callbackArgs, e, _self) == false) {
            return;
          }
        }
      }
  
      /** Update the Titles of each sections (command, customTitle, ...) */
      function updateAllTitles(gridMenuOptions) {
        if ($customTitleElm && $customTitleElm.text) {
          $customTitleElm.text(gridMenuOptions.customTitle);
        }
        if ($columnTitleElm && $columnTitleElm.text) {
          $columnTitleElm.text(gridMenuOptions.columnTitle);
        }
      }
  
      function updateColumnOrder() {
        // Because columns can be reordered, we have to update the `columns`
        // to reflect the new order, however we can't just take `grid.getColumns()`,
        // as it does not include columns currently hidden by the picker.
        // We create a new `columns` structure by leaving currently-hidden
        // columns in their original ordinal position and interleaving the results
        // of the current column sort.
        var current = _grid.getColumns().slice(0);
        var ordered = new Array(columns.length);
        for (var i = 0; i < ordered.length; i++) {
          if (_grid.getColumnIndex(columns[i].id) === undefined) {
            // If the column doesn't return a value from getColumnIndex,
            // it is hidden. Leave it in this position.
            ordered[i] = columns[i];
          } else {
            // Otherwise, grab the next visible column.
            ordered[i] = current.shift();
          }
        }
        columns = ordered;
      }
  
      function updateColumn(e) {
        if ($(e.target).data("option") == "autoresize") {
          // when calling setOptions, it will resize with ALL Columns (even the hidden ones)
          // we can avoid this problem by keeping a reference to the visibleColumns before setOptions and then setColumns after
          var previousVisibleColumns = getVisibleColumns();
          var isChecked = e.target.checked;
          _grid.setOptions({ forceFitColumns: isChecked });
          _grid.setColumns(previousVisibleColumns);
          return;
        }
  
        if ($(e.target).data("option") == "syncresize") {
          if (e.target.checked) {
            _grid.setOptions({ syncColumnCellResize: true });
          } else {
            _grid.setOptions({ syncColumnCellResize: false });
          }
          return;
        }
  
        if ($(e.target).is(":checkbox")) {
          var isChecked = e.target.checked;
          var columnId = $(e.target).data("column-id") || "";
          var visibleColumns = [];
          $.each(columnCheckboxes, function (i) {
            if ($(this).is(":checked")) {
              visibleColumns.push(columns[i]);
            }
          });
  
          if (!visibleColumns.length) {
            $(e.target).attr("checked", "checked");
            return;
          }
  
          var callbackArgs = {
            "columnId": columnId,
            "showing": isChecked,
            "grid": _grid,
            "allColumns": columns,
            "columns": visibleColumns
          };
          _grid.setColumns(visibleColumns);
          _self.onColumnsChanged.notify(callbackArgs, e, _self);
        }
      }
  
      init(_grid);
  
      function getAllColumns() {
        return columns;
      }
  
      /** visible columns, we can simply get them directly from the grid */
      function getVisibleColumns() {
        return _grid.getColumns();
      }
  
      /**
       * Method that user can pass to override the default behavior.
       * In order word, user can choose or an item is (usable/visible/enable) by providing his own logic.
       * @param overrideFn: override function callback
       * @param args: multiple arguments provided to the override (cell, row, columnDef, dataContext, grid)
       */
      function runOverrideFunctionWhenExists(overrideFn, args) {
        if (typeof overrideFn === 'function') {
          return overrideFn.call(this, args);
        }
        return true;
      }
  
      $.extend(this, {
        "init": init,
        "getAllColumns": getAllColumns,
        "getVisibleColumns": getVisibleColumns,
        "destroy": destroy,
        "deleteMenu": deleteMenu,
        "recreateGridMenu": recreateGridMenu,
        "showGridMenu": showGridMenu,
        "setOptions": setOptions,
        "updateAllTitles": updateAllTitles,
  
        "onAfterMenuShow": new Slick.Event(),
        "onBeforeMenuShow": new Slick.Event(),
        "onMenuClose": new Slick.Event(),
        "onCommand": new Slick.Event(),
        "onColumnsChanged": new Slick.Event()
      });
    }
  
    module.exports = {
      "GridMenu": SlickGridMenu
    };
  