first step for manual merging of the slickgrid branch
[myslice.git] / third-party / slickgrid-2.1 / plugins / slick.headermenu.js
diff --git a/third-party/slickgrid-2.1/plugins/slick.headermenu.js b/third-party/slickgrid-2.1/plugins/slick.headermenu.js
new file mode 100644 (file)
index 0000000..ec8244d
--- /dev/null
@@ -0,0 +1,275 @@
+(function ($) {
+  // register namespace
+  $.extend(true, window, {
+    "Slick": {
+      "Plugins": {
+        "HeaderMenu": HeaderMenu
+      }
+    }
+  });
+
+
+  /***
+   * A plugin to add drop-down menus to column headers.
+   *
+   * USAGE:
+   *
+   * Add the plugin .js & .css files and register it with the grid.
+   *
+   * To specify a menu in a column header, extend the column definition like so:
+   *
+   *   var columns = [
+   *     {
+   *       id: 'myColumn',
+   *       name: 'My column',
+   *
+   *       // This is the relevant part
+   *       header: {
+   *          menu: {
+   *              items: [
+   *                {
+   *                  // menu item options
+   *                },
+   *                {
+   *                  // menu item options
+   *                }
+   *              ]
+   *          }
+   *       }
+   *     }
+   *   ];
+   *
+   *
+   * Available menu options:
+   *    tooltip:      Menu button tooltip.
+   *
+   *
+   * Available menu item options:
+   *    title:        Menu item text.
+   *    disabled:     Whether the item is disabled.
+   *    tooltip:      Item tooltip.
+   *    command:      A command identifier to be passed to the onCommand event handlers.
+   *    iconCssClass: A CSS class to be added to the menu item icon.
+   *    iconImage:    A url to the icon image.
+   *
+   *
+   * The plugin exposes the following events:
+   *    onBeforeMenuShow:   Fired before the menu is shown.  You can customize the menu or dismiss it by returning false.
+   *        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.Plugins.HeaderButtons
+   * @constructor
+   */
+  function HeaderMenu(options) {
+    var _grid;
+    var _self = this;
+    var _handler = new Slick.EventHandler();
+    var _defaults = {
+      buttonCssClass: null,
+      buttonImage: null
+    };
+    var $menu;
+    var $activeHeaderColumn;
+
+
+    function init(grid) {
+      options = $.extend(true, {}, _defaults, options);
+      _grid = grid;
+      _handler
+        .subscribe(_grid.onHeaderCellRendered, handleHeaderCellRendered)
+        .subscribe(_grid.onBeforeHeaderCellDestroy, handleBeforeHeaderCellDestroy);
+
+      // Force the grid to re-render the header now that the events are hooked up.
+      _grid.setColumns(_grid.getColumns());
+
+      // Hide the menu on outside click.
+      $(document.body).bind("mousedown", handleBodyMouseDown);
+    }
+
+
+    function destroy() {
+      _handler.unsubscribeAll();
+      $(document.body).unbind("mousedown", handleBodyMouseDown);
+    }
+
+
+    function handleBodyMouseDown(e) {
+      if ($menu && $menu[0] != e.target && !$.contains($menu[0], e.target)) {
+        hideMenu();
+      }
+    }
+
+
+    function hideMenu() {
+      if ($menu) {
+        $menu.remove();
+        $menu = null;
+        $activeHeaderColumn
+          .removeClass("slick-header-column-active");
+      }
+    }
+
+    function handleHeaderCellRendered(e, args) {
+      var column = args.column;
+      var menu = column.header && column.header.menu;
+
+      if (menu) {
+        var $el = $("<div></div>")
+          .addClass("slick-header-menubutton")
+          .data("column", column)
+          .data("menu", menu);
+
+        if (options.buttonCssClass) {
+          $el.addClass(options.buttonCssClass);
+        }
+
+        if (options.buttonImage) {
+          $el.css("background-image", "url(" + options.buttonImage + ")");
+        }
+
+        if (menu.tooltip) {
+          $el.attr("title", menu.tooltip);
+        }
+
+        $el
+          .bind("click", showMenu)
+          .appendTo(args.node);
+      }
+    }
+
+
+    function handleBeforeHeaderCellDestroy(e, args) {
+      var column = args.column;
+
+      if (column.header && column.header.menu) {
+        $(args.node).find(".slick-header-menubutton").remove();
+      }
+    }
+
+
+    function showMenu(e) {
+      var $menuButton = $(this);
+      var menu = $menuButton.data("menu");
+      var columnDef = $menuButton.data("column");
+
+      // Let the user modify the menu or cancel altogether,
+      // or provide alternative menu implementation.
+      if (_self.onBeforeMenuShow.notify({
+          "grid": _grid,
+          "column": columnDef,
+          "menu": menu
+        }, e, _self) == false) {
+        return;
+      }
+
+
+      if (!$menu) {
+        $menu = $("<div class='slick-header-menu'></div>")
+          .appendTo(_grid.getContainerNode());
+      }
+      $menu.empty();
+
+
+      // Construct the menu items.
+      for (var i = 0; i < menu.items.length; i++) {
+        var item = menu.items[i];
+
+        var $li = $("<div class='slick-header-menuitem'></div>")
+          .data("command", item.command || '')
+          .data("column", columnDef)
+          .data("item", item)
+          .bind("click", handleMenuItemClick)
+          .appendTo($menu);
+
+        if (item.disabled) {
+          $li.addClass("slick-header-menuitem-disabled");
+        }
+
+        if (item.tooltip) {
+          $li.attr("title", item.tooltip);
+        }
+
+        var $icon = $("<div class='slick-header-menuicon'></div>")
+          .appendTo($li);
+
+        if (item.iconCssClass) {
+          $icon.addClass(item.iconCssClass);
+        }
+
+        if (item.iconImage) {
+          $icon.css("background-image", "url(" + item.iconImage + ")");
+        }
+
+        $("<span class='slick-header-menucontent'></span>")
+          .text(item.title)
+          .appendTo($li);
+      }
+
+
+      // Position the menu.
+      $menu
+        .offset({ top: $(this).offset().top + $(this).height(), left: $(this).offset().left });
+
+
+      // Mark the header as active to keep the highlighting.
+      $activeHeaderColumn = $menuButton.closest(".slick-header-column");
+      $activeHeaderColumn
+        .addClass("slick-header-column-active");
+
+      // Stop propagation so that it doesn't register as a header click event.
+      e.preventDefault();
+      e.stopPropagation();
+    }
+
+
+    function handleMenuItemClick(e) {
+      var command = $(this).data("command");
+      var columnDef = $(this).data("column");
+      var item = $(this).data("item");
+
+      if (item.disabled) {
+        return;
+      }
+
+      hideMenu();
+
+      if (command != null && command != '') {
+        _self.onCommand.notify({
+            "grid": _grid,
+            "column": columnDef,
+            "command": command,
+            "item": item
+          }, e, _self);
+      }
+
+      // Stop propagation so that it doesn't register as a header click event.
+      e.preventDefault();
+      e.stopPropagation();
+    }
+
+    $.extend(this, {
+      "init": init,
+      "destroy": destroy,
+
+      "onBeforeMenuShow": new Slick.Event(),
+      "onCommand": new Slick.Event()
+    });
+  }
+})(jQuery);