/* 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 {AppConstants} = ChromeUtils.import("resource://gre/modules/AppConstants.jsm"); const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm"); const kDTDs = [ "chrome://global/locale/wizard.dtd" ]; class MozWizard extends MozXULElement { constructor() { super(); this._accessMethod = null; this._currentPage = null; this._canAdvance = true; this._canRewind = false; this._hasLoaded = false; this._pageStack = []; this._bundle = Services.strings.createBundle("chrome://global/locale/wizard.properties"); this.addEventListener("keypress", (event) => { if (event.keyCode == KeyEvent.DOM_VK_RETURN) { this._hitEnter(event); } else if (event.keyCode == KeyEvent.DOM_VK_ESCAPE && !event.defaultPrevented) { this.cancel(); } }, { mozSystemGroup: true }); this.attachShadow({ mode: "open" }).appendChild( MozXULElement.parseXULToFragment(` `)); this.initializeAttributeInheritance(); this._deck = this.shadowRoot.querySelector(".wizard-page-box"); this._wizardButtons = this.shadowRoot.querySelector(".wizard-buttons"); this._wizardHeader = this.shadowRoot.querySelector(".wizard-header"); this._wizardHeader.appendChild( MozXULElement.parseXULToFragment(AppConstants.platform == "macosx" ? ` ` : ` ` ) ); } static get inheritedAttributes() { return { ".wizard-buttons": "pagestep,firstpage,lastpage", }; } connectedCallback() { if (this.delayConnectedCallback()) { return; } this.pageCount = this.wizardPages.length; this._initPages(); this.advance(); // start off on the first page window.addEventListener("close", (event) => { if (document.documentElement.cancel()) { event.preventDefault(); } }); // Give focus to the first focusable element in the wizard, do it after // onload completes, see bug 103197. window.addEventListener("load", () => window.setTimeout(() => { document.documentElement._hasLoaded = true; if (!document.commandDispatcher.focusedElement) { document.commandDispatcher.advanceFocusIntoSubtree(this); } try { let button = document.documentElement._wizardButtons.defaultButton; if (button) { window.notifyDefaultButtonLoaded(button); } } catch (e) {} }, 0)); } set title(val) { return document.title = val; } get title() { return document.title; } set canAdvance(val) { this.getButton("next").disabled = !val; return this._canAdvance = val; } get canAdvance() { return this._canAdvance; } set canRewind(val) { this.getButton("back").disabled = !val; return this._canRewind = val; } get canRewind() { return this._canRewind; } get pageStep() { return this._pageStack.length; } get wizardPages() { const xulns = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; return this.getElementsByTagNameNS(xulns, "wizardpage"); } set currentPage(val) { if (!val) return val; this._currentPage = val; // Setting this attribute allows wizard's clients to dynamically // change the styles of each page based on purpose of the page. this.setAttribute("currentpageid", val.pageid); if (this.onFirstPage) { this.canRewind = false; this.setAttribute("firstpage", "true"); if (AppConstants.platform == "linux") { this.getButton("back").setAttribute("hidden", "true"); } } else { this.canRewind = true; this.setAttribute("firstpage", "false"); if (AppConstants.platform == "linux") { this.getButton("back").setAttribute("hidden", "false"); } } if (this.onLastPage) { this.canAdvance = true; this.setAttribute("lastpage", "true"); } else { this.setAttribute("lastpage", "false"); } this._deck.setAttribute("selectedIndex", val.pageIndex); this._advanceFocusToPage(val); this._adjustWizardHeader(); this._wizardButtons.onPageChange(); this._fireEvent(val, "pageshow"); return val; } get currentPage() { return this._currentPage; } set pageIndex(val) { if (val < 0 || val >= this.pageCount) return val; var page = this.wizardPages[val]; this._pageStack[this._pageStack.length - 1] = page; this.currentPage = page; return val; } get pageIndex() { return this._currentPage ? this._currentPage.pageIndex : -1; } get onFirstPage() { return this._pageStack.length == 1; } get onLastPage() { var cp = this.currentPage; return cp && ((this._accessMethod == "sequential" && cp.pageIndex == this.pageCount - 1) || (this._accessMethod == "random" && cp.next == "")); } getButton(aDlgType) { return this._wizardButtons.getButton(aDlgType); } getPageById(aPageId) { var els = this.getElementsByAttribute("pageid", aPageId); return els.item(0); } extra1() { if (this.currentPage) this._fireEvent(this.currentPage, "extra1"); } extra2() { if (this.currentPage) this._fireEvent(this.currentPage, "extra2"); } rewind() { if (!this.canRewind) return; if (this.currentPage && !this._fireEvent(this.currentPage, "pagehide")) return; if (this.currentPage && !this._fireEvent(this.currentPage, "pagerewound")) return; if (!this._fireEvent(this, "wizardback")) return; this._pageStack.pop(); this.currentPage = this._pageStack[this._pageStack.length - 1]; this.setAttribute("pagestep", this._pageStack.length); } advance(aPageId) { if (!this.canAdvance) return; if (this.currentPage && !this._fireEvent(this.currentPage, "pagehide")) return; if (this.currentPage && !this._fireEvent(this.currentPage, "pageadvanced")) return; if (this.onLastPage && !aPageId) { if (this._fireEvent(this, "wizardfinish")) window.setTimeout(function() { window.close(); }, 1); } else { if (!this._fireEvent(this, "wizardnext")) return; let page; if (aPageId) { page = this.getPageById(aPageId); } else if (this.currentPage) { if (this._accessMethod == "random") { page = this.getPageById(this.currentPage.next); } else { page = this.wizardPages[this.currentPage.pageIndex + 1]; } } else { page = this.wizardPages[0]; } if (page) { this._pageStack.push(page); this.setAttribute("pagestep", this._pageStack.length); this.currentPage = page; } } } goTo(aPageId) { var page = this.getPageById(aPageId); if (page) { this._pageStack[this._pageStack.length - 1] = page; this.currentPage = page; } } cancel() { if (!this._fireEvent(this, "wizardcancel")) return true; window.close(); window.setTimeout(function() { window.close(); }, 1); return false; } _advanceFocusToPage(aPage) { if (!this._hasLoaded) return; // XXX: it'd be correct to advance focus into the panel, however we can't do // it until bug 1558990 is fixed, so moving the focus into a wizard itsef // as a workaround - it's same behavior but less optimal. document.commandDispatcher.advanceFocusIntoSubtree(this); // if advanceFocusIntoSubtree tries to focus one of our // dialog buttons, then remove it and put it on the root var focused = document.commandDispatcher.focusedElement; if (focused && focused.hasAttribute("dlgtype")) this.focus(); } _initPages() { var meth = "sequential"; var pages = this.wizardPages; for (var i = 0; i < pages.length; ++i) { var page = pages[i]; page.pageIndex = i; if (page.next != "") meth = "random"; } this._accessMethod = meth; } _adjustWizardHeader() { var label = this.currentPage.getAttribute("label"); if (!label && this.onFirstPage && this._bundle) { if (AppConstants.platform == "macosx") { label = this._bundle.GetStringFromName("default-first-title-mac"); } else { label = this._bundle.formatStringFromName("default-first-title", [this.title]); } } else if (!label && this.onLastPage && this._bundle) { if (AppConstants.platform == "macosx") { label = this._bundle.GetStringFromName("default-last-title-mac"); } else { label = this._bundle.formatStringFromName("default-last-title", [this.title]); } } this._wizardHeader. querySelector(".wizard-header-label").textContent = label; let headerDescEl = this._wizardHeader.querySelector(".wizard-header-description"); if (headerDescEl) { headerDescEl.textContent = this.currentPage.getAttribute("description"); } } _hitEnter(evt) { if (!evt.defaultPrevented) this.advance(); } _fireEvent(aTarget, aType) { var event = document.createEvent("Events"); event.initEvent(aType, true, true); // handle dom event handlers return aTarget.dispatchEvent(event); } } customElements.define("wizard", MozWizard); class MozWizardPage extends MozXULElement { constructor() { super(); this.pageIndex = -1; } connectedCallback() { this.setAttribute("slot", "wizardpage"); } get pageid() { return this.getAttribute("pageid"); } set pageid(val) { this.setAttribute("pageid", val); } get next() { return this.getAttribute("next"); } set next(val) { this.setAttribute("next", val); this.parentNode._accessMethod = "random"; return val; } } customElements.define("wizardpage", MozWizardPage); class MozWizardButtons extends MozXULElement { connectedCallback() { this.textContent = ""; this.appendChild(MozXULElement.parseXULToFragment(this._markup, kDTDs)); this._wizardButtonDeck = this.querySelector(".wizard-next-deck"); this.initializeAttributeInheritance(); const listeners = [ ["back", () => document.documentElement.rewind()], ["next", () => document.documentElement.advance()], ["finish", () => document.documentElement.advance()], ["cancel", () => document.documentElement.cancel()], ["extra1", () => document.documentElement.extra1()], ["extra2", () => document.documentElement.extra2()], ]; for (let [name, listener] of listeners) { let btn = this.getButton(name); if (btn) { btn.addEventListener("command", listener); } } } static get inheritedAttributes() { return AppConstants.platform == "macosx" ? { "[dlgtype='next']": "hidden=lastpage", } : null; } get _markup() { if (AppConstants.platform == "macosx") { return `