[tbb-commits] [torbutton/master] Bug 23483: Donation banner on about:tor for 2017
gk at torproject.org
gk at torproject.org
Thu Sep 21 20:59:42 UTC 2017
commit b3ff9863db338b2bd612f109e8bbce4c4af7cbd0
Author: Arthur Edelstein <arthuredelstein at gmail.com>
Date: Mon Sep 18 16:42:07 2017 -0700
Bug 23483: Donation banner on about:tor for 2017
(Also removes a dot from aboutTor.donationBanner.slogan)
---
src/chrome/content/aboutTor/aboutTor-content.js | 5 ++
src/chrome/content/aboutTor/aboutTor.xhtml | 19 +++-
src/chrome/content/aboutTor/donation_banner.js | 105 ++++++++++++++++++++++
src/chrome/content/aboutTor/onion-hand.png | Bin 0 -> 69055 bytes
src/chrome/content/torbutton.js | 4 +-
src/chrome/locale/en/aboutTor.properties | 2 +-
src/chrome/skin/donation_banner.css | 113 ++++++++++++++++++++++++
src/modules/donation-banner.js | 112 +++++++++++++++++++++++
8 files changed, 357 insertions(+), 3 deletions(-)
diff --git a/src/chrome/content/aboutTor/aboutTor-content.js b/src/chrome/content/aboutTor/aboutTor-content.js
index ec515bb..95e8abd 100644
--- a/src/chrome/content/aboutTor/aboutTor-content.js
+++ b/src/chrome/content/aboutTor/aboutTor-content.js
@@ -105,6 +105,11 @@ var AboutTorListener = {
else
body.removeAttribute("showmanual");
+ if (aData.bannerData)
+ body.setAttribute("banner-data", aData.bannerData);
+ else
+ body.removeAttribute("banner-data");
+
// Setting body.initialized="yes" displays the body, which must be done
// at this point because our remaining initialization depends on elements
// being visible so that their size and position are accurate.
diff --git a/src/chrome/content/aboutTor/aboutTor.xhtml b/src/chrome/content/aboutTor/aboutTor.xhtml
index 7ae4b8b..367f9a6 100644
--- a/src/chrome/content/aboutTor/aboutTor.xhtml
+++ b/src/chrome/content/aboutTor/aboutTor.xhtml
@@ -21,6 +21,8 @@
<title>&aboutTor.title;</title>
<link rel="stylesheet" type="text/css" media="all"
href="resource://torbutton/chrome/skin/aboutTor.css"/>
+ <link rel="stylesheet" type="text/css" media="all"
+ href="resource://torbutton/chrome/skin/donation_banner.css"/>
<script type="text/javascript;version=1.7">
<![CDATA[
window.addEventListener("pageshow", function() {
@@ -31,6 +33,21 @@ window.addEventListener("pageshow", function() {
</script>
</head>
<body dir="&locale.dir;">
+ <div id="banner">
+ <div id="banner-contents-container">
+ <div id="banner-tagline"><span></span></div>
+ <div id="banner-slogan"><span></span></div>
+ <a id="banner-donate-button-link"
+ href="https://www.torproject.org/donate/donate-tbb">
+ <div id="banner-donate-button">
+ <div id="banner-donate-button-inner">
+ <span></span>
+ </div>
+ </div>
+ </a>
+ </div>
+ </div>
+ <div id="banner-spacer"></div>
<div id="torstatus" class="top">
<div id="torstatus-version"/>
<div id="torstatus-image"/>
@@ -112,6 +129,6 @@ window.addEventListener("pageshow", function() {
<p>&aboutTor.footer.label;
<a href="&aboutTor.learnMore.link;">&aboutTor.learnMore.label;</a></p>
</div>
-
+ <script src="resource://torbutton/chrome/content/aboutTor/donation_banner.js"></script>
</body>
</html>
diff --git a/src/chrome/content/aboutTor/donation_banner.js b/src/chrome/content/aboutTor/donation_banner.js
new file mode 100644
index 0000000..1c95822
--- /dev/null
+++ b/src/chrome/content/aboutTor/donation_banner.js
@@ -0,0 +1,105 @@
+/* jshint esnext:true */
+
+let sel = selector => document.querySelector(selector);
+
+// Shrink the font size if the text in the given element is overflowing.
+let fitTextInElement = function (element) {
+ element.style.fontSize = "8px";
+ let defaultWidth = element.scrollWidth,
+ defaultHeight = element.scrollHeight;
+ let bestSize;
+ for (let testSize = 8; testSize <= 40; testSize += 0.5) {
+ element.style.fontSize = `${testSize}px`;
+ if (element.scrollWidth <= defaultWidth &&
+ element.scrollHeight <= defaultHeight) {
+ bestSize = testSize;
+ } else {
+ break;
+ }
+ }
+ element.style.fontSize = `${bestSize}px`;
+};
+
+// Increase padding at end to "squeeze" text, until just before
+// it gets squeezed so much that it gets longer vertically.
+let avoidWidows = function (element) {
+ element.style.paddingRight = "0px";
+ let originalWidth = element.scrollWidth;
+ let originalHeight = element.scrollHeight;
+ let bestPadding;
+ for (let testPadding = 0; testPadding < originalWidth; testPadding += 0.5) {
+ element.style.paddingRight = `${testPadding}px`;
+ if (element.scrollHeight <= originalHeight) {
+ bestPadding = testPadding;
+ } else {
+ break;
+ }
+ }
+ element.style.paddingRight = `${bestPadding}px`;
+ if (window.getComputedStyle(element).direction === "rtl") {
+ element.style.paddingLeft = element.style.paddingRight;
+ element.style.paddingRight = "0px";
+ }
+};
+
+// Resize the text inside banner to fit.
+let updateTextSizes = function () {
+ fitTextInElement(sel("#banner-tagline"));
+ fitTextInElement(sel("#banner-slogan"));
+ fitTextInElement(sel("#banner-donate-button-inner"));
+ avoidWidows(sel("#banner-tagline span"));
+};
+
+// Returns a random integer x, such that 0 <= x < max
+let randomInteger = max => Math.floor(max * Math.random());
+
+// The main donation banner function.
+let runDonationBanner = function ({ taglines, slogan, donate, shortLocale }) {
+ try {
+ sel("#banner-tagline span").innerText = taglines[randomInteger(taglines.length)];
+ sel("#banner-slogan span").innerText = slogan;
+ let donateButtonText = sel("#banner-donate-button-inner span");
+ let rtl = window.getComputedStyle(donateButtonText).direction === "rtl";
+ donateButtonText.innerHTML = donate + " " + (rtl ? "◀" : "▶");
+ sel("#banner").style.display = "flex";
+ sel("#banner-spacer").style.display = "block";
+ addEventListener("resize", updateTextSizes);
+ updateTextSizes();
+ // Add a suffix corresponding to locale so we can send user
+ // to a correctly-localized donation page via redirect.
+ sel("#banner-donate-button-link").href += "-" + shortLocale;
+ sel("#torstatus-image").style.display = "none";
+ } catch (e) {
+ // Something went wrong.
+ console.error(e);
+ sel("#banner").style.display = "none";
+ sel("#bannerSpacer").style.display = "none";
+ sel("#torstatus-image").style.display = "block";
+ }
+};
+
+// Calls callback(attributeValue) when the specified attribute changes on
+// target. Returns a zero-arg function that stops observing.
+let observeAttribute = function (target, attributeName, callback) {
+ let observer = new MutationObserver(mutations => {
+ mutations.forEach(mutation => {
+ if (mutation.type === "attributes" &&
+ mutation.attributeName === attributeName) {
+ callback(target.getAttribute(attributeName));
+ }
+ });
+ });
+ observer.observe(target, { attributes: true });
+ return () => observer.disconnect();
+};
+
+// Start the donation banner if "toron" has been set to "yes".
+let stopObserving = observeAttribute(document.body, "toron", value => {
+ stopObserving();
+ if (value === "yes") {
+ let bannerDataJSON = document.body.getAttribute("banner-data");
+ if (bannerDataJSON && bannerDataJSON.length > 0) {
+ runDonationBanner(JSON.parse(bannerDataJSON));
+ }
+ }
+});
diff --git a/src/chrome/content/aboutTor/onion-hand.png b/src/chrome/content/aboutTor/onion-hand.png
new file mode 100644
index 0000000..00a5a41
Binary files /dev/null and b/src/chrome/content/aboutTor/onion-hand.png differ
diff --git a/src/chrome/content/torbutton.js b/src/chrome/content/torbutton.js
index db6b694..955f8eb 100644
--- a/src/chrome/content/torbutton.js
+++ b/src/chrome/content/torbutton.js
@@ -13,6 +13,7 @@ let { showDialog } = Cu.import("resource://torbutton/modules/utils.js", {});
let { unescapeTorString } = Cu.import("resource://torbutton/modules/utils.js", {});
let SecurityPrefs = Cu.import("resource://torbutton/modules/security-prefs.js", {});
let { bindPrefAndInit, observe } = Cu.import("resource://torbutton/modules/utils.js", {});
+let { bannerData } = Cu.import("resource://torbutton/modules/donation-banner.js", {});
const k_tb_last_browser_version_pref = "extensions.torbutton.lastBrowserVersion";
const k_tb_browser_update_needed_pref = "extensions.torbutton.updateNeeded";
@@ -450,7 +451,8 @@ var torbutton_abouttor_message_handler = {
torOn: torbutton_tor_check_ok(),
updateNeeded: torbutton_update_is_needed(),
showManual: torbutton_show_torbrowser_manual(),
- toolbarButtonXPos: torbutton_get_toolbarbutton_xpos()
+ toolbarButtonXPos: torbutton_get_toolbarbutton_xpos(),
+ bannerData: bannerData(),
};
},
diff --git a/src/chrome/locale/en/aboutTor.properties b/src/chrome/locale/en/aboutTor.properties
index 4436e21..d0d3a64 100644
--- a/src/chrome/locale/en/aboutTor.properties
+++ b/src/chrome/locale/en/aboutTor.properties
@@ -10,7 +10,7 @@ aboutTor.searchDDG.search.link=https://duckduckgo.com/
aboutTor.donationBanner.donate=Donate Now!
-aboutTor.donationBanner.slogan=Tor: Powering Digital Resistance.
+aboutTor.donationBanner.slogan=Tor: Powering Digital Resistance
aboutTor.donationBanner.mozilla=Give today and Mozilla will match your gift!
aboutTor.donationBanner.tagline1=Protecting Journalists, Whistleblowers, & Activists Since 2006
diff --git a/src/chrome/skin/donation_banner.css b/src/chrome/skin/donation_banner.css
new file mode 100644
index 0000000..8580066
--- /dev/null
+++ b/src/chrome/skin/donation_banner.css
@@ -0,0 +1,113 @@
+#banner {
+ -khtml-user-select: none; /* Konqueror */
+ -moz-user-select: none; /* Firefox */
+ -ms-user-select: none; /* Internet Explorer/Edge */
+ -webkit-touch-callout: none; /* iOS Safari */
+ -webkit-user-select: none; /* Chrome/Safari/Opera */
+ display: none;
+ height: 150px;
+ justify-content: center;
+ left: 0px;
+ margin-top: 0px;
+ min-width: 900px;
+ opacity: 1;
+ position: absolute;
+ user-select: none;
+ width: 100%;
+ z-index: 1;
+}
+#banner:before {
+ background-color: #406;
+ background-image: url('resource://torbutton/chrome/content/aboutTor/onion-hand.png');
+ background-position: center;
+ background-size: cover;
+ content: "";
+ height: 150px;
+ left: 0px;
+ position: absolute;
+ top: 0px;
+ width: 100%;
+}
+#banner:-moz-dir(rtl):before {
+ transform: scaleX(-1);
+}
+#banner-contents-container {
+ align-items: center;
+ height: 100%;
+ max-width: 700px;
+ position: relative;
+ width: 700px;
+}
+#banner-tagline {
+ align-items: center;
+ bottom: 60px;
+ color: white;
+ display: flex;
+ font-family: monospace;
+ font-size: 8px;
+ font-weight: bold;
+ left: 85px;
+ position: absolute;
+ right: 0px;
+ text-align: start;
+ text-transform: uppercase;
+ top: 10px;
+}
+#banner-tagline:-moz-dir(rtl) {
+ left: 0px;
+ right: 85px;
+}
+#banner-slogan {
+ align-items: start;
+ bottom: 0px;
+ color: #f8f8a0;
+ display: flex;
+ font-family: monospace;
+ font-weight: bold;
+ left: 85px;
+ position: absolute;
+ right: 285px;
+ text-align: start;
+ top: 100px;
+ white-space: nowrap;
+}
+#banner-slogan:-moz-dir(rtl) {
+ left: 285px;
+ right: 85px;
+}
+#banner-donate-button {
+ background-color: #13a513;
+ border: 0px;
+ bottom: 10px;
+ color: #fbf7ef;
+ font-family: sans-serif;
+ font-size: 12px;
+ font-weight: bold;
+ left: 430px;
+ letter-spacing: -0.00em;
+ position: absolute;
+ right: 0px;
+ top: 100px;
+}
+#banner-donate-button:-moz-dir(rtl) {
+ left: 0px;
+ right: 430px;
+}
+#banner-donate-button:hover {
+ background-color: #38bc38;
+}
+#banner-donate-button-inner {
+ bottom: 6px;
+ display: flex;
+ justify-content: center;
+ left: 8px;
+ position: absolute;
+ right: 8px;
+ top: 6px;
+}
+#banner-spacer {
+ display: none;
+ height: 150px;
+ position: relative;
+ top: 0;
+}
diff --git a/src/modules/donation-banner.js b/src/modules/donation-banner.js
new file mode 100644
index 0000000..bb35e86
--- /dev/null
+++ b/src/modules/donation-banner.js
@@ -0,0 +1,112 @@
+/* jshint esversion:6 */
+
+const Cu = Components.utils;
+
+// ### Import Mozilla Services
+Cu.import("resource://gre/modules/Services.jsm");
+
+// A list of locales for which the banner has been translated.
+const kBannerLocales = [
+ "bg",
+ "da",
+ "el",
+ "en",
+ "es",
+ "fr",
+ "fr_CA",
+ "is",
+ "it",
+ "nb",
+ "tr",
+];
+
+// A list of donation page locales (at least redirects should exist).
+const kDonationPageLocales = [
+ "ar",
+ "de",
+ "en",
+ "es",
+ "fa",
+ "fr",
+ "it",
+ "ja",
+ "ko",
+ "nl",
+ "pl",
+ "pt",
+ "ru",
+ "tr",
+ "vi",
+ "zh",
+];
+
+const kPropertiesURL = "chrome://torbutton/locale/aboutTor.properties";
+const gStringBundle = Services.strings.createBundle(kPropertiesURL);
+
+// Check if we should show the banner, depends on
+// browser locale, current date, and how many times
+// we have already shown the banner.
+const shouldShowBanner = function ({ locale, shortLocale }) {
+ try {
+ // If our override test pref is true, then just show the banner regardless.
+ if (Services.prefs.getBoolPref("extensions.torbutton.testBanner", false)) {
+ return true;
+ }
+ // Don't show a banner if update is needed.
+ let updateNeeded = Services.prefs.getBoolPref("extensions.torbutton.updateNeeded");
+ if (updateNeeded) {
+ return false;
+ }
+ // Only show banner when we have that locale and if a donation redirect exists.
+ if (kBannerLocales.indexOf(locale) === -1 ||
+ kDonationPageLocales.indexOf(shortLocale) === -1) {
+ return false;
+ }
+ // Only show banner between 2017 Oct 23 and 2018 Jan 25.
+ let now = new Date();
+ let start = new Date(2017, 9, 23);
+ let end = new Date(2018, 0, 26);
+ let shownCountPref = "extensions.torbutton.donation_banner2017.shown_count";
+ if (now < start || now > end) {
+ // Clean up pref if not in use.
+ Services.prefs.clearUserPref(shownCountPref);
+ return false;
+ }
+ // Only show banner 50 times.
+ let count = 0;
+ if (Services.prefs.prefHasUserValue(shownCountPref)) {
+ count = Services.prefs.getIntPref(shownCountPref);
+ }
+ if (count >= 50) {
+ return false;
+ }
+ Services.prefs.setIntPref(shownCountPref, count+1);
+ return true;
+ } catch (e) {
+ return false;
+ }
+};
+
+// Read data needed for displaying banner on page.
+var bannerData = function () {
+ // Read short locale.
+ let locale = Services.prefs.getCharPref("general.useragent.locale");
+ let shortLocale = locale.match(/[a-zA-Z]+/)[0].toLowerCase();
+ if (!shouldShowBanner({ locale, shortLocale })) {
+ return null;
+ }
+ // Load tag lines.
+ let taglines = [];
+ for (let index = 0; index < 5; ++index) {
+ let tagline = gStringBundle.GetStringFromName(
+ "aboutTor.donationBanner.tagline" + (index + 1));
+ taglines.push(tagline);
+ }
+ // Read slogan and donate button text.
+ let slogan = gStringBundle.GetStringFromName("aboutTor.donationBanner.slogan");
+ let donate = gStringBundle.GetStringFromName("aboutTor.donationBanner.donate");
+ return JSON.stringify({ taglines, slogan, donate, shortLocale });
+};
+
+// Export utility functions for external use.
+var EXPORTED_SYMBOLS = ["bannerData"];
More information about the tbb-commits
mailing list