|
1 <?xml version="1.0"?> |
|
2 <!-- This Source Code Form is subject to the terms of the Mozilla Public |
|
3 - License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> |
|
5 |
|
6 |
|
7 <bindings id="tabBindings" |
|
8 xmlns="http://www.mozilla.org/xbl" |
|
9 xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" |
|
10 xmlns:xbl="http://www.mozilla.org/xbl"> |
|
11 |
|
12 <binding id="tab-base"> |
|
13 <resources> |
|
14 <stylesheet src="chrome://global/skin/tabbox.css"/> |
|
15 </resources> |
|
16 </binding> |
|
17 |
|
18 <binding id="tabbox" |
|
19 extends="chrome://global/content/bindings/tabbox.xml#tab-base"> |
|
20 <implementation implements="nsIDOMEventListener"> |
|
21 <property name="handleCtrlTab"> |
|
22 <setter> |
|
23 <![CDATA[ |
|
24 this.setAttribute("handleCtrlTab", val); |
|
25 return val; |
|
26 ]]> |
|
27 </setter> |
|
28 <getter> |
|
29 <![CDATA[ |
|
30 return (this.getAttribute("handleCtrlTab") != "false"); |
|
31 ]]> |
|
32 </getter> |
|
33 </property> |
|
34 |
|
35 <property name="handleCtrlPageUpDown"> |
|
36 <setter> |
|
37 <![CDATA[ |
|
38 this.setAttribute("handleCtrlPageUpDown", val); |
|
39 return val; |
|
40 ]]> |
|
41 </setter> |
|
42 <getter> |
|
43 <![CDATA[ |
|
44 return (this.getAttribute("handleCtrlPageUpDown") != "false"); |
|
45 ]]> |
|
46 </getter> |
|
47 </property> |
|
48 |
|
49 <field name="_handleMetaAltArrows" readonly="true"> |
|
50 #ifdef XP_MACOSX |
|
51 true |
|
52 #else |
|
53 false |
|
54 #endif |
|
55 </field> |
|
56 |
|
57 <!-- _tabs and _tabpanels are deprecated, they exist only for |
|
58 backwards compatibility. --> |
|
59 <property name="_tabs" readonly="true" onget="return this.tabs;"/> |
|
60 <property name="_tabpanels" readonly="true" onget="return this.tabpanels;"/> |
|
61 |
|
62 <property name="tabs" readonly="true"> |
|
63 <getter> |
|
64 <![CDATA[ |
|
65 return this.getElementsByTagNameNS( |
|
66 "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", |
|
67 "tabs").item(0); |
|
68 ]]> |
|
69 </getter> |
|
70 </property> |
|
71 |
|
72 <property name="tabpanels" readonly="true"> |
|
73 <getter> |
|
74 <![CDATA[ |
|
75 return this.getElementsByTagNameNS( |
|
76 "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", |
|
77 "tabpanels").item(0); |
|
78 ]]> |
|
79 </getter> |
|
80 </property> |
|
81 |
|
82 <property name="selectedIndex"> |
|
83 <getter> |
|
84 <![CDATA[ |
|
85 var tabs = this.tabs; |
|
86 return tabs ? tabs.selectedIndex : -1; |
|
87 ]]> |
|
88 </getter> |
|
89 |
|
90 <setter> |
|
91 <![CDATA[ |
|
92 var tabs = this.tabs; |
|
93 if (tabs) |
|
94 tabs.selectedIndex = val; |
|
95 this.setAttribute("selectedIndex", val); |
|
96 return val; |
|
97 ]]> |
|
98 </setter> |
|
99 </property> |
|
100 |
|
101 <property name="selectedTab"> |
|
102 <getter> |
|
103 <![CDATA[ |
|
104 var tabs = this.tabs; |
|
105 return tabs && tabs.selectedItem; |
|
106 ]]> |
|
107 </getter> |
|
108 |
|
109 <setter> |
|
110 <![CDATA[ |
|
111 if (val) { |
|
112 var tabs = this.tabs; |
|
113 if (tabs) |
|
114 tabs.selectedItem = val; |
|
115 } |
|
116 return val; |
|
117 ]]> |
|
118 </setter> |
|
119 </property> |
|
120 |
|
121 <property name="selectedPanel"> |
|
122 <getter> |
|
123 <![CDATA[ |
|
124 var tabpanels = this.tabpanels; |
|
125 return tabpanels && tabpanels.selectedPanel; |
|
126 ]]> |
|
127 </getter> |
|
128 |
|
129 <setter> |
|
130 <![CDATA[ |
|
131 if (val) { |
|
132 var tabpanels = this.tabpanels; |
|
133 if (tabpanels) |
|
134 tabpanels.selectedPanel = val; |
|
135 } |
|
136 return val; |
|
137 ]]> |
|
138 </setter> |
|
139 </property> |
|
140 |
|
141 <method name="handleEvent"> |
|
142 <parameter name="event"/> |
|
143 <body> |
|
144 <![CDATA[ |
|
145 if (!event.isTrusted) { |
|
146 // Don't let untrusted events mess with tabs. |
|
147 return; |
|
148 } |
|
149 |
|
150 switch (event.keyCode) { |
|
151 case event.DOM_VK_TAB: |
|
152 if (event.ctrlKey && !event.altKey && !event.metaKey) |
|
153 if (this.tabs && this.handleCtrlTab) { |
|
154 this.tabs.advanceSelectedTab(event.shiftKey ? -1 : 1, true); |
|
155 event.stopPropagation(); |
|
156 event.preventDefault(); |
|
157 } |
|
158 break; |
|
159 case event.DOM_VK_PAGE_UP: |
|
160 if (event.ctrlKey && !event.shiftKey && !event.altKey && !event.metaKey) |
|
161 if (this.tabs && this.handleCtrlPageUpDown) { |
|
162 this.tabs.advanceSelectedTab(-1, true); |
|
163 event.stopPropagation(); |
|
164 event.preventDefault(); |
|
165 } |
|
166 break; |
|
167 case event.DOM_VK_PAGE_DOWN: |
|
168 if (event.ctrlKey && !event.shiftKey && !event.altKey && !event.metaKey) |
|
169 if (this.tabs && this.handleCtrlPageUpDown) { |
|
170 this.tabs.advanceSelectedTab(1, true); |
|
171 event.stopPropagation(); |
|
172 event.preventDefault(); |
|
173 } |
|
174 break; |
|
175 case event.DOM_VK_LEFT: |
|
176 if (event.metaKey && event.altKey && !event.shiftKey && !event.ctrlKey) |
|
177 if (this.tabs && this._handleMetaAltArrows) { |
|
178 var offset = window.getComputedStyle(this, "") |
|
179 .direction == "ltr" ? -1 : 1; |
|
180 this.tabs.advanceSelectedTab(offset, true); |
|
181 event.stopPropagation(); |
|
182 event.preventDefault(); |
|
183 } |
|
184 break; |
|
185 case event.DOM_VK_RIGHT: |
|
186 if (event.metaKey && event.altKey && !event.shiftKey && !event.ctrlKey) |
|
187 if (this.tabs && this._handleMetaAltArrows) { |
|
188 var offset = window.getComputedStyle(this, "") |
|
189 .direction == "ltr" ? 1 : -1; |
|
190 this.tabs.advanceSelectedTab(offset, true); |
|
191 event.stopPropagation(); |
|
192 event.preventDefault(); |
|
193 } |
|
194 break; |
|
195 } |
|
196 ]]> |
|
197 </body> |
|
198 </method> |
|
199 |
|
200 <field name="_eventNode">this</field> |
|
201 |
|
202 <property name="eventNode" onget="return this._eventNode;"> |
|
203 <setter> |
|
204 <![CDATA[ |
|
205 if (val != this._eventNode) { |
|
206 val.addEventListener("keypress", this, false); |
|
207 this._eventNode.removeEventListener("keypress", this, false); |
|
208 this._eventNode = val; |
|
209 } |
|
210 return val; |
|
211 ]]> |
|
212 </setter> |
|
213 </property> |
|
214 |
|
215 <constructor> |
|
216 switch (this.getAttribute("eventnode")) { |
|
217 case "parent": this._eventNode = this.parentNode; break; |
|
218 case "window": this._eventNode = window; break; |
|
219 case "document": this._eventNode = document; break; |
|
220 } |
|
221 this._eventNode.addEventListener("keypress", this, false); |
|
222 </constructor> |
|
223 |
|
224 <destructor> |
|
225 this._eventNode.removeEventListener("keypress", this, false); |
|
226 </destructor> |
|
227 </implementation> |
|
228 </binding> |
|
229 |
|
230 <binding id="tabs" role="xul:tabs" |
|
231 extends="chrome://global/content/bindings/general.xml#basecontrol"> |
|
232 <resources> |
|
233 <stylesheet src="chrome://global/skin/tabbox.css"/> |
|
234 </resources> |
|
235 |
|
236 <content> |
|
237 <xul:spacer class="tabs-left"/> |
|
238 <children/> |
|
239 <xul:spacer class="tabs-right" flex="1"/> |
|
240 </content> |
|
241 |
|
242 <implementation implements="nsIDOMXULSelectControlElement, nsIDOMXULRelatedElement"> |
|
243 <constructor> |
|
244 <![CDATA[ |
|
245 // first and last tabs need to be able to have unique styles |
|
246 // and also need to select first tab on startup. |
|
247 if (this.firstChild) |
|
248 this.firstChild.setAttribute("first-tab", "true"); |
|
249 if (this.lastChild) |
|
250 this.lastChild.setAttribute("last-tab", "true"); |
|
251 |
|
252 if (!this.hasAttribute("orient")) |
|
253 this.setAttribute("orient", "horizontal"); |
|
254 |
|
255 if (this.tabbox && this.tabbox.hasAttribute("selectedIndex")) { |
|
256 let selectedIndex = parseInt(this.tabbox.getAttribute("selectedIndex")); |
|
257 this.selectedIndex = selectedIndex > 0 ? selectedIndex : 0; |
|
258 return; |
|
259 } |
|
260 |
|
261 var children = this.childNodes; |
|
262 var length = children.length; |
|
263 for (var i = 0; i < length; i++) { |
|
264 if (children[i].getAttribute("selected") == "true") { |
|
265 this.selectedIndex = i; |
|
266 return; |
|
267 } |
|
268 } |
|
269 |
|
270 var value = this.value; |
|
271 if (value) |
|
272 this.value = value; |
|
273 else |
|
274 this.selectedIndex = 0; |
|
275 ]]> |
|
276 </constructor> |
|
277 |
|
278 <!-- nsIDOMXULRelatedElement --> |
|
279 <method name="getRelatedElement"> |
|
280 <parameter name="aTabElm"/> |
|
281 <body> |
|
282 <![CDATA[ |
|
283 if (!aTabElm) |
|
284 return null; |
|
285 |
|
286 let tabboxElm = this.tabbox; |
|
287 if (!tabboxElm) |
|
288 return null; |
|
289 |
|
290 let tabpanelsElm = tabboxElm.tabpanels; |
|
291 if (!tabpanelsElm) |
|
292 return null; |
|
293 |
|
294 // Get linked tab panel by 'linkedpanel' attribute on the given tab |
|
295 // element. |
|
296 let linkedPanelElm = null; |
|
297 |
|
298 let linkedPanelId = aTabElm.linkedPanel; |
|
299 if (linkedPanelId) { |
|
300 let ownerDoc = this.ownerDocument; |
|
301 |
|
302 // XXX bug 565858: if XUL tab element is anonymous element then |
|
303 // suppose linked tab panel is hosted within the same XBL binding |
|
304 // and search it by ID attribute inside an anonymous content of |
|
305 // the binding. This is not robust assumption since tab elements may |
|
306 // live outside a tabbox element so that for example tab elements |
|
307 // can be explicit content but tab panels can be anonymous. |
|
308 |
|
309 let bindingParent = ownerDoc.getBindingParent(aTabElm); |
|
310 if (bindingParent) |
|
311 return ownerDoc.getAnonymousElementByAttribute(bindingParent, |
|
312 "id", |
|
313 linkedPanelId); |
|
314 |
|
315 return ownerDoc.getElementById(linkedPanelId); |
|
316 } |
|
317 |
|
318 // otherwise linked tabpanel element has the same index as the given |
|
319 // tab element. |
|
320 let tabElmIdx = this.getIndexOfItem(aTabElm); |
|
321 return tabpanelsElm.childNodes[tabElmIdx]; |
|
322 ]]> |
|
323 </body> |
|
324 </method> |
|
325 |
|
326 <!-- nsIDOMXULSelectControlElement --> |
|
327 <property name="itemCount" readonly="true" |
|
328 onget="return this.childNodes.length"/> |
|
329 |
|
330 <property name="value" onget="return this.getAttribute('value');"> |
|
331 <setter> |
|
332 <![CDATA[ |
|
333 this.setAttribute("value", val); |
|
334 var children = this.childNodes; |
|
335 for (var c = children.length - 1; c >= 0; c--) { |
|
336 if (children[c].value == val) { |
|
337 this.selectedIndex = c; |
|
338 break; |
|
339 } |
|
340 } |
|
341 return val; |
|
342 ]]> |
|
343 </setter> |
|
344 </property> |
|
345 |
|
346 <field name="tabbox" readonly="true"><![CDATA[ |
|
347 var parent = this.parentNode; |
|
348 while (parent) { |
|
349 if (parent.localName == "tabbox") |
|
350 break; |
|
351 parent = parent.parentNode; |
|
352 } |
|
353 parent; |
|
354 ]]></field> |
|
355 |
|
356 <!-- _tabbox is deprecated, it exists only for backwards compatibility. --> |
|
357 <field name="_tabbox" readonly="true"><![CDATA[ |
|
358 this.tabbox; |
|
359 ]]></field> |
|
360 |
|
361 <property name="selectedIndex"> |
|
362 <getter> |
|
363 <![CDATA[ |
|
364 const tabs = this.childNodes; |
|
365 for (var i = 0; i < tabs.length; i++) { |
|
366 if (tabs[i].selected) |
|
367 return i; |
|
368 } |
|
369 return -1; |
|
370 ]]> |
|
371 </getter> |
|
372 |
|
373 <setter> |
|
374 <![CDATA[ |
|
375 var tab = this.getItemAtIndex(val); |
|
376 if (tab) { |
|
377 var alreadySelected = tab.selected; |
|
378 |
|
379 Array.forEach(this.childNodes, function (aTab) { |
|
380 if (aTab.selected && aTab != tab) |
|
381 aTab._selected = false; |
|
382 }); |
|
383 tab._selected = true; |
|
384 |
|
385 this.setAttribute("value", tab.value); |
|
386 |
|
387 let linkedPanel = this.getRelatedElement(tab); |
|
388 if (linkedPanel) { |
|
389 this.tabbox.setAttribute("selectedIndex", val); |
|
390 |
|
391 // This will cause an onselect event to fire for the tabpanel |
|
392 // element. |
|
393 this.tabbox.tabpanels.selectedPanel = linkedPanel; |
|
394 } |
|
395 |
|
396 if (!alreadySelected) { |
|
397 // Fire an onselect event for the tabs element. |
|
398 var event = document.createEvent('Events'); |
|
399 event.initEvent('select', true, true); |
|
400 this.dispatchEvent(event); |
|
401 } |
|
402 } |
|
403 return val; |
|
404 ]]> |
|
405 </setter> |
|
406 </property> |
|
407 |
|
408 <property name="selectedItem"> |
|
409 <getter> |
|
410 <![CDATA[ |
|
411 const tabs = this.childNodes; |
|
412 for (var i = 0; i < tabs.length; i++) { |
|
413 if (tabs[i].selected) |
|
414 return tabs[i]; |
|
415 } |
|
416 return null; |
|
417 ]]> |
|
418 </getter> |
|
419 |
|
420 <setter> |
|
421 <![CDATA[ |
|
422 if (val && !val.selected) |
|
423 // The selectedIndex setter ignores invalid values |
|
424 // such as -1 if |val| isn't one of our child nodes. |
|
425 this.selectedIndex = this.getIndexOfItem(val); |
|
426 return val; |
|
427 ]]> |
|
428 </setter> |
|
429 </property> |
|
430 |
|
431 <method name="getIndexOfItem"> |
|
432 <parameter name="item"/> |
|
433 <body> |
|
434 <![CDATA[ |
|
435 return Array.indexOf(this.childNodes, item); |
|
436 ]]> |
|
437 </body> |
|
438 </method> |
|
439 |
|
440 <method name="getItemAtIndex"> |
|
441 <parameter name="index"/> |
|
442 <body> |
|
443 <![CDATA[ |
|
444 return this.childNodes.item(index); |
|
445 ]]> |
|
446 </body> |
|
447 </method> |
|
448 |
|
449 <method name="_selectNewTab"> |
|
450 <parameter name="aNewTab"/> |
|
451 <parameter name="aFallbackDir"/> |
|
452 <parameter name="aWrap"/> |
|
453 <body> |
|
454 <![CDATA[ |
|
455 var requestedTab = aNewTab; |
|
456 while (aNewTab.hidden || aNewTab.disabled || !this._canAdvanceToTab(aNewTab)) { |
|
457 aNewTab = aFallbackDir == -1 ? aNewTab.previousSibling : aNewTab.nextSibling; |
|
458 if (!aNewTab && aWrap) |
|
459 aNewTab = aFallbackDir == -1 ? this.childNodes[this.childNodes.length - 1] : |
|
460 this.childNodes[0]; |
|
461 if (!aNewTab || aNewTab == requestedTab) |
|
462 return; |
|
463 } |
|
464 |
|
465 var isTabFocused = false; |
|
466 try { |
|
467 isTabFocused = |
|
468 (document.commandDispatcher.focusedElement == this.selectedItem); |
|
469 } catch (e) {} |
|
470 this.selectedItem = aNewTab; |
|
471 if (isTabFocused) { |
|
472 aNewTab.focus(); |
|
473 } |
|
474 else if (this.getAttribute("setfocus") != "false") { |
|
475 let selectedPanel = this.tabbox.selectedPanel; |
|
476 document.commandDispatcher.advanceFocusIntoSubtree(selectedPanel); |
|
477 |
|
478 // Make sure that the focus doesn't move outside the tabbox |
|
479 if (this.tabbox) { |
|
480 try { |
|
481 let el = document.commandDispatcher.focusedElement; |
|
482 while (el && el != this.tabbox.tabpanels) { |
|
483 if (el == this.tabbox || el == selectedPanel) |
|
484 return; |
|
485 el = el.parentNode; |
|
486 } |
|
487 aNewTab.focus(); |
|
488 } catch(e) { |
|
489 } |
|
490 } |
|
491 } |
|
492 ]]> |
|
493 </body> |
|
494 </method> |
|
495 |
|
496 <method name="_canAdvanceToTab"> |
|
497 <parameter name="aTab"/> |
|
498 <body> |
|
499 <![CDATA[ |
|
500 return true; |
|
501 ]]> |
|
502 </body> |
|
503 </method> |
|
504 |
|
505 <method name="advanceSelectedTab"> |
|
506 <parameter name="aDir"/> |
|
507 <parameter name="aWrap"/> |
|
508 <body> |
|
509 <![CDATA[ |
|
510 var startTab = this.selectedItem; |
|
511 var next = startTab[aDir == -1 ? "previousSibling" : "nextSibling"]; |
|
512 if (!next && aWrap) { |
|
513 next = aDir == -1 ? this.childNodes[this.childNodes.length - 1] : |
|
514 this.childNodes[0]; |
|
515 } |
|
516 if (next && next != startTab) { |
|
517 this._selectNewTab(next, aDir, aWrap); |
|
518 } |
|
519 ]]> |
|
520 </body> |
|
521 </method> |
|
522 |
|
523 <method name="appendItem"> |
|
524 <parameter name="label"/> |
|
525 <parameter name="value"/> |
|
526 <body> |
|
527 <![CDATA[ |
|
528 var XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; |
|
529 var tab = document.createElementNS(XULNS, "tab"); |
|
530 tab.setAttribute("label", label); |
|
531 tab.setAttribute("value", value); |
|
532 this.appendChild(tab); |
|
533 return tab; |
|
534 ]]> |
|
535 </body> |
|
536 </method> |
|
537 |
|
538 <method name="insertItemAt"> |
|
539 <parameter name="index"/> |
|
540 <parameter name="label"/> |
|
541 <parameter name="value"/> |
|
542 <body> |
|
543 <![CDATA[ |
|
544 var XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; |
|
545 var tab = document.createElementNS(XULNS, "tab"); |
|
546 tab.setAttribute("label", label); |
|
547 tab.setAttribute("value", value); |
|
548 var before = this.getItemAtIndex(index); |
|
549 if (before) |
|
550 this.insertBefore(tab, before); |
|
551 else |
|
552 this.appendChild(tab); |
|
553 return tab; |
|
554 ]]> |
|
555 </body> |
|
556 </method> |
|
557 |
|
558 <method name="removeItemAt"> |
|
559 <parameter name="index"/> |
|
560 <body> |
|
561 <![CDATA[ |
|
562 var remove = this.getItemAtIndex(index); |
|
563 if (remove) |
|
564 this.removeChild(remove); |
|
565 return remove; |
|
566 ]]> |
|
567 </body> |
|
568 </method> |
|
569 </implementation> |
|
570 |
|
571 #ifdef MOZ_WIDGET_GTK |
|
572 <handlers> |
|
573 <handler event="DOMMouseScroll"> |
|
574 <![CDATA[ |
|
575 if (event.detail > 0) |
|
576 this.advanceSelectedTab(1, false); |
|
577 else |
|
578 this.advanceSelectedTab(-1, false); |
|
579 |
|
580 event.stopPropagation(); |
|
581 ]]> |
|
582 </handler> |
|
583 </handlers> |
|
584 #endif |
|
585 </binding> |
|
586 |
|
587 <binding id="tabpanels" role="xul:tabpanels" |
|
588 extends="chrome://global/content/bindings/tabbox.xml#tab-base"> |
|
589 <implementation implements="nsIDOMXULRelatedElement"> |
|
590 <!-- nsIDOMXULRelatedElement --> |
|
591 <method name="getRelatedElement"> |
|
592 <parameter name="aTabPanelElm"/> |
|
593 <body> |
|
594 <![CDATA[ |
|
595 if (!aTabPanelElm) |
|
596 return null; |
|
597 |
|
598 let tabboxElm = this.tabbox; |
|
599 if (!tabboxElm) |
|
600 return null; |
|
601 |
|
602 let tabsElm = tabboxElm.tabs; |
|
603 if (!tabsElm) |
|
604 return null; |
|
605 |
|
606 // Return tab element having 'linkedpanel' attribute equal to the id |
|
607 // of the tab panel or the same index as the tab panel element. |
|
608 let tabpanelIdx = Array.indexOf(this.childNodes, aTabPanelElm); |
|
609 if (tabpanelIdx == -1) |
|
610 return null; |
|
611 |
|
612 let tabElms = tabsElm.childNodes; |
|
613 let tabElmFromIndex = tabElms[tabpanelIdx]; |
|
614 |
|
615 let tabpanelId = aTabPanelElm.id; |
|
616 if (tabpanelId) { |
|
617 for (let idx = 0; idx < tabElms.length; idx++) { |
|
618 var tabElm = tabElms[idx]; |
|
619 if (tabElm.linkedPanel == tabpanelId) |
|
620 return tabElm; |
|
621 } |
|
622 } |
|
623 |
|
624 return tabElmFromIndex; |
|
625 ]]> |
|
626 </body> |
|
627 </method> |
|
628 |
|
629 <!-- public --> |
|
630 <field name="tabbox" readonly="true"><![CDATA[ |
|
631 var parent = this.parentNode; |
|
632 while (parent) { |
|
633 if (parent.localName == "tabbox") |
|
634 break; |
|
635 parent = parent.parentNode; |
|
636 } |
|
637 parent; |
|
638 ]]></field> |
|
639 |
|
640 <field name="_selectedPanel">this.childNodes.item(this.selectedIndex)</field> |
|
641 |
|
642 <property name="selectedIndex"> |
|
643 <getter> |
|
644 <![CDATA[ |
|
645 var indexStr = this.getAttribute("selectedIndex"); |
|
646 return indexStr ? parseInt(indexStr) : -1; |
|
647 ]]> |
|
648 </getter> |
|
649 |
|
650 <setter> |
|
651 <![CDATA[ |
|
652 if (val < 0 || val >= this.childNodes.length) |
|
653 return val; |
|
654 var panel = this._selectedPanel; |
|
655 this._selectedPanel = this.childNodes[val]; |
|
656 this.setAttribute("selectedIndex", val); |
|
657 if (this._selectedPanel != panel) { |
|
658 var event = document.createEvent("Events"); |
|
659 event.initEvent("select", true, true); |
|
660 this.dispatchEvent(event); |
|
661 } |
|
662 return val; |
|
663 ]]> |
|
664 </setter> |
|
665 </property> |
|
666 |
|
667 <property name="selectedPanel"> |
|
668 <getter> |
|
669 <![CDATA[ |
|
670 return this._selectedPanel; |
|
671 ]]> |
|
672 </getter> |
|
673 |
|
674 <setter> |
|
675 <![CDATA[ |
|
676 var selectedIndex = -1; |
|
677 for (var panel = val; panel != null; panel = panel.previousSibling) |
|
678 ++selectedIndex; |
|
679 this.selectedIndex = selectedIndex; |
|
680 return val; |
|
681 ]]> |
|
682 </setter> |
|
683 </property> |
|
684 </implementation> |
|
685 </binding> |
|
686 |
|
687 <binding id="tab" display="xul:button" role="xul:tab" |
|
688 extends="chrome://global/content/bindings/general.xml#control-item"> |
|
689 <resources> |
|
690 <stylesheet src="chrome://global/skin/tabbox.css"/> |
|
691 </resources> |
|
692 |
|
693 <content> |
|
694 <xul:hbox class="tab-middle box-inherit" xbl:inherits="align,dir,pack,orient,selected" flex="1"> |
|
695 <xul:image class="tab-icon" |
|
696 xbl:inherits="validate,src=image" |
|
697 role="presentation"/> |
|
698 <xul:label class="tab-text" |
|
699 xbl:inherits="value=label,accesskey,crop,disabled" |
|
700 flex="1" |
|
701 role="presentation"/> |
|
702 </xul:hbox> |
|
703 </content> |
|
704 |
|
705 <implementation implements="nsIDOMXULSelectControlItemElement"> |
|
706 <property name="control" readonly="true"> |
|
707 <getter> |
|
708 <![CDATA[ |
|
709 var parent = this.parentNode; |
|
710 if (parent instanceof Components.interfaces.nsIDOMXULSelectControlElement) |
|
711 return parent; |
|
712 return null; |
|
713 ]]> |
|
714 </getter> |
|
715 </property> |
|
716 |
|
717 <property name="selected" readonly="true" |
|
718 onget="return this.getAttribute('selected') == 'true';"/> |
|
719 |
|
720 <property name="_selected"> |
|
721 <setter> |
|
722 <![CDATA[ |
|
723 if (val) |
|
724 this.setAttribute("selected", "true"); |
|
725 else |
|
726 this.removeAttribute("selected"); |
|
727 |
|
728 if (this.previousSibling && this.previousSibling.localName == "tab") { |
|
729 if (val) |
|
730 this.previousSibling.setAttribute("beforeselected", "true"); |
|
731 else |
|
732 this.previousSibling.removeAttribute("beforeselected"); |
|
733 this.removeAttribute("first-tab"); |
|
734 } |
|
735 else |
|
736 this.setAttribute("first-tab", "true"); |
|
737 |
|
738 if (this.nextSibling && this.nextSibling.localName == "tab") { |
|
739 if (val) |
|
740 this.nextSibling.setAttribute("afterselected", "true"); |
|
741 else |
|
742 this.nextSibling.removeAttribute("afterselected"); |
|
743 this.removeAttribute("last-tab"); |
|
744 } |
|
745 else |
|
746 this.setAttribute("last-tab", "true"); |
|
747 return val; |
|
748 ]]> |
|
749 </setter> |
|
750 </property> |
|
751 |
|
752 <property name="linkedPanel" onget="return this.getAttribute('linkedpanel')" |
|
753 onset="this.setAttribute('linkedpanel', val); return val;"/> |
|
754 |
|
755 <field name="arrowKeysShouldWrap" readonly="true"> |
|
756 #ifdef XP_MACOSX |
|
757 true |
|
758 #else |
|
759 false |
|
760 #endif |
|
761 </field> |
|
762 <field name="TelemetryStopwatch" readonly="true"> |
|
763 let tmp = {}; |
|
764 Cu.import("resource://gre/modules/TelemetryStopwatch.jsm", tmp); |
|
765 tmp.TelemetryStopwatch; |
|
766 </field> |
|
767 </implementation> |
|
768 |
|
769 <handlers> |
|
770 <handler event="mousedown" button="0"> |
|
771 <![CDATA[ |
|
772 if (this.disabled) |
|
773 return; |
|
774 |
|
775 if (this != this.parentNode.selectedItem) { // Not selected yet |
|
776 let stopwatchid = this.parentNode.getAttribute("stopwatchid"); |
|
777 if (stopwatchid) { |
|
778 this.TelemetryStopwatch.start(stopwatchid); |
|
779 } |
|
780 |
|
781 // Call this before setting the 'ignorefocus' attribute because this |
|
782 // will pass on focus if the formerly selected tab was focused as well. |
|
783 this.parentNode._selectNewTab(this); |
|
784 |
|
785 var isTabFocused = false; |
|
786 try { |
|
787 isTabFocused = (document.commandDispatcher.focusedElement == this); |
|
788 } catch (e) {} |
|
789 |
|
790 // Set '-moz-user-focus' to 'ignore' so that PostHandleEvent() can't |
|
791 // focus the tab; we only want tabs to be focusable by the mouse if |
|
792 // they are already focused. After a short timeout we'll reset |
|
793 // '-moz-user-focus' so that tabs can be focused by keyboard again. |
|
794 if (!isTabFocused) { |
|
795 this.setAttribute("ignorefocus", "true"); |
|
796 setTimeout(function (tab) tab.removeAttribute("ignorefocus"), 0, this); |
|
797 } |
|
798 |
|
799 if (stopwatchid) { |
|
800 this.TelemetryStopwatch.finish(stopwatchid); |
|
801 } |
|
802 } |
|
803 // Otherwise this tab is already selected and we will fall |
|
804 // through to mousedown behavior which sets focus on the current tab, |
|
805 // Only a click on an already selected tab should focus the tab itself. |
|
806 ]]> |
|
807 </handler> |
|
808 |
|
809 <handler event="keypress" keycode="VK_LEFT"> |
|
810 <![CDATA[ |
|
811 var direction = window.getComputedStyle(this.parentNode, null).direction; |
|
812 this.parentNode.advanceSelectedTab(direction == 'ltr' ? -1 : 1, this.arrowKeysShouldWrap); |
|
813 ]]> |
|
814 </handler> |
|
815 |
|
816 <handler event="keypress" keycode="VK_RIGHT"> |
|
817 <![CDATA[ |
|
818 var direction = window.getComputedStyle(this.parentNode, null).direction; |
|
819 this.parentNode.advanceSelectedTab(direction == 'ltr' ? 1 : -1, this.arrowKeysShouldWrap); |
|
820 ]]> |
|
821 </handler> |
|
822 |
|
823 <handler event="keypress" keycode="VK_UP"> |
|
824 <![CDATA[ |
|
825 this.parentNode.advanceSelectedTab(-1, this.arrowKeysShouldWrap); |
|
826 ]]> |
|
827 </handler> |
|
828 |
|
829 <handler event="keypress" keycode="VK_DOWN"> |
|
830 <![CDATA[ |
|
831 this.parentNode.advanceSelectedTab(1, this.arrowKeysShouldWrap); |
|
832 ]]> |
|
833 </handler> |
|
834 |
|
835 <handler event="keypress" keycode="VK_HOME"> |
|
836 <![CDATA[ |
|
837 this.parentNode._selectNewTab(this.parentNode.childNodes[0]); |
|
838 ]]> |
|
839 </handler> |
|
840 |
|
841 <handler event="keypress" keycode="VK_END"> |
|
842 <![CDATA[ |
|
843 var tabs = this.parentNode.childNodes; |
|
844 this.parentNode._selectNewTab(tabs[tabs.length - 1], -1); |
|
845 ]]> |
|
846 </handler> |
|
847 </handlers> |
|
848 </binding> |
|
849 |
|
850 </bindings> |
|
851 |