forked from mirrors/gecko-dev
Differential Revision: https://phabricator.services.mozilla.com/D17526 --HG-- extra : moz-landing-system : lando
263 lines
6.9 KiB
JavaScript
263 lines
6.9 KiB
JavaScript
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
"use strict";
|
|
|
|
// This is loaded into chrome windows with the subscript loader. Wrap in
|
|
// a block to prevent accidentally leaking globals onto `window`.
|
|
{
|
|
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
|
|
|
class MozTabbox extends MozXULElement {
|
|
constructor() {
|
|
super();
|
|
this._handleMetaAltArrows = /Mac/.test(navigator.platform);
|
|
this.disconnectedCallback = this.disconnectedCallback.bind(this);
|
|
}
|
|
|
|
connectedCallback() {
|
|
Services.els.addSystemEventListener(document, "keydown", this, false);
|
|
window.addEventListener("unload", this.disconnectedCallback, { once: true });
|
|
}
|
|
|
|
disconnectedCallback() {
|
|
window.removeEventListener("unload", this.disconnectedCallback);
|
|
Services.els.removeSystemEventListener(document, "keydown", this, false);
|
|
}
|
|
|
|
set handleCtrlTab(val) {
|
|
this.setAttribute("handleCtrlTab", val);
|
|
return val;
|
|
}
|
|
|
|
get handleCtrlTab() {
|
|
return (this.getAttribute("handleCtrlTab") != "false");
|
|
}
|
|
/**
|
|
* _tabs and _tabpanels are deprecated, they exist only for
|
|
* backwards compatibility.
|
|
*/
|
|
get _tabs() {
|
|
return this.tabs;
|
|
}
|
|
|
|
get _tabpanels() {
|
|
return this.tabpanels;
|
|
}
|
|
|
|
get tabs() {
|
|
if (this.hasAttribute("tabcontainer")) {
|
|
return document.getElementById(this.getAttribute("tabcontainer"));
|
|
}
|
|
return this.getElementsByTagNameNS(
|
|
"http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
|
|
"tabs").item(0);
|
|
}
|
|
|
|
get tabpanels() {
|
|
return this.getElementsByTagNameNS(
|
|
"http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
|
|
"tabpanels").item(0);
|
|
}
|
|
|
|
set selectedIndex(val) {
|
|
let tabs = this.tabs;
|
|
if (tabs)
|
|
tabs.selectedIndex = val;
|
|
this.setAttribute("selectedIndex", val);
|
|
return val;
|
|
}
|
|
|
|
get selectedIndex() {
|
|
let tabs = this.tabs;
|
|
return tabs ? tabs.selectedIndex : -1;
|
|
}
|
|
|
|
set selectedTab(val) {
|
|
if (val) {
|
|
let tabs = this.tabs;
|
|
if (tabs)
|
|
tabs.selectedItem = val;
|
|
}
|
|
return val;
|
|
}
|
|
|
|
get selectedTab() {
|
|
let tabs = this.tabs;
|
|
return tabs && tabs.selectedItem;
|
|
}
|
|
|
|
set selectedPanel(val) {
|
|
if (val) {
|
|
let tabpanels = this.tabpanels;
|
|
if (tabpanels)
|
|
tabpanels.selectedPanel = val;
|
|
}
|
|
return val;
|
|
}
|
|
|
|
get selectedPanel() {
|
|
let tabpanels = this.tabpanels;
|
|
return tabpanels && tabpanels.selectedPanel;
|
|
}
|
|
|
|
handleEvent(event) {
|
|
if (!event.isTrusted) {
|
|
// Don't let untrusted events mess with tabs.
|
|
return;
|
|
}
|
|
|
|
// Don't check if the event was already consumed because tab
|
|
// navigation should always work for better user experience.
|
|
|
|
switch (event.keyCode) {
|
|
case event.DOM_VK_TAB:
|
|
if (event.ctrlKey && !event.altKey && !event.metaKey)
|
|
if (this.tabs && this.handleCtrlTab) {
|
|
this.tabs.advanceSelectedTab(event.shiftKey ? -1 : 1, true);
|
|
event.preventDefault();
|
|
}
|
|
break;
|
|
case event.DOM_VK_PAGE_UP:
|
|
if (event.ctrlKey && !event.shiftKey && !event.altKey && !event.metaKey &&
|
|
this.tabs) {
|
|
this.tabs.advanceSelectedTab(-1, true);
|
|
event.preventDefault();
|
|
}
|
|
break;
|
|
case event.DOM_VK_PAGE_DOWN:
|
|
if (event.ctrlKey && !event.shiftKey && !event.altKey && !event.metaKey &&
|
|
this.tabs) {
|
|
this.tabs.advanceSelectedTab(1, true);
|
|
event.preventDefault();
|
|
}
|
|
break;
|
|
case event.DOM_VK_LEFT:
|
|
if (event.metaKey && event.altKey && !event.shiftKey && !event.ctrlKey)
|
|
if (this.tabs && this._handleMetaAltArrows) {
|
|
let offset = window.getComputedStyle(this)
|
|
.direction == "ltr" ? -1 : 1;
|
|
this.tabs.advanceSelectedTab(offset, true);
|
|
event.preventDefault();
|
|
}
|
|
break;
|
|
case event.DOM_VK_RIGHT:
|
|
if (event.metaKey && event.altKey && !event.shiftKey && !event.ctrlKey)
|
|
if (this.tabs && this._handleMetaAltArrows) {
|
|
let offset = window.getComputedStyle(this)
|
|
.direction == "ltr" ? 1 : -1;
|
|
this.tabs.advanceSelectedTab(offset, true);
|
|
event.preventDefault();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
customElements.define("tabbox", MozTabbox);
|
|
|
|
class MozTabpanels extends MozXULElement {
|
|
connectedCallback() {
|
|
if (this.delayConnectedCallback()) {
|
|
return;
|
|
}
|
|
|
|
this._tabbox = null;
|
|
this._selectedPanel = this.children.item(this.selectedIndex);
|
|
}
|
|
|
|
get tabbox() {
|
|
// Memoize the result rather than replacing this getter, so that
|
|
// it can be reset if the parent changes.
|
|
if (this._tabbox) {
|
|
return this._tabbox;
|
|
}
|
|
|
|
let parent = this.parentNode;
|
|
while (parent) {
|
|
if (parent.localName == "tabbox") {
|
|
break;
|
|
}
|
|
parent = parent.parentNode;
|
|
}
|
|
|
|
return this._tabbox = parent;
|
|
}
|
|
|
|
set selectedIndex(val) {
|
|
if (val < 0 || val >= this.children.length)
|
|
return val;
|
|
|
|
let panel = this._selectedPanel;
|
|
this._selectedPanel = this.children[val];
|
|
|
|
if (this.getAttribute("async") != "true") {
|
|
this.setAttribute("selectedIndex", val);
|
|
}
|
|
|
|
if (this._selectedPanel != panel) {
|
|
let event = document.createEvent("Events");
|
|
event.initEvent("select", true, true);
|
|
this.dispatchEvent(event);
|
|
}
|
|
return val;
|
|
}
|
|
|
|
get selectedIndex() {
|
|
let indexStr = this.getAttribute("selectedIndex");
|
|
return indexStr ? parseInt(indexStr) : -1;
|
|
}
|
|
|
|
set selectedPanel(val) {
|
|
let selectedIndex = -1;
|
|
for (let panel = val; panel != null; panel = panel.previousElementSibling)
|
|
++selectedIndex;
|
|
this.selectedIndex = selectedIndex;
|
|
return val;
|
|
}
|
|
|
|
get selectedPanel() {
|
|
return this._selectedPanel;
|
|
}
|
|
|
|
/**
|
|
* nsIDOMXULRelatedElement
|
|
*/
|
|
getRelatedElement(aTabPanelElm) {
|
|
if (!aTabPanelElm)
|
|
return null;
|
|
|
|
let tabboxElm = this.tabbox;
|
|
if (!tabboxElm)
|
|
return null;
|
|
|
|
let tabsElm = tabboxElm.tabs;
|
|
if (!tabsElm)
|
|
return null;
|
|
|
|
// Return tab element having 'linkedpanel' attribute equal to the id
|
|
// of the tab panel or the same index as the tab panel element.
|
|
let tabpanelIdx = Array.indexOf(this.children, aTabPanelElm);
|
|
if (tabpanelIdx == -1)
|
|
return null;
|
|
|
|
let tabElms = tabsElm.children;
|
|
let tabElmFromIndex = tabElms[tabpanelIdx];
|
|
|
|
let tabpanelId = aTabPanelElm.id;
|
|
if (tabpanelId) {
|
|
for (let idx = 0; idx < tabElms.length; idx++) {
|
|
let tabElm = tabElms[idx];
|
|
if (tabElm.linkedPanel == tabpanelId)
|
|
return tabElm;
|
|
}
|
|
}
|
|
|
|
return tabElmFromIndex;
|
|
}
|
|
}
|
|
|
|
MozXULElement.implementCustomInterface(MozTabpanels, [Ci.nsIDOMXULRelatedElement]);
|
|
customElements.define("tabpanels", MozTabpanels);
|
|
}
|