-
Notifications
You must be signed in to change notification settings - Fork 25
/
Copy pathmenuable.js
132 lines (125 loc) · 3.36 KB
/
menuable.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
steal('jquery/controller',
'jquery/event/default',
'jquery/event/pause',
'jquery/dom/closest').then(function($){
/**
* A general Menu System.
* 1. Listens for 'click' on 'li' elements (configurable)
* 2. Triggers "deselect" on that li.
* 3. By Default on "deselect"
* triggers "hide" on the old submenu.
* If hide is prevented -> stops.
* removes selected styling on old li
* triggers "select" on that li
* 4. By Default on "select"
* triggers "show" on the new submenu
* adds selected styling on li
*
* The menu also listens to the following by default:
* "hide" -> hides the menu
* "show" -> shows the menu
*/
$.Controller('Mxui.Nav.Menuable',
{
defaults : {
/**
* A list of other types we want to mixin to each menu
*/
types : [],
/**
* The active className
*/
active : "active",
/**
* The selected className
*/
select : "selected",
child_selector : "li"
},
listensTo : ["hide","show"]
},
{
/**
* Returns the sub-menu from this item
*/
sub : function(el){
return el.children().eq(1);
},
/**
* Returns where a sub-menu element should be positioned from.
*/
calculateSubmenuPosition : function(el, ev){
return el;
},
">{child_selector} activate" : function(el, ev){
if(el.hasClass(this.options.active))
return;
if(this.activating)
return;
this.activating = true;
var options = this.options, oldActive = this.element.children("."+options.active+":first"), self= this;
ev.pause();
var doThis = function(){
oldActive.triggerAsync("deactivate", function(){
self.sub(el).triggerAsync('show',
self.calculateSubmenuPosition(el, ev),
function(){
ev.resume();
})
})
}
// if we are already selected
if(el.hasClass(this.options.select))
doThis();
else
// select us, after we have been selected, do everything ...
el.triggerAsync("select", function(){
doThis();
});
},
">{child_selector} default.activate" : function(el, ev){
el.addClass(this.options.active)
this.activating = false;
this.element.trigger("change")
},
">{child_selector} deactivate" : function(el, ev ){
ev.pause();
this.sub(el).triggerAsync('hide', function(){
ev.resume();
})
},
">{child_selector} default.deactivate" : function(el, ev){
el.removeClass(this.options.active)
},
//there is no preventing this behavior ...
">{child_selector} select" : function(el, ev){
if(this.selecting)
return;
this.selecting = true;
ev.pause();
this.find("."+this.options.select+":first").triggerAsync('deselect',function(){
ev.resume(); // should hit your handler and the default behavior
})
},
">{child_selector} default.select" : function(el, ev){
el.addClass(this.options.select)
this.selecting = false;
},
">{child_selector} default.deselect" : function(el, ev ){
el.removeClass(this.options.select)
},
/**
* Checks if we are the target for the hide, and hides any active submenus.
* This could check that those submenu hides are ok, but doesnt .... yet.
*/
">hide" : function(el, ev){
var self = this;
ev.pause();
this.element.find("."+this.options.active).triggerAsync("deactivate", function(){
self.element.find("."+self.options.select).triggerAsync("deselect", function(){
ev.resume();
})
});
}
});
})