slickgrid added to third-party
[myslice.git] / third-party / slickgrid-2.1 / plugins / slick.headermenu.js
1 (function ($) {
2   // register namespace
3   $.extend(true, window, {
4     "Slick": {
5       "Plugins": {
6         "HeaderMenu": HeaderMenu
7       }
8     }
9   });
10
11
12   /***
13    * A plugin to add drop-down menus to column headers.
14    *
15    * USAGE:
16    *
17    * Add the plugin .js & .css files and register it with the grid.
18    *
19    * To specify a menu in a column header, extend the column definition like so:
20    *
21    *   var columns = [
22    *     {
23    *       id: 'myColumn',
24    *       name: 'My column',
25    *
26    *       // This is the relevant part
27    *       header: {
28    *          menu: {
29    *              items: [
30    *                {
31    *                  // menu item options
32    *                },
33    *                {
34    *                  // menu item options
35    *                }
36    *              ]
37    *          }
38    *       }
39    *     }
40    *   ];
41    *
42    *
43    * Available menu options:
44    *    tooltip:      Menu button tooltip.
45    *
46    *
47    * Available menu item options:
48    *    title:        Menu item text.
49    *    disabled:     Whether the item is disabled.
50    *    tooltip:      Item tooltip.
51    *    command:      A command identifier to be passed to the onCommand event handlers.
52    *    iconCssClass: A CSS class to be added to the menu item icon.
53    *    iconImage:    A url to the icon image.
54    *
55    *
56    * The plugin exposes the following events:
57    *    onBeforeMenuShow:   Fired before the menu is shown.  You can customize the menu or dismiss it by returning false.
58    *        Event args:
59    *            grid:     Reference to the grid.
60    *            column:   Column definition.
61    *            menu:     Menu options.  Note that you can change the menu items here.
62    *
63    *    onCommand:    Fired on menu item click for buttons with 'command' specified.
64    *        Event args:
65    *            grid:     Reference to the grid.
66    *            column:   Column definition.
67    *            command:  Button command identified.
68    *            button:   Button options.  Note that you can change the button options in your
69    *                      event handler, and the column header will be automatically updated to
70    *                      reflect them.  This is useful if you want to implement something like a
71    *                      toggle button.
72    *
73    *
74    * @param options {Object} Options:
75    *    buttonCssClass:   an extra CSS class to add to the menu button
76    *    buttonImage:      a url to the menu button image (default '../images/down.gif')
77    * @class Slick.Plugins.HeaderButtons
78    * @constructor
79    */
80   function HeaderMenu(options) {
81     var _grid;
82     var _self = this;
83     var _handler = new Slick.EventHandler();
84     var _defaults = {
85       buttonCssClass: null,
86       buttonImage: null
87     };
88     var $menu;
89     var $activeHeaderColumn;
90
91
92     function init(grid) {
93       options = $.extend(true, {}, _defaults, options);
94       _grid = grid;
95       _handler
96         .subscribe(_grid.onHeaderCellRendered, handleHeaderCellRendered)
97         .subscribe(_grid.onBeforeHeaderCellDestroy, handleBeforeHeaderCellDestroy);
98
99       // Force the grid to re-render the header now that the events are hooked up.
100       _grid.setColumns(_grid.getColumns());
101
102       // Hide the menu on outside click.
103       $(document.body).bind("mousedown", handleBodyMouseDown);
104     }
105
106
107     function destroy() {
108       _handler.unsubscribeAll();
109       $(document.body).unbind("mousedown", handleBodyMouseDown);
110     }
111
112
113     function handleBodyMouseDown(e) {
114       if ($menu && $menu[0] != e.target && !$.contains($menu[0], e.target)) {
115         hideMenu();
116       }
117     }
118
119
120     function hideMenu() {
121       if ($menu) {
122         $menu.remove();
123         $menu = null;
124         $activeHeaderColumn
125           .removeClass("slick-header-column-active");
126       }
127     }
128
129     function handleHeaderCellRendered(e, args) {
130       var column = args.column;
131       var menu = column.header && column.header.menu;
132
133       if (menu) {
134         var $el = $("<div></div>")
135           .addClass("slick-header-menubutton")
136           .data("column", column)
137           .data("menu", menu);
138
139         if (options.buttonCssClass) {
140           $el.addClass(options.buttonCssClass);
141         }
142
143         if (options.buttonImage) {
144           $el.css("background-image", "url(" + options.buttonImage + ")");
145         }
146
147         if (menu.tooltip) {
148           $el.attr("title", menu.tooltip);
149         }
150
151         $el
152           .bind("click", showMenu)
153           .appendTo(args.node);
154       }
155     }
156
157
158     function handleBeforeHeaderCellDestroy(e, args) {
159       var column = args.column;
160
161       if (column.header && column.header.menu) {
162         $(args.node).find(".slick-header-menubutton").remove();
163       }
164     }
165
166
167     function showMenu(e) {
168       var $menuButton = $(this);
169       var menu = $menuButton.data("menu");
170       var columnDef = $menuButton.data("column");
171
172       // Let the user modify the menu or cancel altogether,
173       // or provide alternative menu implementation.
174       if (_self.onBeforeMenuShow.notify({
175           "grid": _grid,
176           "column": columnDef,
177           "menu": menu
178         }, e, _self) == false) {
179         return;
180       }
181
182
183       if (!$menu) {
184         $menu = $("<div class='slick-header-menu'></div>")
185           .appendTo(_grid.getContainerNode());
186       }
187       $menu.empty();
188
189
190       // Construct the menu items.
191       for (var i = 0; i < menu.items.length; i++) {
192         var item = menu.items[i];
193
194         var $li = $("<div class='slick-header-menuitem'></div>")
195           .data("command", item.command || '')
196           .data("column", columnDef)
197           .data("item", item)
198           .bind("click", handleMenuItemClick)
199           .appendTo($menu);
200
201         if (item.disabled) {
202           $li.addClass("slick-header-menuitem-disabled");
203         }
204
205         if (item.tooltip) {
206           $li.attr("title", item.tooltip);
207         }
208
209         var $icon = $("<div class='slick-header-menuicon'></div>")
210           .appendTo($li);
211
212         if (item.iconCssClass) {
213           $icon.addClass(item.iconCssClass);
214         }
215
216         if (item.iconImage) {
217           $icon.css("background-image", "url(" + item.iconImage + ")");
218         }
219
220         $("<span class='slick-header-menucontent'></span>")
221           .text(item.title)
222           .appendTo($li);
223       }
224
225
226       // Position the menu.
227       $menu
228         .offset({ top: $(this).offset().top + $(this).height(), left: $(this).offset().left });
229
230
231       // Mark the header as active to keep the highlighting.
232       $activeHeaderColumn = $menuButton.closest(".slick-header-column");
233       $activeHeaderColumn
234         .addClass("slick-header-column-active");
235
236       // Stop propagation so that it doesn't register as a header click event.
237       e.preventDefault();
238       e.stopPropagation();
239     }
240
241
242     function handleMenuItemClick(e) {
243       var command = $(this).data("command");
244       var columnDef = $(this).data("column");
245       var item = $(this).data("item");
246
247       if (item.disabled) {
248         return;
249       }
250
251       hideMenu();
252
253       if (command != null && command != '') {
254         _self.onCommand.notify({
255             "grid": _grid,
256             "column": columnDef,
257             "command": command,
258             "item": item
259           }, e, _self);
260       }
261
262       // Stop propagation so that it doesn't register as a header click event.
263       e.preventDefault();
264       e.stopPropagation();
265     }
266
267     $.extend(this, {
268       "init": init,
269       "destroy": destroy,
270
271       "onBeforeMenuShow": new Slick.Event(),
272       "onCommand": new Slick.Event()
273     });
274   }
275 })(jQuery);