summaryrefslogtreecommitdiffstats
path: root/application/basilisk/components/syncedtabs/SyncedTabsDeckView.js
blob: e9efff323666168ece5bcbca9f3f0086e121341c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
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);
    }
  },
};