зеркало из
1
0
Форкнуть 0

Updated the top advertisers table in info page. Fixed minor typos.

This commit is contained in:
Jason Chuang 2018-10-18 15:36:26 -04:00
Родитель 1e8b4accb7
Коммит f10a11bb2b
4 изменённых файлов: 217 добавлений и 106 удалений

Просмотреть файл

@ -259,7 +259,7 @@ const [AppDoorHanger] = (function() {
.text(" ");
firstRow.append("span")
.attr("class", "count")
.text(d => `(${d.count} times)`);
.text(d => d.count === 1 ? "(1 time)" : `(${d.count} times)`);
secondRow.append("span")
.attr("class", "bar")
.style("width", barWidth);

Просмотреть файл

@ -51,15 +51,15 @@
<div class="paragraph">What you may not know is that even when you haven&apos;t shared interests, Facebook makes guesses about your views and preferences based on your activity. This is also used to target you, but it&apos;s not as easy for you to see this. When you use Ad Analysis for Facebook, you can see what Facebook knows about you and how that affects the advertising you see.</div>
<div class="title">How Facebook uses what it knows</div>
<div class="paragraph">Unlike the large target audiences of traditional mass media, Facebook is able to micro-target much smaller groups of people with highly specific advertising messages. How does the social networking site use specific information about you for targeted advertising? While Facebook does not give data directly to advertisers, it does allow advertisers to target people who match selected criteria.</div>
<div class="paragraph">Like other forms of advertising, Facebook provides targeting based on simple demographics like age, gender, or location. It also provides targeting based on your stated interests. These interests are part of the data that Facebook allows you to download.</div>
<div class="paragraph">Facebook also infers information about you based on the data it gathers from you, your activities, and even from your relationship to others in your network. Facebook also uses these inferred target values to you as &ldquo;categories&rdquo; that advertisers can use to target individual ads. Facebook does not make these advertising categories available to you as a download, which can make it difficult to see the bigger picture of how you&apos;re being targeted over time on Facebook.</div>
<div class="paragraph">That is where Ad Analysis for Facebook can help. By using this extension, you can get a fuller picture and see a collection of targeting data used to deliver advertising to you. You can also compare a wide range of targets shared as a public data set by thousands of other Facebook users.</div>
<div class="tabsContainer">
<div class="tabsHeadContainer">
<div class="tabHead action actionShowYourTargets">Your Targeting Data</div>
@ -74,56 +74,58 @@
</div>
<div class="title">How the Ad Analysis for Facebook extension works</div>
<div class="paragraph">In order to work, the extension must be on and <a href="http://www.facebook.com/" rel="noopener noreferrer" target="_blank">www.facebook.com</a> must be open in the Firefox browser. If the extension is off, or if you are on any website other than www.facebook.com, the extension will do nothing.</div>
<div class="paragraph">While you are browsing Facebook, the extension scans the webpage for posts that are visible only in the browser window. It checks for posts that include the phrase &ldquo;sponsored&rdquo; to determine which posts are ads.</div>
<div class="singleImageLayout"><img src="advert.png" srcset="advert.png 1x, advert@2x.png 2x" alt="An example of a Facebook post"/></div>
<div class="paragraph">Then, the extension collects the targeting information available in the modal window opened from the &ldquo;Why am I seeing this?&rdquo; menu. This automates the manual steps you would need to take to open the menu and read the modal window for each ad if you wanted to track this information on your own. </div>
<div class="paragraph">The targeting information you see in the Ad Analysis for Facebook panel that extends from the toolbar represents roughly the last 7 days of posts you view. The targeting information you see on this page under Your Targeting Data represents all the posts you&apos;ve viewed since you&apos;ve started using this extension.</div>
<div class="doubleImageLayout"><img src="why_am_i_seeing_this.png" srcset="why_am_i_seeing_this.png 1x, why_am_i_seeing_this@2x.png 2x" alt="Examples of a Facebook post with its menu open showing the 'Why am I seeing this?' menu item, and a modal window containing ad targeting information."/></div>
<div class="paragraph">All of the targeting information displayed by this extension remains on your computer. None of it is sent to Mozilla or third parties. </div>
<div class="paragraph">This extension is not a Facebook app and does not use the Facebook API. Unlike apps that use the Facebook API, this extension does not share your Facebook data with Facebook or other third parties.</div>
<div class="title">Peek outside your filter bubble</div>
<div class="paragraph">Seeing how and why you&apos;re targeted for advertising is one way to examine how Facebook shapes the content you&apos;re likely to see and how that might influence your opinions and beliefs. But what about the advertising you don&apos;t see? What messages are you being excluded from, and what messages do other people in your community see? How does that shape the conversations people are having online and the things people do offline?</div>
<div class="paragraph">Facebook maintains its own <a href="https://www.facebook.com/business/help/167836590566506/" rel="noopener noreferrer" target="_blank">Ad Archive</a>, which offers Facebook users a simple keyword search tool to view ads related to politics or issues of national importance.</div>
<div class="paragraph">The Ad Archive is useful if you know what you are looking for, but may not help you if you are trying to answer questions like &ldquo;Who is spending the most money on political ads in my state?&rdquo; or &ldquo;If I am a Millennial, do I see different ads than Baby Boomers?&rdquo; Using the tool below, you can see a list of top advertisers; filter advertisers by state, gender, and age; and link to advertiser&apos;s ads.</div>
<div class="control">Top Facebook political advertisers <select class="select selectTopAdvertisersByWeek"></select></div>
<div class="control"><input type="radio" name="filterBy" class="radio radioFilterByState" checked="checked"/><span class="label labelTopAdvertisersByState">Filter by state: </span><select class="select selectTopAdvertisersByState"></select></div>
<div class="control"><input type="radio" name="filterBy" class="radio radioFilterByGenderAndAge"/><span class="label labelTopAdvertisersByGenderAndAge">Filter by gender and age: </span><select class="select selectTopAdvertisersByGender"></select> <select class="select selectTopAdvertisersByAge"></select></div>
<div class="topAdvertisersContainer">
<div id="PageImpressionsContainer"></div>
</div>
<div class="title">More about Ad Analysis for Facebook</div>
<div class="subtitle">Source code</div>
<div class="paragraph">Ad Analysis for Facebook is an open source project from Mozilla, makers of the Firefox browser. You can access code and supporting documentation on <a href="https://github.com/mozilla/ad-analysis-for-facebook/" rel="noopener noreferrer" target="_blank">GitHub</a>.</div>
<div class="subtitle">Your privacy</div>
<div class="paragraph">This extension only collects information from the www.facebook.com page when the extension is on and Facebook is in the active tab on Firefox.</div>
<div class="paragraph">This extension collects information from Facebook ads that appear on your screen. It does not collect information from your posts.</div>
<div class="paragraph">This extension stores the information it collects from the www.facebook.com webpage in the browser. It does not send that data to Mozilla or any other entity.</div>
<div class="paragraph">If other users log in to www.facebook.com using Firefox on this computer, this extension may show and collect information from the ads displayed to them, or allow them to see the ads targeted to you.</div>
<div class="paragraph">You can clear the information saved by this extension in the &ldquo;Clear all collected ads&rdquo; section of the toolbar panel.</div>
<div class="subtitle">Credits</div>
@ -140,7 +142,7 @@
<div class="paragraph">To limit the data Facebook can collect about you while you browse the web, check out <a href="https://blog.mozilla.org/firefox/facebook-container-extension/" rel="noopener noreferrer" target="_blank">Facebook Container</a>.</div>
<div class="paragraph">To contribute to the ProPublica Political Ad Collection project, install the <a href="https://addons.mozilla.org/en-US/firefox/addon/facebook-ad-collector/" rel="noopener noreferrer" target="_blank">Facebook Political Ad Collector extension</a>.</div>
<div class="paragraph">To contribute to the ProPublica Political Ad Collection project, install the <a href="https://addons.mozilla.org/en-US/firefox/addon/facebook-ad-collector/" rel="noopener noreferrer" target="_blank">Political Ad Collector extension</a>.</div>
<!---------------------------------------- THE END ---------------------------------------->

Просмотреть файл

@ -51,7 +51,7 @@ const [AppPage] = (function() {
AppPage.prototype._renderTargets = function(elem, showPercentage = false) {
const renderAdCount = (d) => {
return ` (${d.typeCount} times)`;
return (d.typeCount === 1) ? "(1 time)" : ` (${d.typeCount} times)`;
};
const renderAdPercentage = (d) => {
const percentage = Math.round(1000.0 * d.typeCount / d.adCount) / 10.0;
@ -447,33 +447,37 @@ const [AppPage] = (function() {
if (a.lowImpressions !== b.lowImpressions) {
return b.lowImpressions - a.lowImpressions;
}
else {
return alphanumeric(a.advertiser).localeCompare(alphanumeric(b.advertiser));
if (a.highImpressions !== b.highImpressions) {
return b.highImpressions - a.highImpressions;
}
return alphanumeric(a.advertiser).localeCompare(alphanumeric(b.advertiser));
};
const highImpressionsSorter = (a, b) => {
if (a.highImpressions !== b.highImpressions) {
return b.highImpressions - a.highImpressions;
}
else {
return alphanumeric(a.advertiser).localeCompare(alphanumeric(b.advertiser));
if (a.lowImpressions !== b.lowImpressions) {
return b.lowImpressions - a.lowImpressions;
}
return alphanumeric(a.advertiser).localeCompare(alphanumeric(b.advertiser));
};
const lowSpendingSorter = (a, b) => {
if (a.lowSpending !== b.lowSpending) {
return b.lowSpending - a.lowSpending;
}
else {
return alphanumeric(a.advertiser).localeCompare(alphanumeric(b.advertiser));
if (a.highSpending !== b.highSpending) {
return b.highSpending - a.highSpending;
}
return alphanumeric(a.advertiser).localeCompare(alphanumeric(b.advertiser));
};
const highSpendingSorter = (a, b) => {
if (a.highSpending !== b.highSpending) {
return b.highSpending - a.highSpending;
}
else {
return alphanumeric(a.advertiser).localeCompare(alphanumeric(b.advertiser));
if (a.lowSpending !== b.lowSpending) {
return b.lowSpending - a.lowSpending;
}
return alphanumeric(a.advertiser).localeCompare(alphanumeric(b.advertiser));
};
const getTopAdvertisers = () => {
const threshold = (data) => {
@ -482,8 +486,10 @@ const [AppPage] = (function() {
const data = rawTable.map(row => {
return {
"advertiser": row[0],
"highImpressions": parseInt(row[1]),
"highSpending": parseInt(row[2]),
"lowImpressions": parseInt(row[1]),
"highImpressions": parseInt(row[2]),
"lowSpending": parseInt(row[3]),
"highSpending": parseInt(row[4]),
};
});
if (this.sortByKey === SORT_BY_LABEL) {
@ -519,52 +525,97 @@ const [AppPage] = (function() {
this.renderTopAdvertisers();
};
};
const labelHeader = header.append("span")
.attr("class", "label");
labelHeader.append("span")
.text("Advertiser");
const impressionsHeader = header.append("span")
.attr("class", "impressions action actionSortBy actionSortByHighImpressions")
const labelHeader = header.append("span").attr("class", "field label")
.append("div").attr("class", "doubleLabel action actionSortBy actionSortByLabel")
.on("mouseover", onMouseOverEvent)
.on("mouseout", onMouseOutEvent)
.on("click",createOnClickEvent(SORT_BY_LABEL));
labelHeader.append("span").text("Advertiser");
labelHeader.append("img")
.attr("class", "sort sortAZ")
.attr("alt", "Sort by advertiser")
.attr("src", "sort_az.svg");
const impressionsHeader = header.append("span").attr("class", "field impressions");
impressionsHeader.append("div").attr("class", "topLabel")
.append("span").text("Impressions");
const bottomImpressionsHeader = impressionsHeader.append("div").attr("class", "bottomLabel");
const bottomLowImpressionsHeader = bottomImpressionsHeader.append("span")
.attr("class", "lowImpressions action actionSortBy actionSortByLowImpressions")
.on("mouseover", onMouseOverEvent)
.on("mouseout", onMouseOutEvent)
.on("click",createOnClickEvent(SORT_BY_LOW_IMPRESSIONS));
bottomImpressionsHeader.append("span").attr("class", "dash");
const bottomHighImpressionsHeader = bottomImpressionsHeader.append("span")
.attr("class", "highImpressions action actionSortBy actionSortByHighImpressions")
.on("mouseover", onMouseOverEvent)
.on("mouseout", onMouseOutEvent)
.on("click",createOnClickEvent(SORT_BY_HIGH_IMPRESSIONS));
impressionsHeader.append("span")
.text("Impressions");
impressionsHeader.append("span")
.append("img")
.attr("class", "sort sortNumber")
.attr("alt", "Sort by impressions")
.attr("src", "sort_number.svg");
const spendingHeader = header.append("span")
.attr("class", "spending action actionSortBy actionSortByHighSpending")
bottomLowImpressionsHeader.append("span").text("Low");
bottomLowImpressionsHeader.append("img")
.attr("class", "sort sortNumber")
.attr("alt", "Sort by low impressions")
.attr("src", "sort_number.svg");
bottomHighImpressionsHeader.append("span").text("High");
bottomHighImpressionsHeader.append("img")
.attr("class", "sort sortNumber")
.attr("alt", "Sort by high impressions")
.attr("src", "sort_number.svg");
const spendingHeader = header.append("span").attr("class", "field spending");
spendingHeader.append("div").attr("class", "topLabel")
.append("span").text("Spending");
const bottomSpendingHeader = spendingHeader.append("div").attr("class", "bottomLabel");
const bottomLowSpendingHeader = bottomSpendingHeader.append("span")
.attr("class", "lowSpending action actionSortBy actionSortByLowSpending")
.on("mouseover", onMouseOverEvent)
.on("mouseout", onMouseOutEvent)
.on("click", createOnClickEvent(SORT_BY_HIGH_SPENDING));
spendingHeader.append("span")
.text("Spending");
spendingHeader.append("span")
.append("img")
.attr("class", "sort sortNumber")
.attr("alt", "Sort by spending")
.attr("src", "sort_number.svg");
.on("click",createOnClickEvent(SORT_BY_LOW_SPENDING));
bottomSpendingHeader.append("span").attr("class", "dash");
const bottomHighSpendingHeader = bottomSpendingHeader.append("span")
.attr("class", "highSpending action actionSortBy actionSortByHighSpending")
.on("mouseover", onMouseOverEvent)
.on("mouseout", onMouseOutEvent)
.on("click",createOnClickEvent(SORT_BY_HIGH_SPENDING));
bottomLowSpendingHeader.append("span").text("Low");
bottomLowSpendingHeader.append("img")
.attr("class", "sort sortNumber action actionSortBy actionSortByLowSpending")
.attr("alt", "Sort by low spending")
.attr("src", "sort_number.svg");
bottomHighSpendingHeader.append("span").text("High");
bottomHighSpendingHeader.append("img")
.attr("class", "sort sortNumber action actionSortBy actionSortByHighSpending")
.attr("alt", "Sort by high spending")
.attr("src", "sort_number.svg");
};
const renderTableBody = (body) => {
const rows = body.selectAll("div.row").data(getTopAdvertisers).enter()
.append("div")
.attr("class", "row");
const labelField = rows.append("span").attr("class", "label");
labelField.append("span")
rows.append("span")
.attr("class", "field label")
.append("a")
.attr("href", d => getQueryURL(d.advertiser))
.attr("rel", "noopener noreferrer")
.attr("target", "_blank")
.text(d => d.advertiser);
const impressionsField = rows.append("span").attr("class", "impressions");
impressionsField.append("span")
.text(d => `< ${formatter(d.highImpressions)}`);
const spendingField = rows.append("span").attr("class", "spending");
spendingField.append("span")
.text(d => d.highSpending === 0 ? "-" : `< $${formatter(d.highSpending)}`);
rows.append("span")
.attr("class", "field lowImpressions")
.text(d => `${formatter(d.lowImpressions)}`);
rows.append("span")
.attr("class", "field dash")
.text("-");
rows.append("span")
.attr("class", "field highImpressions")
.text(d => `${formatter(d.highImpressions)}`);
rows.append("span")
.attr("class", "field lowSpending")
.text(d => d.lowSpending === 0 && d.highSpending === 0 ? "" : `$${formatter(d.lowSpending)}`);
rows.append("span")
.attr("class", "field dash")
.text(d => d.highSpending === 0 ? "" : "-");
rows.append("span")
.attr("class", "field highSpending")
.text(d => d.highSpending === 0 ? "" : `$${formatter(d.highSpending)}`);
};
const renderTopAdvertisersTable = (container) => {
container.append("div")

Просмотреть файл

@ -11,11 +11,16 @@ html, body, div, h1, h2, h3, h4 {
border: 0;
}
html {
margin-bottom: -1px;
}
body {
font-family: @pageFontFamily;
font-weight: @fontNormalWeight;
font-size: 14px;
background-color: @white-100;
margin-bottom: -1px;
}
a {
@ -257,7 +262,7 @@ select {
max-height: 0px;
margin-bottom: 32px;
overflow-y: scroll;
.targetValue {
display: inline-block;
font-size: 12px;
@ -292,11 +297,11 @@ select {
}
}
}
#PageImpressionsContainer {
.topAdvertisersTable {
display: none;
font-size: 16px;
font-size: 14px;
a {
color: @extensionLightBlue;
text-decoration: none;
@ -311,27 +316,25 @@ select {
* {
pointer-events: none;
}
}
.sort {
height: 14px;
vertical-align: -2px;
margin-left: 5px;
}
.sortActive {
font-weight: @fontNormalWeight;
.sort {
height: 14px;
vertical-align: -2px;
margin-left: 5px;
visibility: hidden;
opacity: 1.0;
}
&.sortActive {
.sort {
visibility: visible;
opacity: 1.0;
}
}
&.sortInactive {
.sort {
visibility: visible;
opacity: 0.25;
}
}
&.hover {
color: @extensionDarkBlue;
}
.sortInactive {
.sort {
opacity: 0.25;
}
}
&.hover {
color: @extensionDarkBlue;
}
}
.body {
@ -349,22 +352,77 @@ select {
}
.header, .row {
white-space: nowrap;
.label {
.field {
display: inline-block;
vertical-align: top;
white-space: normal;
width: 425px;
overflow: hidden;
}
.impressions, .spending, .counts {
display: inline-block;
vertical-align: top;
white-space: normal;
margin-left: 10px;
width: 160px;
overflow: hidden;
&.label {
width: 340px;
.doubleLabel {
height: 36px;
span {
vertical-align: -8px;
}
img {
vertical-align: -10px;
}
}
}
&.impressions,
&.spending {
margin-left: 50px;
width: 155px;
.topLabel {
margin-bottom: 2px;
}
.bottomLabel {
font-weight: @fontSemiLightWeight;
img {
height: 12px;
vertical-align: -2px;
}
.dash {
display: inline-block;
width: 25px;
}
.lowImpressions {
display: inline-block;
width: 65px;
}
.highImpressions {
display: inline-block;
width: 65px;
}
.lowSpending {
display: inline-block;
width: 60px;
}
.highSpending {
display: inline-block;
width: 60px;
}
}
}
&.dash {
width: 25px;
text-align: center;
}
&.lowImpressions {
margin-left: 50px;
width: 65px;
}
&.highImpressions {
width: 65px;
}
&.lowSpending {
margin-left: 50px;
width: 60px;
}
&.highSpending {
width: 60px;
}
}
}
}
}
}
}