|
1 YUI().use("*", function(Y) { |
|
2 |
|
3 var UA = Y.UA, |
|
4 getClassName = Y.ClassNameManager.getClassName, |
|
5 sCheckboxFocusClass = getClassName("checkbox", "focus"), // Create yui-checkbox-focus |
|
6 sCheckboxCheckedClass = getClassName("checkbox", "checked"), // Create yui-checkbox-checked |
|
7 sCheckboxActiveClass = getClassName("checkbox", "active"), // Create yui-checkbox-active |
|
8 bKeyListenersInitialized = false, |
|
9 bMouseListenersInitialized = false, |
|
10 forAttr = (UA.ie && UA.ie < 8) ? "htmlFor" : "for", |
|
11 bBlockDocumentMouseUp = false, |
|
12 bBlockClearActive = false, |
|
13 bBlockBlur = false, |
|
14 oActiveCheckbox; |
|
15 |
|
16 |
|
17 var initKeyListeners = function () { |
|
18 |
|
19 this.delegate("keydown", onCheckboxKeyDown, ".yui-checkbox"); |
|
20 this.delegate("click", onCheckboxClick, ".yui-checkbox"); |
|
21 this.delegate("blur", onCheckboxBlur, "input[type=checkbox]"); |
|
22 |
|
23 bKeyListenersInitialized = true; |
|
24 |
|
25 }; |
|
26 |
|
27 |
|
28 var initMouseListeners = function () { |
|
29 |
|
30 this.delegate("mouseover", onCheckboxMouseOver, ".yui-checkbox"); |
|
31 this.delegate("mouseout", onCheckboxMouseOut, ".yui-checkbox-active"); |
|
32 this.get("ownerDocument").on("mouseup", onDocumentMouseUp); |
|
33 |
|
34 bMouseListenersInitialized = true; |
|
35 |
|
36 }; |
|
37 |
|
38 |
|
39 var getCheckbox = function (node) { |
|
40 |
|
41 return (node.hasClass("yui-checkbox") ? node : node.ancestor(".yui-checkbox")); |
|
42 |
|
43 }; |
|
44 |
|
45 |
|
46 var getCheckboxForLabel = function (label) { |
|
47 |
|
48 var sID = label.getAttribute(forAttr), |
|
49 oInput, |
|
50 oCheckbox; |
|
51 |
|
52 if (sID) { |
|
53 |
|
54 oInput = Y.one("#" + sID); |
|
55 |
|
56 if (oInput) { |
|
57 oCheckbox = getCheckbox(oInput); |
|
58 } |
|
59 |
|
60 } |
|
61 |
|
62 return oCheckbox; |
|
63 |
|
64 }; |
|
65 |
|
66 |
|
67 var updateCheckedState = function (input) { |
|
68 |
|
69 var oCheckbox = getCheckbox(input); |
|
70 |
|
71 if (input.get("checked")) { |
|
72 oCheckbox.addClass(sCheckboxCheckedClass); |
|
73 } |
|
74 else { |
|
75 oCheckbox.removeClass(sCheckboxCheckedClass); |
|
76 } |
|
77 |
|
78 }; |
|
79 |
|
80 |
|
81 var setActiveCheckbox = function (checkbox) { |
|
82 |
|
83 checkbox.addClass(sCheckboxActiveClass); |
|
84 oActiveCheckbox = checkbox; |
|
85 |
|
86 }; |
|
87 |
|
88 |
|
89 var clearActiveCheckbox = function () { |
|
90 |
|
91 if (oActiveCheckbox) { |
|
92 oActiveCheckbox.removeClass(sCheckboxActiveClass); |
|
93 oActiveCheckbox = null; |
|
94 } |
|
95 |
|
96 }; |
|
97 |
|
98 |
|
99 var onCheckboxMouseOver = function (event, matchedEl) { |
|
100 |
|
101 if (oActiveCheckbox && oActiveCheckbox.compareTo(this)) { |
|
102 oActiveCheckbox.addClass(sCheckboxActiveClass); |
|
103 } |
|
104 |
|
105 }; |
|
106 |
|
107 |
|
108 var onCheckboxMouseOut = function (event) { |
|
109 |
|
110 this.removeClass(sCheckboxActiveClass); |
|
111 |
|
112 }; |
|
113 |
|
114 |
|
115 var onDocumentMouseUp = function (event) { |
|
116 |
|
117 var oCheckbox; |
|
118 |
|
119 if (!bBlockDocumentMouseUp) { |
|
120 |
|
121 oCheckbox = getCheckbox(event.target); |
|
122 |
|
123 if ((oCheckbox && !oCheckbox.compareTo(oActiveCheckbox)) || !oCheckbox) { |
|
124 clearActiveCheckbox(); |
|
125 } |
|
126 |
|
127 } |
|
128 |
|
129 bBlockDocumentMouseUp = false; |
|
130 |
|
131 }; |
|
132 |
|
133 |
|
134 var onCheckboxFocus = function (event) { |
|
135 |
|
136 // Remove the focus style from any checkbox that might still have it |
|
137 |
|
138 var oCheckbox = Y.one("#checkboxes").one(".yui-checkbox-focus"); |
|
139 |
|
140 if (oCheckbox) { |
|
141 oCheckbox.removeClass(sCheckboxFocusClass); |
|
142 } |
|
143 |
|
144 // Defer adding key-related and click event listeners until |
|
145 // one of the checkboxes is initially focused. |
|
146 |
|
147 if (!bKeyListenersInitialized) { |
|
148 initKeyListeners.call(event.container); |
|
149 } |
|
150 |
|
151 oCheckbox = getCheckbox(this); |
|
152 |
|
153 oCheckbox.addClass(sCheckboxFocusClass); |
|
154 |
|
155 }; |
|
156 |
|
157 |
|
158 var onCheckboxBlur = function (event) { |
|
159 |
|
160 if (bBlockBlur) { |
|
161 bBlockBlur = false; |
|
162 return; |
|
163 } |
|
164 |
|
165 var oCheckbox = getCheckbox(this); |
|
166 |
|
167 oCheckbox.removeClass(sCheckboxFocusClass); |
|
168 |
|
169 if (!bBlockClearActive && oCheckbox.compareTo(oActiveCheckbox)) { |
|
170 clearActiveCheckbox(); |
|
171 } |
|
172 |
|
173 bBlockClearActive = false; |
|
174 |
|
175 }; |
|
176 |
|
177 |
|
178 var onCheckboxMouseDown = function (event) { |
|
179 |
|
180 // Defer adding mouse-related and click event listeners until |
|
181 // the user mouses down on one of the checkboxes. |
|
182 |
|
183 if (!bMouseListenersInitialized) { |
|
184 initMouseListeners.call(event.container); |
|
185 } |
|
186 |
|
187 var oCheckbox, |
|
188 oInput; |
|
189 |
|
190 |
|
191 if (this.get("nodeName").toLowerCase() === "label") { |
|
192 |
|
193 // If the target of the event was the checkbox's label element, the |
|
194 // label will dispatch a click event to the checkbox, meaning the |
|
195 // "onCheckboxClick" handler will be called twice. For that reason |
|
196 // it is necessary to block the "onDocumentMouseUp" handler from |
|
197 // clearing the active state, so that a reference to the active |
|
198 // checkbox still exists the second time the "onCheckboxClick" |
|
199 // handler is called. |
|
200 |
|
201 bBlockDocumentMouseUp = true; |
|
202 |
|
203 // When the user clicks the label instead of the checkbox itself, |
|
204 // the checkbox will be blurred if it has focus. Since the |
|
205 // "onCheckboxBlur" handler clears the active state it is |
|
206 // necessary to block the clearing of the active state when the |
|
207 // label is clicked instead of the checkbox itself. |
|
208 |
|
209 bBlockClearActive = true; |
|
210 |
|
211 oCheckbox = getCheckboxForLabel(this); |
|
212 |
|
213 } |
|
214 else { |
|
215 |
|
216 oCheckbox = this; |
|
217 |
|
218 } |
|
219 |
|
220 // Need to focus the input manually for two reasons: |
|
221 // 1) Mousing down on a label in Webkit doesn't focus its |
|
222 // associated checkbox |
|
223 // 2) By default checkboxes are focused when the user mouses |
|
224 // down on them. However, since the actually checkbox is |
|
225 // obscurred by the two span elements that are used to |
|
226 // style it, the checkbox wont' receive focus as it was |
|
227 // never the actual target of the mousedown event. |
|
228 |
|
229 oInput = oCheckbox.one("input"); |
|
230 |
|
231 |
|
232 // Calling Event.preventDefault won't block the blurring of the |
|
233 // currently focused element in IE, so we'll use the "bBlockBlur" |
|
234 // variable to stop the code in the blur event handler |
|
235 // from executing. |
|
236 |
|
237 bBlockBlur = (UA.ie && oInput.get("checked")); |
|
238 |
|
239 |
|
240 oInput.focus(); |
|
241 |
|
242 setActiveCheckbox(oCheckbox); |
|
243 |
|
244 // Need to call preventDefault because by default mousing down on |
|
245 // an element will blur the element in the document that currently |
|
246 // has focus--in this case, the input element that was |
|
247 // just focused. |
|
248 |
|
249 event.preventDefault(); |
|
250 |
|
251 }; |
|
252 |
|
253 |
|
254 var onCheckboxClick = function (event) { |
|
255 |
|
256 var oInput; |
|
257 |
|
258 if (this.compareTo(oActiveCheckbox)) { |
|
259 |
|
260 oInput = this.one("input"); |
|
261 |
|
262 if (!event.target.compareTo(oInput)) { |
|
263 |
|
264 // If the click event was fired via the mouse the checked |
|
265 // state will have to be manually updated since the input |
|
266 // is hidden offscreen and therefore couldn't be the |
|
267 // target of the click. |
|
268 |
|
269 oInput.set("checked", (!oInput.get("checked"))); |
|
270 |
|
271 } |
|
272 |
|
273 updateCheckedState(oInput); |
|
274 clearActiveCheckbox(); |
|
275 |
|
276 } |
|
277 |
|
278 }; |
|
279 |
|
280 |
|
281 var onCheckboxKeyDown = function (event) { |
|
282 |
|
283 // Style the checkbox as being active when the user presses the |
|
284 // space bar |
|
285 |
|
286 if (event.keyCode === 32) { |
|
287 setActiveCheckbox(this); |
|
288 } |
|
289 |
|
290 }; |
|
291 |
|
292 Y.all("#checkboxes>div>span").addClass("yui-checkbox"); |
|
293 |
|
294 // Remove the "yui-checkboxes-loading" class used to hide the |
|
295 // checkboxes now that the checkboxes have been skinned. |
|
296 |
|
297 Y.one("#checkboxes").get("ownerDocument").get("documentElement").removeClass("yui-checkboxes-loading"); |
|
298 |
|
299 // Add the minimum number of event listeners needed to start, bind the |
|
300 // rest when needed |
|
301 |
|
302 Y.delegate("mousedown", onCheckboxMouseDown, "#checkboxes", ".yui-checkbox,label"); |
|
303 Y.delegate("focus", onCheckboxFocus, "#checkboxes", "input[type=checkbox]"); |
|
304 |
|
305 }); |