forked from mirrors/gecko-dev
		
	Differential Revision: https://phabricator.services.mozilla.com/D48338 --HG-- extra : moz-landing-system : lando
		
			
				
	
	
		
			223 lines
		
	
	
	
		
			7.5 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			223 lines
		
	
	
	
		
			7.5 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/. */
 | 
						|
 | 
						|
/* globals log, catcher, onboardingHtml, onboardingCss, util, shooter, callBackground, assertIsTrusted, assertIsBlankDocument */
 | 
						|
 | 
						|
"use strict";
 | 
						|
 | 
						|
this.slides = (function() {
 | 
						|
  const exports = {};
 | 
						|
 | 
						|
  const { watchFunction } = catcher;
 | 
						|
 | 
						|
  let iframe;
 | 
						|
  let doc;
 | 
						|
  let currentSlide = 1;
 | 
						|
  let numberOfSlides;
 | 
						|
  let callbacks;
 | 
						|
 | 
						|
  exports.display = function(addCallbacks) {
 | 
						|
    if (iframe) {
 | 
						|
      throw new Error("Attemted to call slides.display() twice");
 | 
						|
    }
 | 
						|
    return new Promise((resolve, reject) => {
 | 
						|
      callbacks = addCallbacks;
 | 
						|
      // FIXME: a lot of this iframe logic is in ui.js; maybe move to util.js
 | 
						|
      iframe = document.createElement("iframe");
 | 
						|
      iframe.src = browser.extension.getURL("blank.html");
 | 
						|
      iframe.id = "firefox-screenshots-onboarding-iframe";
 | 
						|
      iframe.style.zIndex = "99999999999";
 | 
						|
      iframe.style.display = "block";
 | 
						|
      iframe.style.border = "none";
 | 
						|
      iframe.style.position = "fixed";
 | 
						|
      iframe.style.top = "0";
 | 
						|
      iframe.style.left = "0";
 | 
						|
      iframe.style.margin = "0";
 | 
						|
      iframe.style.backgroundColor = "transparent";
 | 
						|
      iframe.scrolling = "no";
 | 
						|
      updateIframeSize();
 | 
						|
      let html = onboardingHtml.replace("<style></style>", `<style>${onboardingCss}</style>`);
 | 
						|
      html = html.replace(/MOZ_EXTENSION([^"]+)/g, (match, filename) => {
 | 
						|
        return browser.extension.getURL(filename);
 | 
						|
      });
 | 
						|
      iframe.addEventListener("load", catcher.watchFunction(() => {
 | 
						|
        doc = iframe.contentDocument;
 | 
						|
        assertIsBlankDocument(doc);
 | 
						|
        const parsedDom = (new doc.defaultView.DOMParser()).parseFromString(
 | 
						|
          html,
 | 
						|
          "text/html"
 | 
						|
        );
 | 
						|
        doc.replaceChild(
 | 
						|
          doc.adoptNode(parsedDom.documentElement),
 | 
						|
          doc.documentElement
 | 
						|
        );
 | 
						|
        doc.addEventListener("keyup", onKeyUp);
 | 
						|
        doc.documentElement.dir = browser.i18n.getMessage("@@bidi_dir");
 | 
						|
        doc.documentElement.lang = browser.i18n.getMessage("@@ui_locale");
 | 
						|
        localizeText(doc);
 | 
						|
        activateSlide(doc);
 | 
						|
        // Give the DOM a moment to settle before applying focus
 | 
						|
        setTimeout(() => {
 | 
						|
          iframe.contentWindow.focus();
 | 
						|
        });
 | 
						|
        resolve();
 | 
						|
      }), {once: true});
 | 
						|
      document.body.appendChild(iframe);
 | 
						|
      window.addEventListener("resize", onResize);
 | 
						|
    });
 | 
						|
  };
 | 
						|
 | 
						|
  exports.remove = exports.unload = function() {
 | 
						|
    window.removeEventListener("resize", onResize);
 | 
						|
    if (doc) {
 | 
						|
      doc.removeEventListener("keyup", onKeyUp);
 | 
						|
    }
 | 
						|
    util.removeNode(iframe);
 | 
						|
    iframe = doc = null;
 | 
						|
    currentSlide = 1;
 | 
						|
    numberOfSlides = undefined;
 | 
						|
    callbacks = undefined;
 | 
						|
  };
 | 
						|
 | 
						|
  function localizeText(doc) {
 | 
						|
    let els = doc.querySelectorAll("[data-l10n-id]");
 | 
						|
    for (const el of els) {
 | 
						|
      const id = el.getAttribute("data-l10n-id");
 | 
						|
      const text = browser.i18n.getMessage(id);
 | 
						|
      el.textContent = text;
 | 
						|
    }
 | 
						|
    els = doc.querySelectorAll("[data-l10n-label-id]");
 | 
						|
    for (const el of els) {
 | 
						|
      const id = el.getAttribute("data-l10n-label-id");
 | 
						|
      const text = browser.i18n.getMessage(id);
 | 
						|
      el.setAttribute("aria-label", text);
 | 
						|
    }
 | 
						|
    // termsAndPrivacyNoticeCloudServices is a more complicated substitution:
 | 
						|
    const termsContainer = doc.querySelector(".onboarding-legal-notice");
 | 
						|
    termsContainer.innerHTML = "";
 | 
						|
    const termsSentinel = "__TERMS__";
 | 
						|
    const privacySentinel = "__PRIVACY__";
 | 
						|
    const sentinelSplitter = "!!!";
 | 
						|
    const linkTexts = {
 | 
						|
      [termsSentinel]: browser.i18n.getMessage("termsAndPrivacyNoticeTermsLink"),
 | 
						|
      [privacySentinel]: browser.i18n.getMessage("termsAndPrivacyNoticyPrivacyLink"),
 | 
						|
    };
 | 
						|
    const linkUrls = {
 | 
						|
      [termsSentinel]: "https://www.mozilla.org/about/legal/terms/services/",
 | 
						|
      [privacySentinel]: "https://www.mozilla.org/privacy/firefox/",
 | 
						|
    };
 | 
						|
    const text = browser.i18n.getMessage(
 | 
						|
      "termsAndPrivacyNotice2",
 | 
						|
      [sentinelSplitter + termsSentinel + sentinelSplitter,
 | 
						|
       sentinelSplitter + privacySentinel + sentinelSplitter]);
 | 
						|
    const parts = text.split(sentinelSplitter);
 | 
						|
    for (const part of parts) {
 | 
						|
      let el;
 | 
						|
      if (part === termsSentinel || part === privacySentinel) {
 | 
						|
        el = doc.createElement("a");
 | 
						|
        el.href = linkUrls[part];
 | 
						|
        el.textContent = linkTexts[part];
 | 
						|
        el.target = "_blank";
 | 
						|
        el.id = (part === termsSentinel) ? "terms" : "privacy";
 | 
						|
      } else {
 | 
						|
        el = doc.createTextNode(part);
 | 
						|
      }
 | 
						|
      termsContainer.appendChild(el);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  function activateSlide(doc) {
 | 
						|
    numberOfSlides = parseInt(doc.querySelector("[data-number-of-slides]").getAttribute("data-number-of-slides"), 10);
 | 
						|
    doc.querySelector("#next").addEventListener("click", watchFunction(assertIsTrusted(() => {
 | 
						|
      shooter.sendEvent("navigate-slide", "next");
 | 
						|
      next();
 | 
						|
    })));
 | 
						|
    doc.querySelector("#prev").addEventListener("click", watchFunction(assertIsTrusted(() => {
 | 
						|
      shooter.sendEvent("navigate-slide", "prev");
 | 
						|
      prev();
 | 
						|
    })));
 | 
						|
    for (const el of doc.querySelectorAll(".goto-slide")) {
 | 
						|
      el.addEventListener("click", watchFunction(assertIsTrusted((event) => {
 | 
						|
        shooter.sendEvent("navigate-slide", "goto");
 | 
						|
        const el = event.target;
 | 
						|
        const index = parseInt(el.getAttribute("data-number"), 10);
 | 
						|
        setSlide(index);
 | 
						|
      })));
 | 
						|
    }
 | 
						|
    doc.querySelector("#skip").addEventListener("click", watchFunction(assertIsTrusted((event) => {
 | 
						|
      shooter.sendEvent("cancel-slides", "skip");
 | 
						|
      callbacks.onEnd();
 | 
						|
    })));
 | 
						|
    doc.querySelector("#done").addEventListener("click", watchFunction(assertIsTrusted((event) => {
 | 
						|
      shooter.sendEvent("finish-slides", "done");
 | 
						|
      callbacks.onEnd();
 | 
						|
    })));
 | 
						|
    doc.querySelector("#slide-overlay").addEventListener("click", watchFunction(assertIsTrusted((event) => {
 | 
						|
      if (event.target === doc.querySelector("#slide-overlay")) {
 | 
						|
        shooter.sendEvent("cancel-slides", "background-click");
 | 
						|
        callbacks.onEnd();
 | 
						|
      }
 | 
						|
    })));
 | 
						|
    setSlide(1);
 | 
						|
  }
 | 
						|
 | 
						|
  function next() {
 | 
						|
    setSlide(currentSlide + 1);
 | 
						|
  }
 | 
						|
 | 
						|
  function prev() {
 | 
						|
    setSlide(currentSlide - 1);
 | 
						|
  }
 | 
						|
 | 
						|
  const onResize = catcher.watchFunction(function() {
 | 
						|
    if (!iframe) {
 | 
						|
      log.warn("slides onResize called when iframe is not setup");
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    updateIframeSize();
 | 
						|
  });
 | 
						|
 | 
						|
  function updateIframeSize() {
 | 
						|
    iframe.style.height = window.innerHeight + "px";
 | 
						|
    iframe.style.width = window.innerWidth + "px";
 | 
						|
  }
 | 
						|
 | 
						|
  const onKeyUp = catcher.watchFunction(assertIsTrusted(function(event) {
 | 
						|
    if ((event.key || event.code) === "Escape") {
 | 
						|
      shooter.sendEvent("cancel-slides", "keyboard-escape");
 | 
						|
      callbacks.onEnd();
 | 
						|
    }
 | 
						|
    if ((event.key || event.code) === "ArrowRight") {
 | 
						|
      shooter.sendEvent("navigate-slide", "keyboard-arrowright");
 | 
						|
      next();
 | 
						|
    }
 | 
						|
    if ((event.key || event.code) === "ArrowLeft") {
 | 
						|
      shooter.sendEvent("navigate-slide", "keyboard-arrowleft");
 | 
						|
      prev();
 | 
						|
    }
 | 
						|
  }));
 | 
						|
 | 
						|
  function setSlide(index) {
 | 
						|
    if (index < 1) {
 | 
						|
      index = 1;
 | 
						|
    }
 | 
						|
    if (index > numberOfSlides) {
 | 
						|
      index = numberOfSlides;
 | 
						|
    }
 | 
						|
    shooter.sendEvent("visited-slide", `slide-${index}`);
 | 
						|
    currentSlide = index;
 | 
						|
    const slideEl = doc.querySelector("#slide-container");
 | 
						|
    for (let i = 1; i <= numberOfSlides; i++) {
 | 
						|
      const className = `active-slide-${i}`;
 | 
						|
      if (i === currentSlide) {
 | 
						|
        slideEl.classList.add(className);
 | 
						|
      } else {
 | 
						|
        slideEl.classList.remove(className);
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  return exports;
 | 
						|
})();
 | 
						|
null;
 |