r908 - in branches/experimental: tests/visual/menu ui

r908 - in branches/experimental: tests/visual/menu ui


Author: paul.bakaus
Date: Tue Nov 11 06:54:09 2008
New Revision: 908
Modified:
branches/experimental/tests/visual/menu/menu.html
branches/experimental/ui/ui.menu.js
Log:
menu: added flyoutDelay option, added 'add' method to add nodes to the menu
on the fly (currently only works with flyout menus), refactored flyout code
(no scoped variables in timeouts + double event bindings anymore, thank god)
Modified: branches/experimental/tests/visual/menu/menu.html
==============================================================================
--- branches/experimental/tests/visual/menu/menu.html    (original)
+++ branches/experimental/tests/visual/menu/menu.html    Tue Nov 11 06:54:09
2008
@@ -46,6 +46,7 @@
                $('button').menu({
                    items: '#items2',
                    mode: 'dropdown',
+                    flyoutDelay: 300,
                    choose: function(e, ui) {
                        console.log('Selected item ', ui.item);
                    },
@@ -53,6 +54,10 @@
                        //console.log('Browsing item ', ui.item);
                    },
                    open: function(e, ui) {
+                        
+                        //$(this).menu('add', '<li><a href="#">Test Item 1</a></li>');
+                        $(this).menu('add', '<li><a href="#">Test Item 2</a><ul><li><a
href="#">Test Item Sub</a></li></ul></li>', '1/1');
+                        
                        //console.log('Opened menu');
                    },
                    close: function(e, ui) {
Modified: branches/experimental/ui/ui.menu.js
==============================================================================
--- branches/experimental/ui/ui.menu.js    (original)
+++ branches/experimental/ui/ui.menu.js    Tue Nov 11 06:54:09 2008
@@ -23,6 +23,10 @@
    _init: function() {
        
        var o = this.options;
+        $.extend(this, {
+            _showTimers: [],
+            _hideTimers: []
+        });
        
        //Prepare a nodelist of list items that's not in the DOM (anymore)
        if(o.items.constructor == String) {
@@ -50,22 +54,13 @@
            this.menu.addClass('ui-menu-container');
        //Attach hover states for items
-        $('li', this.items)
-            .hover(function() {
-                $(this)
-                    .addClass(o.hoverClassSecondary)
-                    .find('a:eq(0)').addClass(o.hoverClass +' ui-menu-item-on').focus();
-            }, function() {
-                $(this)
-                    .removeClass(o.hoverClassSecondary)
-                    .find('> a').removeClass(o.hoverClass +' ui-menu-item-on').blur();
-            });
+        this._attachHoverStates(this.items);
            
        // when there are multiple levels of hierarchy, create flyout or
drilldown menu
        if ($('ul', this.items).length) {
            if(this.options.type == 'drilldown')
                this._prepareDrilldown();
-            if(this.options.type == 'normal')
+            if(this.options.type == 'flyout')
                this._prepareFlyout();
        }
        
@@ -95,6 +90,75 @@
        
    },
    
+    _itemOver: function(item, e) {
+
+        var item = $(item), self = this;
+        item
+            .addClass(this.options.hoverClassSecondary)
+            .find('a:eq(0)').addClass(this.options.hoverClass +'
ui-menu-item-on').focus();
+            
+            
+        if(this.options.type == 'flyout' && item.is(':has(ul)')) {
+
+            var subList = $('> ul', item);
+            
+            for (var i=0; i < this._hideTimers.length; i++) {
+                if(this._hideTimers[i][1][0] == subList[0])
clearTimeout(this._hideTimers[i][0]);
+            };
+            
+            this._showTimers.push([setTimeout(function(){
+                subList.addClass('ui-component-content').show();
+                subList.positionAround(e, {
+                    around: subList.parent(),
+                    direction: 'right',
+                    offset: [0,-1]
+                });
+                self._trigger('browse', e, { item: subList });    
+            }, this.options.flyoutDelay), subList]);    
+            
+        }
+
+    },
+    
+    _itemOut: function(item, e) {
+
+        var item = $(item), self = this;
+        item
+            .removeClass(this.options.hoverClassSecondary)
+            .find('> a').removeClass(this.options.hoverClass +'
ui-menu-item-on').blur();
+            
+
+        if(this.options.type == 'flyout' && item.is(':has(ul)')) {
+            
+            for (var i=0; i < this._showTimers.length; i++) {
+                if(this._showTimers[i][1][0] == $('> ul', item)[0])
clearTimeout(this._showTimers[i][0]);
+            };
+            
+            this._hideTimers.push([setTimeout(function(){
+                $('> ul', item).removeClass('ui-component-content').hide();
+            }, this.options.flyoutDelay), $('> ul', item)]);
+
+        }
+
+    },
+    
+    _attachHoverStates: function(items, andSelf) {
+        
+        var self = this, items = andSelf ? $('li', items).add(items) : $('li',
items);
+        items.each(function() {
+            if(!$.data(this, 'menu-hover-attached')) {
+                $(this)
+                    .data('menu-hover-attached', true)
+                    .hover(function(e) {
+                        self._itemOver(this, e);
+                    }, function(e) {
+                        self._itemOut(this, e);
+                    });    
+            }
+        });
+    
+    },
+    
    _resetDrilldown: function(stayOpen) {
        this.breadcrumb.empty().append(this.crumbDefaultHeader);
        $('.ui-menu-current', this.menu).removeClass('ui-menu-current');    
@@ -259,77 +323,101 @@
        
    },
    
+    _attachFlyoutStyles: function(item) {
+                
+        item.style.position = 'relative';
+        var showTimer, hideTimer, self = this;
+        var sublists = $('ul', item); //select all sub lists from this point
+        
+        sublists
+            .css({
+                position: 'absolute',
+                top: -1,
+                left: this.options.width, //Show them at the left of the preceding list
+                width: this.options.width, //Set the width according to the options
+                visibility: 'visible' })
+            .hide();
+            
+            
+        $('> a', item)
+            .addClass('ui-menu-indicator') //Add a class that shows the little
arrow to indicate a sub list
+            .html('<span class="'+self.options.nextMenuClass+'">'+$('> a',
item).text()+'</span>'); //Insert a new span
+
+    },
+    
    _prepareFlyout: function() {
        
        
        var self = this;
        this.items.addClass('ui-menu-flyout');
        
-        this.menu.find('li:has(ul)') //Find all li's that have sub lists
-            .css({ position: 'relative' }) //position them relativ to allow sub
lists stick to them
-            .each(function() {
-                
-                var showTimer, hideTimer;
-                var sublists = $('ul', this); //select all sub lists from this point
-                
-                sublists
-                    .css({
-                        position: 'absolute',
-                        top: -1,
-                        left: self.options.width, //Show them at the left of the preceding
list
-                        width: self.options.width, //Set the width according to the options
-                        visibility: 'visible' })
-                    .hide();
-        
-                $('> a', this)
-                    .addClass('ui-menu-indicator') //Add a class that shows the little
arrow to indicate a sub list
-                    .html('<span class="'+self.options.nextMenuClass+'">'+$('> a',
this).text()+'</span>') //Insert a new span
-                    .hover(function(e) {
-                        
-                        if(hideTimer) clearTimeout(hideTimer);
-                        var subList = $(this).next();
-                        
-                        showTimer = setTimeout(function(){
-                            subList.addClass('ui-component-content').show();
-                            subList.positionAround(e, {
-                                around: subList.parent(),
-                                direction: 'right',
-                                offset: [0,-1]
-                            });
-                            self._trigger('browse', e, { item: subList });    
-                        }, 300);
-                        
-                    }, function() {
-                        
-                        if(showTimer) clearTimeout(showTimer);
-                        var subList = $(this).next();
-                        
-                        hideTimer = setTimeout(function(){
-                            subList.removeClass('ui-component-content').hide();
-                        }, 300);    
-                    });
-            
-                $('ul a', this)
-                    .hover(function(){
-                        if(hideTimer) clearTimeout(hideTimer);
-                    }, function() {
-                        hideTimer = setTimeout(function(){
-                            sublists.hide();
-                        }, 300);    
-                    }
-                );
-
-            });
-
+         //Find all li's that have sub lists and attach behaviour to them
+        this.menu.find('li:has(ul)').each(function() {
+            self._attachFlyoutStyles(this);
+        });
+        //Attach the choose click handler to all list items
        $('a', this.menu).bind('click', function(e){
-            self._choose($(this.parentNode), e);    
-            return false;
+            self._choose($(this.parentNode), e);
+            e.preventDefault();
        });
            
    },
    
-    _generateMarkupFromJSON: function() {
+    _generateMarkupFromJSON: function(json) {
+
+    },
+    
+    add: function(item, position) {
+        
+        var self = this;
+        var item = $(item.constructor == String || item.jquery ? item :
this._generateMarkupFromJSON());
+        
+        if(!position) {
+            this.items.append(item);
+        } else {
+            
+            position = position.split('/');
+            var hash = '', old = '', append;
+            for (var i=0; i < position.length; i++) {
+                old = hash; hash += 'li:eq(' + (parseInt(position[i],10)-1) + ') ';
+                if(!$(hash, this.items).length) { append = true; break; }
+            };
+            
+            if(!append) { //We want to have it before/after a node
+                $(hash, this.items).before(item);
+            } else {
+                
+                if(!$(old, this.items).find('ul').length) {
+                    $(old, this.items).append('<ul></ul>');
+                    this._attachFlyoutStyles($(old, this.items)[0]);
+                }
+                
+                $(old, this.items).find('ul').append(item);
+
+            }
+
+        }
+        
+        if(this.options.type == 'drilldown') {
+            
+            
+            
+        } else { //It's a flyout menu item
+            
+            this._attachHoverStates(item, true);
+        
+            //Find all li's that have sub lists and attach styling to them
+            if(item.is(':has(ul)')) this._attachFlyoutStyles(item[0]);
+            item.find('li:has(ul)').each(function() {
self._attachFlyoutStyles(this); });
+    
+            //Attach the choose click handler to all list items
+            $('a', item).bind('click', function(e){
+                self._choose($(this.parentNode), e);
+                e.preventDefault();
+            });
+            
+        }
        
    },
    
@@ -396,7 +484,7 @@
$.extend($.ui.menu, {
    manager: [],
    defaults: {
-        type: 'normal', //Can be set to either normal, drilldown or toolbar
+        type: 'flyout', //Can be set to either flyout, drilldown or toolbar
        mode: 'static', //Can be set to context (open on right click), dropdown
or static (render into the selected element)
        items: '> ul', //Can be either a jQuery selector, therefore using markup
in the selected node, or a JSON list of menu entries
        appendTo: 'body', //Only in case of context/dropdown - where the actual
menu is being appended to,
@@ -410,6 +498,9 @@
        hoverClass: 'ui-hover-state',
        hoverClassSecondary: 'ui-component-content',
        nextMenuClass: 'ui-arrow-right-default', // class to style the link
(specifically, a span within the link) used in the multi-level menu to show
the next level
+        
+        //flyout specific
+        flyoutDelay: 300,
        
        //drilldown menu specific
        crossSpeed: 300, // cross-fade speed for multi-level menus