1 (function($, rf) {
  2 
  3     rf.ui = rf.ui || {};
  4 
  5     var defaultOptions = {
  6         mode : 'server',
  7         attachToBody : false,
  8         showDelay : 50,
  9         hideDelay : 300,
 10         verticalOffset : 0,
 11         horizontalOffset : 0,
 12         showEvent : 'mouseover',
 13         positionOffset : [0, 0],
 14         cssRoot : "ddm",
 15         cssClasses : {}
 16     };
 17 
 18     rf.ui.MenuBase = function(componentId, options) {
 19         $super.constructor.call(this, componentId, options);
 20         this.id = componentId;
 21         this.namespace = this.namespace || "." + rf.Event.createNamespace(this.name, this.id);
 22 
 23         this.options = {};
 24         $.extend(this.options, defaultOptions, options || {});
 25         $.extend(this.options.cssClasses, buildCssClasses.call(this, this.options.cssRoot));
 26 
 27         this.attachToDom(componentId);
 28 
 29         this.element = rf.getDomElement(this.id);
 30 
 31         this.displayed = false;
 32 
 33         this.options.positionOffset = [this.options.horizontalOffset, this.options.verticalOffset];
 34         this.popup = new RichFaces.ui.Popup(this.id + "_list", {
 35                 attachTo : this.id,
 36                 direction : this.options.direction,
 37                 jointPoint : this.options.jointPoint,
 38                 positionType : this.options.positionType,
 39                 positionOffset : this.options.positionOffset,
 40                 attachToBody : this.options.attachToBody
 41             });
 42 
 43         this.selectedGroup = null;
 44 
 45         rf.Event.bindById(this.id, "mouseenter", $.proxy(this.__overHandler, this), this);
 46         rf.Event.bindById(this.id, "mouseleave", $.proxy(this.__leaveHandler, this), this);
 47 
 48         this.popupElement = rf.getDomElement(this.popup.id);
 49         this.popupElement.tabIndex = -1;
 50 
 51         this.__updateItemsList();
 52 
 53         rf.Event.bind(this.items, "mouseenter", $.proxy(this.__itemMouseEnterHandler, this), this);
 54 
 55         this.currentSelectedItemIndex = -1;
 56         var navEventHandlers;
 57         navEventHandlers = {};
 58         navEventHandlers["keydown" + this.namespace] = this.__keydownHandler;
 59 
 60         rf.Event.bind(this.popupElement, navEventHandlers, this);
 61     };
 62 
 63     var buildCssClasses = function(cssRoot) {
 64         var cssClasses = {
 65             itemCss : "rf-" +cssRoot+ "-itm",
 66             selectItemCss : "rf-" +cssRoot+ "-itm-sel",
 67             unselectItemCss : "rf-" +cssRoot+ "-itm-unsel",
 68             disabledItemCss : "rf-" +cssRoot+ "-itm-dis",
 69             labelCss: "rf-" +cssRoot+ "-lbl",
 70             listCss : "rf-" +cssRoot+ "-lst",
 71             listContainerCss : "rf-" +cssRoot+ "-lst-bg"
 72         }
 73         return cssClasses;
 74     }
 75 
 76     rf.BaseComponent.extend(rf.ui.MenuBase);
 77 
 78     // define super class link
 79     var $super = rf.ui.MenuBase.$super;
 80 
 81     $.extend(rf.ui.MenuBase.prototype, (function() {
 82         return {
 83             name : "MenuBase",
 84 
 85             show : function() {
 86                 this.__showPopup();
 87             },
 88 
 89             hide : function() {
 90                 this.__hidePopup();
 91             },
 92 
 93             processItem : function(item) {
 94                 if (item && item.attr('id') && !this.__isDisabled(item) && !this.__isGroup(item)) {
 95                     this.invokeEvent("itemclick", rf.getDomElement(this.id), null);
 96                     this.hide();
 97                 }
 98             },
 99 
100             activateItem : function(menuItemId) {
101                 var item = $(RichFaces.getDomElement(menuItemId));
102                 rf.Event.fireById(item.attr('id'), 'click');
103             },
104 
105             __showPopup : function(e) {
106                 if (!this.__isShown()) {
107                     this.invokeEvent("show", rf.getDomElement(this.id), null);
108                     this.popup.show(e);
109                     this.displayed = true;
110                     rf.ui.MenuManager.setActiveSubMenu(rf.component(this.element));
111                 }
112                 this.popupElement.focus();
113             },
114 
115             __hidePopup : function() {
116                 window.clearTimeout(this.showTimeoutId);
117                 this.showTimeoutId = null;
118                 if (this.__isShown()) {
119                     this.invokeEvent("hide", rf.getDomElement(this.id), null);
120                     this.__closeChildGroups();
121                     this.popup.hide();
122                     this.displayed = false;
123                     this.__deselectCurrentItem();
124                     this.currentSelectedItemIndex = -1;
125                     var parentMenu = rf.component(this.__getParentMenu());
126                     if (this.id != parentMenu.id) {
127                         parentMenu.popupElement.focus();
128                         rf.ui.MenuManager.setActiveSubMenu(parentMenu);
129                     }
130                 }
131             },
132 
133             __closeChildGroups : function() {
134                 var i = 0;
135                 var menuItem;
136                 for (i in this.items) {
137                     menuItem = this.items.eq(i);
138                     if (this.__isGroup(menuItem)) {
139                         rf.component(menuItem).hide();
140                     }
141                 }
142             },
143 
144             __getParentMenuFromItem : function(item) {
145                 var menu;
146                 if (item)
147                     menu = item.parents('div.' + this.options.cssClasses.itemCss).has('div.' + this.options.cssClasses.listContainerCss).eq(1);
148                 if (menu && menu.length > 0)
149                     return menu;
150                 else {
151                     menu = item.parents('div.' + this.options.cssClasses.labelCss);
152                     if (menu && menu.length > 0) {
153                         return menu;
154                     }
155                     else {
156                         return null;
157                     }
158                 }
159             },
160 
161             __getParentMenu : function() {
162                 var menu = $(this.element).parents('div.' + this.options.cssClasses.itemCss).has('div.' + this.options.cssClasses.listContainerCss).eq(0);
163                 if (menu && menu.length > 0) {
164                     return menu;
165                 }
166                 else {
167                     var item = this.items.eq(0);
168                     return this.__getParentMenuFromItem(item);
169                 }
170             },
171 
172             __isGroup : function(item) {
173                 return item.find('div.' + this.options.cssClasses.listCss).length > 0;
174             },
175 
176             __isDisabled : function(item) {
177                 return item.hasClass(this.options.cssClasses.disabledItemCss);
178             },
179 
180             __isShown : function() {
181                 return this.displayed;
182 
183             },
184 
185             __itemMouseEnterHandler : function(e) {
186                 var item = this.__getItemFromEvent(e);
187                 if (item) {
188                     //this.__selectItem(item);
189                     if (this.currentSelectedItemIndex != this.items.index(item)) {
190                         this.__deselectCurrentItem();
191                         this.currentSelectedItemIndex = this.items.index(item);
192                     }
193                 }
194             },
195 
196             __selectItem : function(item) {
197                 if (!rf.component(item).isSelected) {
198                     rf.component(item).select();
199                 }
200             },
201 
202             __getItemFromEvent : function(e) {
203                 return $(e.target).closest("." + this.options.cssClasses.itemCss,e.currentTarget).eq(0);
204             },
205 
206             __showHandler : function(e) {
207                 if (!this.__isShown()) {
208                     this.showTimeoutId = window.setTimeout($.proxy(function() {
209                         this.show(e);
210                     }, this), this.options.showDelay);
211                     return false;
212                 }
213             },
214 
215             __leaveHandler : function() {
216                 this.hideTimeoutId = window.setTimeout($.proxy(function() {
217                     this.hide();
218                 }, this), this.options.hideDelay);
219             },
220 
221             __overHandler : function() {
222                 window.clearTimeout(this.hideTimeoutId);
223                 this.hideTimeoutId = null;
224             },
225 
226             destroy : function() {
227                 // clean up code here
228                 this.detach(this.id);
229 
230                 rf.Event.unbind(this.popupElement, "keydown" + this.namespace);
231 
232                 this.popup.destroy();
233                 this.popup = null;
234 
235                 // call parent's destroy method
236                 $super.destroy.call(this);
237             }
238         };
239     })());
240 
241 })(RichFaces.jQuery, RichFaces);