diff options
Diffstat (limited to 'browser/components/syncedtabs/SyncedTabsDeckView.js')
-rw-r--r-- | browser/components/syncedtabs/SyncedTabsDeckView.js | 116 |
1 files changed, 116 insertions, 0 deletions
diff --git a/browser/components/syncedtabs/SyncedTabsDeckView.js b/browser/components/syncedtabs/SyncedTabsDeckView.js new file mode 100644 index 000000000..e9efff323 --- /dev/null +++ b/browser/components/syncedtabs/SyncedTabsDeckView.js @@ -0,0 +1,116 @@ +/* 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"; + +const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; + +let { getChromeWindow } = Cu.import("resource:///modules/syncedtabs/util.js", {}); + +let log = Cu.import("resource://gre/modules/Log.jsm", {}) + .Log.repository.getLogger("Sync.RemoteTabs"); + +this.EXPORTED_SYMBOLS = [ + "SyncedTabsDeckView" +]; + +/** + * SyncedTabsDeckView + * + * Instances of SyncedTabsDeckView render DOM nodes from a given state. + * No state is kept internaly and the DOM will completely + * rerender unless the state flags `isUpdatable`, which helps + * make small changes without the overhead of a full rerender. + */ +const SyncedTabsDeckView = function (window, tabListComponent, props) { + this.props = props; + + this._window = window; + this._doc = window.document; + + this._tabListComponent = tabListComponent; + this._deckTemplate = this._doc.getElementById("deck-template"); + this.container = this._doc.createElement("div"); +}; + +SyncedTabsDeckView.prototype = { + render(state) { + if (state.isUpdatable) { + this.update(state); + } else { + this.create(state); + } + }, + + create(state) { + let deck = this._doc.importNode(this._deckTemplate.content, true).firstElementChild; + this._clearChilden(); + + let tabListWrapper = this._doc.createElement("div"); + tabListWrapper.className = "tabs-container sync-state"; + this._tabListComponent.init(); + tabListWrapper.appendChild(this._tabListComponent.container); + deck.appendChild(tabListWrapper); + this.container.appendChild(deck); + + this._generateDevicePromo(); + + this._attachListeners(); + this.update(state); + }, + + _getBrowserBundle() { + return getChromeWindow(this._window).document.getElementById("bundle_browser"); + }, + + _generateDevicePromo() { + let bundle = this._getBrowserBundle(); + let formatArgs = ["android", "ios"].map(os => { + let link = this._doc.createElement("a"); + link.textContent = bundle.getString(`appMenuRemoteTabs.mobilePromo.${os}`); + link.className = `${os}-link text-link`; + link.setAttribute("href", "#"); + return link.outerHTML; + }); + // Put it all together... + let contents = bundle.getFormattedString("appMenuRemoteTabs.mobilePromo.text2", formatArgs); + this.container.querySelector(".device-promo").innerHTML = contents; + }, + + destroy() { + this._tabListComponent.uninit(); + this.container.remove(); + }, + + update(state) { + // Note that we may also want to update elements that are outside of the + // deck, so use the document to find the class names rather than our + // container. + for (let panel of state.panels) { + if (panel.selected) { + Array.prototype.map.call(this._doc.getElementsByClassName(panel.id), + item => item.classList.add("selected")); + } else { + Array.prototype.map.call(this._doc.getElementsByClassName(panel.id), + item => item.classList.remove("selected")); + } + } + }, + + _clearChilden() { + while (this.container.firstChild) { + this.container.removeChild(this.container.firstChild); + } + }, + + _attachListeners() { + this.container.querySelector(".android-link").addEventListener("click", this.props.onAndroidClick); + this.container.querySelector(".ios-link").addEventListener("click", this.props.oniOSClick); + let syncPrefLinks = this.container.querySelectorAll(".sync-prefs"); + for (let link of syncPrefLinks) { + link.addEventListener("click", this.props.onSyncPrefClick); + } + }, +}; + |