-
Notifications
You must be signed in to change notification settings - Fork 1
/
script.js
128 lines (116 loc) · 4.2 KB
/
script.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
'use strict';
/* global evt */
window.MenuGroup = (function(win) {
// Extend from the HTMLElement prototype
var proto = evt(Object.create(HTMLElement.prototype));
proto.createdCallback = function() {
this.style.width = '0';
this.classList.add('closed');
this.addEventListener('transitionend', this);
};
proto.calculateChildWidth = function() {
var child = this.firstElementChild;
var childWidth = 0;
var style;
while(child) {
style = window.getComputedStyle(child);
childWidth += child.offsetWidth + parseInt(style.marginLeft, 10) +
parseInt(style.marginRight, 10);
child = child.nextElementSibling;
}
return childWidth;
};
proto.fireEvent = function(event, detail) {
var evtObject = new CustomEvent(event, {
bubbles: false,
detail: detail || this
});
this.dispatchEvent(evtObject);
this.fire(event, detail);
};
proto.handleEvent = function(evt) {
switch(evt.type) {
// Like System app, the transition is our state machine.
case 'transitionend':
// We only process 'background-color' because all states have this
// change.
if ((evt.propertyName !== 'background-color' &&
evt.propertyName !== 'width') ||
evt.target !== this) {
break;
}
if (this.classList.contains('enlarging')) {
this.classList.remove('enlarging');
this.classList.add('shrinking');
// change to shrinking
} else if (this.classList.contains('shrinking')) {
// XXX: this is a workaround of CSS transform. We cannot have a
// rotation right after resizing without any settimeout???
setTimeout((function() {
if (!this.classList.contains('shrinking')) {
// If we don't have shrinking class here, that means this group
// is changing to another state and we don't need to opening
// state.
return;
}
this.classList.remove('shrinking');
this.classList.remove('closed');
this.classList.add('opening');
// change to opening
this.style.width = this.calculateChildWidth() + 'px';
}).bind(this));
} else if (this.classList.contains('opening')) {
this.classList.remove('opening');
// final state: opened
this.fireEvent('opened');
} else if (this.classList.contains('closing')) {
this.classList.remove('closing');
this.classList.add('closed');
// final state: closed
this.fireEvent('closed');
}
break;
}
};
proto.focus = proto.open = function() {
this.fireEvent('will-open');
// If we get focus when we closing the group, we need to cancel the closing
// state.
this.classList.remove('shrinking');
this.classList.remove('opening');
this.classList.remove('closing');
this.classList.add('enlarging');
};
proto.blur = proto.close = function() {
this.fireEvent('will-close');
// We may call close at mid state, we need to reset all of them and go to
// closing state
this.classList.remove('enlarging');
this.classList.remove('shrinking');
this.classList.remove('opening');
this.classList.add('closing');
this.style.width = '0';
};
proto.changeIcon = function(icon) {
if (!this.dataset.icon) {
this.dataset.icon = icon;
return;
}
var current = this.dataset.icon.split(' ')[0];
if (current === icon) {
return;
}
this.classList.add('switching-icon');
this.dataset.icon = icon + ' ' + current;
// Let gecko to recalculate the value and remove it.
// We shouldn't put any value at the setTimeout, but it doesn't work in some
// device, like FirefoxNightly, b2g-desktop. The 100 ms works well in
// these environments.
var self = this;
setTimeout(function() {
self.classList.remove('switching-icon');
}, 100);
};
// Register and return the constructor
return document.registerElement('menu-group', { prototype: proto });
})(window);