“
Save time by making it easy to scan all the latest stories on topics you follow in one place.
“,
“
“,
“
”
].join(“”);
var content2 = [
““,
“
“
Adding topics. Click on the + symbol to save the topic to your Following page.
“,
“
“,
“
(Step 1 of 3)
“,
“
“,
““,
““,
“
”
].join(“”);
var content3 = [
““,
“
“
Clicking on the topic’s name will bring you to a page where you can view all the articles that pertain to that topic.
“,
“
“,
“
(Step 2 of 3)
“,
“
“,
““,
““,
“
”
].join(“”);
var content4 = [
““,
“
“”,
“
Access your following topics by clicking on your display name. The number in your display name indicates the number of new articles you haven’t read from your following topics.
“,
“
“,
“
(Step 3 of 3)
“,
“
“,
““,
““,
“
”
].join(“”);
var contentMarkup = [content1, content2, content3, content4];
function createTourtip() {
var relatedTopics = document.querySelector(“#main-content-area .pb-f-topics-article-related .c-topic-group__body ul”);
var topicPageTitle = document.querySelector(“.o-primary-header–section .c-topic”);
// If the following topics buttons are present on the page, set up the tourtips
if (relatedTopics || topicPageTitle) {
var modifier = relatedTopics ? ‘darwin-arc6134–article’ : ‘darwin-arc6134–topic-page’
var dataAnalyticsClick = JSON.stringify({
testId: “arc6134”,
type: “button”,
feature: “topic tour tip”,
label: “show tooltip”
});
var el = relatedTopics ? document.createElement(“li”) : document.createElement(“div”);
el.classList.add(“darwin-arc6134”);
el.classList.add(modifier);
el.innerHTML = “
“+contentMarkup[0] + “
“;
relatedTopics ? relatedTopics.appendChild(el) : topicPageTitle.appendChild(el);
// Remove follow topics onboarding messages and activation tooltips
setTimeout(function() {
var onboardingMessages = document.querySelectorAll(“.c-topic-group__body .c-onboarding-message”);
var tooltips = document.querySelectorAll(“.c-topic-group__body .c-tooltip”);
console.log(“[ARC-6134]: onboardingMessages: “, onboardingMessages);
Array.from(onboardingMessages).forEach(function(el) {
el.parentNode.removeChild(el);
});
Array.from(tooltips).forEach(function(el) {
el.parentNode.removeChild(el);
});
}, 3000);
}
}
function analyticsPush(type, panel, action) {
if (type === “simple”) {
analyticsLayer.push({
“event”: “simpleEvent”,
“simpleEvent”: {
“testId”: “arc6134”,
“action”: “view tour slide”,
“label”: “slide “+panel
}
});
}
if (type === “click”) {
analyticsLayer.push({
“event”:”clickEvent”,
“clickEvent”: {
“testId”: “arc6134”,
“feature”: “topic tour tip”,
“type”: “button”,
“label”: action,
}
});
}
}
function tourtipActions() {
// Open tourtips
var container = document.querySelector(“.darwin-arc6134”);
var activateIcon = document.querySelector(“.js-tourtip-activate”);
var tourtipContainer = document.querySelector(“.darwin-arc6134__tourtip”);
activateIcon.addEventListener(“click”, function() {
if (!container.classList.contains(“darwin-arc6134–open”)) {
container.classList.add(“darwin-arc6134–open”);
tourtipContainer.removeAttribute(“hidden”);
keepInViewport();
localStorage.setItem(“tgam.tourtip_icon_clicked”, true);
analyticsPush(“click”, 1, “show tooltip”);
analyticsPush(“simple”, 1, “”);
}
});
// Advance tourtip panels
tourtipContainer.addEventListener(‘click’, function(e) {
var tourtipPanel = tourtipContainer.getAttribute(“data-panel”);
var activateIcon = document.querySelector(“.js-tourtip-activate”);
var el = e.target || e.srcElement;
var action = el.getAttribute(“data-action”);
if (action === “next”) {
tourtipPanel++;
analyticsPush(“simple”, tourtipPanel, action);
} else if (action === “prev”) {
tourtipPanel–;
analyticsPush(“simple”, tourtipPanel, action);
} else {
tourtipPanel = 1;
tourtipContainer.setAttribute(“hidden”, true);
container.classList.remove(“darwin-arc6134–open”);
tourtipContainer.classList.remove(“darwin-arc6134__tourtip–leftOut”);
tourtipContainer.classList.remove(“darwin-arc6134__tourtip–rightOut”);
activateIcon.classList.remove(“darwin-arc6134__tourtip–leftOut”);
activateIcon.classList.remove(“darwin-arc6134__tourtip–rightOut”);
tourtipContainer.classList.remove(“js-tourtip–centred”);
tourtipContainer.style.removeProperty(“left”);
}
analyticsPush(“click”, tourtipPanel, action);
tourtipPanel === 4 ? tourtipContainer.classList.add(“darwin-arc6134__tourtip–last-panel”) : tourtipContainer.classList.remove(“darwin-arc6134__tourtip–last-panel”);
tourtipContainer.innerHTML = contentMarkup[tourtipPanel-1];
tourtipContainer.dataset.panel = tourtipPanel;
});
}
function init() {
console.log(“[ARC-6134] init”);
var testIsEnabled = /token=arc6134/.test(window.location.search);
console.log(“ARC-6134 testIsEnabled: “, testIsEnabled);
var testGroup = userTestGroup();
if (testGroup === “variant” || testIsEnabled) {
setUnitState(“arc6134”, “variant”);
console.log(“[ARC-6134] test group: variant”);
createTourtip();
tourtipActions();
intersectTourtip();
}
if (testGroup === “control”) {
console.log(“[ARC-6134] test group: control”);
setUnitState(“arc6134”, “control”);
}
}
init();
}]);
}
window.tgam.darwin.tests.after.push(dwExecute_UXTestARC6134);
})();
”
].join(“”);
}
return template;
}
/**
* Generate a template for the label
* @param {String} label – the label text
* @returns {String}
*/
function displayLabel(label) {
var template = “”;
if (label === “opinion”) {
template = [
“
“,
” “, label, ““,
“
“,
].join(“”);
}
return template;
}
/**
* Display “Follow”, “Following” buttons
* @param {Object} tData
* @returns {String}
*/
function displayFollowingButton(tData) {
var topicName = tData.topicName;
var topicSlug = tData.topicSlug;
var topicType = tData.topicType;
var topicImg = tData.topicImg;
var isAuthor = topicType === “author”;
var authorImg = isAuthor && topicImg
? “”
: “”;
var template = isAuthor ? “/authors/” : “/topics/”;
var href = window.tgam.env.baseRootAbsoluteUrl + template + topicSlug;
var linkClasses = isAuthor ? “c-topic-link c-topic-link–author” : “c-topic-link”;
return [
” “,
].join(“”);
}
/**
* Generates story card markup
* @param {Object} article – to display
* @param {Object} topicData – (topicName, topicSlug, topicType, topicVariation, topicImg)
* @param {Boolean} addFollowButton
* @returns {String} story card markup
*/
function storyCard(article, topicData, addFollowButton) {
console.info(“[ARC-6637] storyCard”, { article: article, topic: topicData });
if (!article || !topicData) {
return “”;
}
var tName = topicData.topicName;
var tType = topicData.topicType;
var tVariation = topicData.topicVariation;
var followingTopic;
var timeTemplate;
var analyticsModifier = tType + “: “;
if (addFollowButton) {
// Display a follow button beside the topic name
followingTopic = displayFollowingButton(topicData);
timeTemplate = displayDateTag(article);
}
var label = getLabel(article);
var image = getImage(article, label);
var labelTemplate = displayLabel(label);
var imageTemplate = displayImage(image);
var headline = article.headlines.basic;
var href = window.tgam.env.baseUrl + article.canonical_url;
var sophiId = article._id;
var dataAnalyticsClick = JSON.stringify({
type: “link”,
feature: “following feed”,
contentId: sophiId,
label: analyticsModifier + tName.toLowerCase() + “: ” + headline.toLowerCase(),
page: “sec:homepage:personalized feed:” + tVariation,
hierarchy: 1
});
var cardMarkup = “”;
if (!followingTopic) {
cardMarkup = [
““,
”
”
“,
” “, tName, ““,
”
“,
”
“,
” “, headline, ““,
”
“,
labelTemplate,
”
“,
”
“, imageTemplate, “
“,
“”
].join(“”);
} else {
cardMarkup = [
”
“,
followingTopic,
”
“,
” “,
”
”
“,
” “, headline, ““,
”
“,
” “,
”
“,
”
“, imageTemplate, “
“,
” “,
].join(“”);
}
return [
”
“,
cardMarkup,
”
“,
].join(“”);
}
/**
* Adds the overlay trigger dot class
* @param {String} type – “unread” or “no-follow”
*/
function addOverlayTriggerDot(type) {
overlayTriggerDotClasses.forEach(function fn(dotClass) {
if (dotClass === “c-your-globe__trigger–dot–” + type) {
overlayTrigger.classList.add(dotClass);
} else {
overlayTrigger.classList.remove(dotClass);
}
});
overlayTrigger.classList.add(“c-your-globe__trigger–dot”);
}
/**
* Removes the overlay trigger dot class
*/
function removeOverlayTriggerDot() {
overlayTriggerDotClasses.forEach(function fn(dotClass) {
overlayTrigger.classList.remove(dotClass);
});
overlayTrigger.classList.remove(“c-your-globe__trigger–dot”);
}
/**
* Returns a heading element to be displayed inside the overlay
* @param {String} text
* @returns {String}
*/
function overlayLabel(text) {
return “
” + text + “
“;
}
var overlayHeadingHasFollowed = (
overlayLabel(“The latest in topics and authors you follow”) +
“
”
);
var overlayHeadingNoFollowed = (
overlayLabel(“Get started: build your personal news feed”) +
“
”
);
var upToDateMessage = “
You’re up to date on your Following feed. Check again later for new stories.
“;
// ************************************************
// Parse API response and inject markup into overlay
// ************************************************
// Story card markup
/**
* Stories originally came from the following locations in the API response:
* – data.articles[i].items[i].topics
* – data.articles[i].items[i].authors
* @param {Array} stories
* @returns {String} story card markup
*/
function latestStoryCards(stories) {
console.info(“[ARC-6637] Display latest stories based on these stories:”, stories);
var storyCards = stories.map(function fn(story) {
// The “topics” and “authors” arrays will only contain one item
// (i.e. the topic or author that the user is following)
var topic;
var topicData;
if (story.topics && story.topics.length) {
// Normal topic
topic = story.topics[0];
topicData = {
topicName: topic.name,
topicSlug: topic.slug,
topicType: “topic”,
topicVariation: “following”
};
} else if (story.authors && story.authors.length) {
// Author topic
topic = story.authors[0];
// Only authors have images associated with them – normal topics do not
var authorImg = topic.metadata && topic.metadata.image ? topic.metadata.image : null;
topicData = {
topicName: topic.byline,
topicSlug: topic.slug,
topicType: “author”,
topicVariation: “following”,
topicImg: authorImg
};
}
// Don’t display a follow button beside the topic because the user is already following it
return storyCard(story, topicData, false);
}).join(“”);
return storyCards;
}
/**
* @param {Array} topics
* @param {String} variation – “recommended” or “trending” (used for the click tracking analytics)
* @returns {String} story card markup
*/
function recommendedTrendingStoryCards(topics, variation) {
var uniqueStories = generateUniqueStory(topics);
var storyCards = uniqueStories.map(function fn(topic) {
// Grab one article
var story = topic.items[0];
// Only authors have images associated with them – normal topics do not
var authorImg = topic.authorTopic && topic.authorMetadata && topic.authorMetadata.image
? topic.authorMetadata.image
: null;
// Normally we’d display all of the topics that are assigned to an article, but
// this API groups articles by topic, so we only have access to that one topic.
// Even if we could display additional topics, we wouldn’t want to becuase having
// multiple follow buttons would junk up the UI.
var topicData = {
topicName: topic.name,
topicSlug: topic.slug,
topicType: topic.authorTopic ? “author” : “topic”,
topicVariation: variation,
topicImg: authorImg
};
// Display the follow button beside the topic because we’re suggesting new topics to follow
return storyCard(story, topicData, true);
}).join(“”);
return storyCards;
}
/**
* Topics originally came from the following locations in the API response:
* – data.recommendedAuthors
* – data.recommendedTopics
* @param {Array} topics
* @returns {String} story card markup
*/
function recommendedStoryCards(topics) {
console.info(“[ARC-6637] Display recommended stories based on these topics:”, topics);
return recommendedTrendingStoryCards(topics, “recommended”);
}
/**
* Topics originally came from the following location in the API response:
* – data.trendingTopics
* @param {Array} topics
* @returns {String} story card markup
*/
function trendingStoryCards(topics) {
console.info(“[ARC-6637] Display trending stories based on these topics:”, topics);
return recommendedTrendingStoryCards(topics, “trending”);
}
// Markup inside the overlay
/**
* @param {Array} stories
* @returns {Object} markup for the overlay’s header and body content areas
*/
function showLatestStories(stories) {
console.info(“[ARC-6637] Scenario: latest stories”);
var storyCardsMarkup = latestStoryCards(stories);
return {
header: overlayHeadingHasFollowed,
body: storyCardsMarkup
};
}
/**
* @param {Array} topics
* @returns {Object} markup for the overlay’s header and body content areas
*/
function upToDateShowRecommended(topics) {
console.info(“[ARC-6637] Scenario: up to date, show recommended”);
var storyCardsMarkup = recommendedStoryCards(topics);
return {
header: overlayHeadingHasFollowed,
body: (
upToDateMessage +
overlayLabel(“Recommended for you”) +
storyCardsMarkup
)
};
}
/**
* @param {Array} topics
* @returns {Object} markup for the overlay’s header and body content areas
*/
function upToDateShowTrending(topics) {
console.info(“[ARC-6637] Scenario: up to date, show trending”);
var storyCardsMarkup = trendingStoryCards(topics);
return {
header: overlayHeadingHasFollowed,
body: (
upToDateMessage +
overlayLabel(“Trending topics to follow”) +
storyCardsMarkup
)
};
}
/**
* @param {Array} topics
* @returns {Object} markup for the overlay’s header and body content areas
*/
function notFollowingShowRecommended(topics) {
console.info(“[ARC-6637] Scenario: not following, show recommended”);
var storyCardsMarkup = recommendedStoryCards(topics);
return {
header: overlayHeadingNoFollowed,
body: (
overlayLabel(“Recommended for you”) +
storyCardsMarkup
)
};
}
/**
* @param {Array} topics
* @returns {Object} markup for the overlay’s header and body content areas
*/
function notFollowingShowTrending(topics) {
console.info(“[ARC-6637] Scenario: not following, show trending”);
var storyCardsMarkup = trendingStoryCards(topics);
return {
header: overlayHeadingNoFollowed,
body: (
overlayLabel(“Trending topics to follow”) +
storyCardsMarkup
)
};
}
/**
* Parse data from the personalized API and inject markup into the overlay
* @see https://confluence.theglobeandmail.com/display/ARC/Logic+for+Embedded+on+Homepage++and+Your+Globe+Overlay
* @param {Object} data – topic and story data provided by the API
*/
function parsePerzonalizedTopicsData(data) {
console.info(“[ARC-6637] parsePerzonalizedTopicsData”, data);
var overlayHeader = qs(“.c-your-globe__overlay-header”);
var overlayHeaderText = qs(“.c-your-globe__overlay-header-text”);
var overlayBody = qs(“.c-your-globe__overlay-body”);
if (!overlayHeader || !overlayHeaderText || !overlayBody) {
return;
}
var totalTopicsFollowed = data.totalTopicsFollowed || 0;
var totalAuthorsFollowed = data.totalAuthorsFollowed || 0;
var recommendedTopics = data.recommendedTopics || [];
var recommendedAuthors = data.recommendedAuthors || [];
var latestStories = data.articles || [];
var topics = [];
var stories = [];
var markup = “”;
var headerBorder = true;
if (totalTopicsFollowed || totalAuthorsFollowed) {
if (latestStories.length) {
// Scenario 1: “Latest stories”
stories = generateLatestStories(latestStories);
markup = showLatestStories(stories); // Similar to displayLatestAndRecommended() in ARC-6292
addOverlayTriggerDot(“unread”);
} else {
if (recommendedTopics.length || recommendedAuthors.length) {
// Scenario 2: “Up to date, show recommended”
topics = recommendedAuthors.concat(recommendedTopics);
markup = upToDateShowRecommended(topics); // Similar to displayRecommendedTrendingTopicList(“rec”) in ARC-6292
} else {
// Scenario 3: “Up to date, show trending”
topics = data.trendingTopics || [];
markup = upToDateShowTrending(topics); // Similar to displayRecommendedTrendingTopicList(“trend”) in ARC-6292
}
}
} else {
topics = recommendedAuthors.concat(recommendedTopics);
if (topics.length) {
// Scenario 4: “Not following, show recommended”
markup = notFollowingShowRecommended(topics); // Similar to displayThreeTopicsOneStory(“rec”) in ARC-6292
} else {
// Scenario 5: “Not following, show trending”
topics = data.trendingTopics || [];
markup = notFollowingShowTrending(topics); // Similar to displayThreeTopicsOneStory(“trend”) in ARC-6292
}
addOverlayTriggerDot(“no-follow”);
headerBorder = false;
}
console.info(“[ARC-6637] Append markup”);
var spinner = qs(“.c-spinner”);
spinner && spinner.parentElement.removeChild(spinner);
overlayHeaderText.insertAdjacentHTML(“afterbegin”, markup.header);
overlayBody.insertAdjacentHTML(“afterbegin”, markup.body);
if (!headerBorder) {
overlayHeader.classList.add(“c-your-globe__overlay-header–no-border”);
}
addFollowingFunctionality();
}
// ************************************************
// API calls
// ************************************************
/**
* Get the personalized feed
* @param {String} hashId – user’s hash id
* @param {String} env – environment
* @see https://confluence.theglobeandmail.com/display/ARC/Logic+for+Embedded+on+Homepage++and+Your+Globe+Overlay
* @returns {Promise