diff --git a/.env.example b/.env.example
index 2a2a258..9eff04f 100644
--- a/.env.example
+++ b/.env.example
@@ -7,3 +7,4 @@ domain-c=localhost # domain-ccc.com
domain-c-background=bg-red-100
port=8080 # port to listen on
google-client-id= # google client id
+facebook-app-id= # facebook app id
diff --git a/app.js b/app.js
index e1e13ba..47f3893 100644
--- a/app.js
+++ b/app.js
@@ -34,6 +34,7 @@ app.use((req, res, next) => {
res.locals.domainB = process.env['domain-b'];
res.locals.domainC = process.env['domain-c'];
res.locals.googleClientId = process.env['google-client-id'];
+ res.locals.facebookAppId = process.env['facebook-app-id'];
res.locals.port = process.env.port;
res.locals.isPortPresent = req.get('host').includes(':');
res.locals.currentDomain = req.get( 'host' );
@@ -54,7 +55,7 @@ app.use((req, res, next) => {
});
// Mount routes for different demo types
-const demoTypes = ['chips', 'related-websites-sets', 'private-state-tokens', 'fedcm'];
+const demoTypes = ['chips', 'related-websites-sets', 'private-state-tokens', 'fedcm', 'storage-access-api'];
demoTypes.forEach(demoType => {
const demoRoutes = require(`./src/demos/${demoType}/routes`);
app.use(`/${demoType}`, demoRoutes); // Mount the routes on a path specific to the demo type
@@ -70,7 +71,8 @@ const scenarios = [
'personalization',
'personalization-localstorage',
'gsi',
- 'social-media'
+ 'social-media',
+ 'social-media-comments'
];
scenarios.forEach(scenario => {
const scenarioRoutes = require(`./src/scenarios/${scenario}/routes`);
diff --git a/public/assets/js/main.js b/public/assets/js/main.js
index 8bf4245..9683f0c 100644
--- a/public/assets/js/main.js
+++ b/public/assets/js/main.js
@@ -4,8 +4,12 @@ document.addEventListener('DOMContentLoaded', function() {
if (isIframe) {
const mainHeader = document.querySelector('.main-header');
const mainFooter = document.querySelector('.main-footer');
+ const internalPageHeader = document.querySelector('.internal-page-header');
+ const themeContainer = document.getElementById('theme-container');
mainHeader?.remove();
- mainFooter?.remove();
+ mainFooter?.remove();
+ internalPageHeader?.remove('hidden');
+ themeContainer?.classList.add('h-screen');
}
});
\ No newline at end of file
diff --git a/public/assets/styles/style.css b/public/assets/styles/style.css
index f9da133..e6a0ebc 100644
--- a/public/assets/styles/style.css
+++ b/public/assets/styles/style.css
@@ -667,14 +667,6 @@ video {
position: relative;
}
-.right-8 {
- right: 2rem;
-}
-
-.top-8 {
- top: 2rem;
-}
-
.z-50 {
z-index: 50;
}
@@ -833,8 +825,8 @@ video {
appearance: none;
}
-.grid-cols-2 {
- grid-template-columns: repeat(2, minmax(0, 1fr));
+.grid-cols-1 {
+ grid-template-columns: repeat(1, minmax(0, 1fr));
}
.flex-col {
@@ -861,24 +853,12 @@ video {
gap: 1.5rem;
}
-.space-x-2 > :not([hidden]) ~ :not([hidden]) {
- --tw-space-x-reverse: 0;
- margin-right: calc(0.5rem * var(--tw-space-x-reverse));
- margin-left: calc(0.5rem * calc(1 - var(--tw-space-x-reverse)));
-}
-
.space-x-4 > :not([hidden]) ~ :not([hidden]) {
--tw-space-x-reverse: 0;
margin-right: calc(1rem * var(--tw-space-x-reverse));
margin-left: calc(1rem * calc(1 - var(--tw-space-x-reverse)));
}
-.space-y-2 > :not([hidden]) ~ :not([hidden]) {
- --tw-space-y-reverse: 0;
- margin-top: calc(0.5rem * calc(1 - var(--tw-space-y-reverse)));
- margin-bottom: calc(0.5rem * var(--tw-space-y-reverse));
-}
-
.space-y-4 > :not([hidden]) ~ :not([hidden]) {
--tw-space-y-reverse: 0;
margin-top: calc(1rem * calc(1 - var(--tw-space-y-reverse)));
@@ -1005,11 +985,6 @@ video {
text-align: center;
}
-.text-2xl {
- font-size: 1.5rem;
- line-height: 2rem;
-}
-
.text-3xl {
font-size: 1.875rem;
line-height: 2.25rem;
@@ -1158,6 +1133,14 @@ video {
}
@media (min-width: 768px) {
+ .md\:w-\[60rem\] {
+ width: 60rem;
+ }
+
+ .md\:max-w-full {
+ max-width: 100%;
+ }
+
.md\:grid-cols-4 {
grid-template-columns: repeat(4, minmax(0, 1fr));
}
diff --git a/src/common/common-scenarios.ejs b/src/common/common-scenarios.ejs
deleted file mode 100644
index 7220aa9..0000000
--- a/src/common/common-scenarios.ejs
+++ /dev/null
@@ -1,3 +0,0 @@
-
\ No newline at end of file
diff --git a/src/common/index.ejs b/src/common/index.ejs
index d7b5399..c12e5d1 100644
--- a/src/common/index.ejs
+++ b/src/common/index.ejs
@@ -9,17 +9,19 @@
<% } %>
-
+
<%= renderCard('Analytics Tracking', '🔎', '/analytics') %>
<%= renderCard('Embedded Content', '📽️', '/embedded-video') %>
- <%= renderCard('E-Commerce', '🛒', '/ecommerce') %>
+ <%= renderCard('Shopping Cart', '🛒', '/ecommerce') %>
<%= renderCard('Personalization', '🎨', '/personalization') %>
<%= renderCard('Personalization with localStorage', '🖼️', '/personalization-localstorage') %>
<%= renderCard('Single Sign-On', '🔐', '/single-sign-on') %>
<%= renderCard('Payment Gateway', '💳', '/payment-gateway') %>
- <%= renderCard('CHIPS', '🍪', '/chips') %>
<%= renderCard('Legacy GSI', '🔐', '/gsi') %>
- <%= renderCard('Facebook', '👍', '/social-media') %>
+ <%= renderCard('Facebook Like', '👍', '/social-media') %>
+ <%= renderCard('Facebook Comments', '💬', '/social-media-comments') %>
+ <%= renderCard('CHIPS', '🍪', '/chips') %>
+ <%= renderCard('Storage Access API', '🗃️', '/storage-access-api') %>
diff --git a/src/common/internal-page/header.ejs b/src/common/internal-page/header.ejs
index 1d2b964..f814f82 100644
--- a/src/common/internal-page/header.ejs
+++ b/src/common/internal-page/header.ejs
@@ -7,7 +7,7 @@
%>
<% if (title && displayHeader) { %>
-
+
<% } %>
diff --git a/src/demos/storage-access-api/index.ejs b/src/demos/storage-access-api/index.ejs
new file mode 100644
index 0000000..5604b62
--- /dev/null
+++ b/src/demos/storage-access-api/index.ejs
@@ -0,0 +1,5 @@
+<%- include(commonPath + '/header.ejs') %>
+ <%- include(commonPath + '/internal-page/header.ejs') %>
+
+ <%- include(commonPath + '/internal-page/footer.ejs') %>
+<%- include(commonPath + '/footer.ejs') %>
diff --git a/src/demos/storage-access-api/personalization.ejs b/src/demos/storage-access-api/personalization.ejs
new file mode 100644
index 0000000..91f9c0a
--- /dev/null
+++ b/src/demos/storage-access-api/personalization.ejs
@@ -0,0 +1,103 @@
+document.addEventListener('DOMContentLoaded', () => {
+ const baseURL = '<%= protocol %>://<%= domainC %><% if (isPortPresent) { %>:<%= port %><% } %>/personalization';
+ const pageContainer = document.getElementById('theme-container');
+ const themeSwitcher = document.getElementById('dark-mode-switch');
+ const errorMessages = document.getElementById('status-message');
+ const loadButton = document.getElementById('load-button');
+ const toggleContainer = document.querySelector('.dark-mode-toggle');
+ const isIframe = window.self !== window.top;
+ const containerClass = isIframe ? 'h-screen flex items-center justify-center' : 'flex items-center justify-center';
+ let hasStorageAccess = false;
+
+ document.hasStorageAccess().then(result => {
+ hasStorageAccess = result;
+ if ( hasStorageAccess ) {
+ updateUserPreference();
+ }
+ })
+
+ async function updateUserPreference() {
+ console.log('hasStorageAccess', hasStorageAccess);
+ if ( hasStorageAccess ) {
+ fetchAndApplyTheme();
+ } else {
+ try {
+ await document.requestStorageAccess();
+ toggleContainer.classList.remove('hidden');
+ loadButton.classList.add('hidden');
+ fetchAndApplyTheme();
+ } catch (error) {
+ console.error('There has been a problem with your fetch operation:', error);
+ errorMessages.textContent = `Error: ${error}`;
+ }
+ }
+ }
+
+ function fetchAndApplyTheme() {
+ fetch(`${baseURL}/get-personalization`, {
+ method: 'GET',
+ credentials: 'include'
+ })
+ .then(response => {
+ if (!response.ok) {
+ throw new Error('Network response was not ok');
+ errorMessages.textContent = `Network response was not ok ${response.status} - ${response.statusText}`;
+ }
+ return response.json();
+ })
+ .then(data => {
+ const theme = data.theme;
+ pageContainer.className = `${containerClass} ${data.theme}`
+ console.log('theme', theme);
+ if (theme === 'dark') {
+ themeSwitcher.checked = true;
+ }
+ })
+ .catch(error => {
+ console.error('There has been a problem with your fetch operation:', error);
+ errorMessages.textContent = `Error: ${error.message}`;
+ });
+ }
+
+ function fetchSetPersonalization() {
+ fetch( `${baseURL}/set-personalization`, {
+ method: 'POST',
+ credentials: 'include',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ theme: themeSwitcher?.checked ? 'dark' : 'light' })
+ })
+ .then(response => response.json())
+ .then(data => {
+ pageContainer.className = `${containerClass} ${data.theme}`;
+ });
+ }
+
+ async function toggleTheme() {
+ hasStorageAccess = await document.hasStorageAccess();
+
+ if( hasStorageAccess ) {
+ fetchSetPersonalization();
+ } else {
+ try {
+ await document.requestStorageAccess();
+ if ( await document.hasStorageAccess() ) {
+ fetchSetPersonalization();
+ } else {
+ console.error('User denied storage access');
+ errorMessages.textContent = 'User denied storage access';
+ }
+ } catch (error) {
+ console.error('Error:', error);
+ errorMessages.textContent = `The request to storage access API was denied because the user never interacted with the top-level site context or the permission wasn't grant by the user`;
+ }
+ }
+ }
+
+ window.toggleTheme = toggleTheme;
+ if (isIframe && !hasStorageAccess) {
+ console.log('In iframe');
+ toggleContainer.classList.add('hidden');
+ loadButton.classList.remove('hidden');
+ loadButton.addEventListener('click', updateUserPreference);
+ }
+});
diff --git a/src/demos/storage-access-api/routes.js b/src/demos/storage-access-api/routes.js
new file mode 100644
index 0000000..f0dad07
--- /dev/null
+++ b/src/demos/storage-access-api/routes.js
@@ -0,0 +1,49 @@
+const express = require('express');
+const path = require('path');
+const router = express.Router();
+
+router.get('/', (req, res) => {
+ const currentDomain = req.get('host');
+ const template = currentDomain === res.locals.domainC ? 'theme-selection' : 'index';
+ res.render(path.join(__dirname,template), {
+ title: 'Storage Access API'
+ });
+});
+
+router.get('/theme-selection', (req, res) => {
+ res.render(path.join(__dirname,'theme-selection'), {
+ title: 'Storage Access API'
+ });
+});
+
+
+router.get( '/get-personalization', ( req, res ) => {
+ const currentTheme = req.cookies.theme || 'light';
+ res.json( { theme: currentTheme });
+});
+
+router.post( '/set-personalization', ( req, res ) => {
+ const { theme } = req.body;
+
+ if (!theme) {
+ res.status(400).send({ message: 'Invalid request' });
+
+ }
+
+ res.cookie('theme', theme, {
+ domain: res.locals.domainC,
+ maxAge: 30 * 24 * 60 * 60 * 1000, // 30 days
+ httpOnly: true,
+ sameSite: "none",
+ secure: true
+ });
+ res.status(200).send({ message: 'Success', theme : theme});
+});
+
+// Serve the personalization.js file to the site
+router.get('/personalization.js', (req, res) => {
+ res.set('Content-Type', 'application/javascript');
+ res.render(path.join(__dirname,'personalization'));
+});
+
+module.exports = router;
diff --git a/src/demos/storage-access-api/theme-selection.ejs b/src/demos/storage-access-api/theme-selection.ejs
new file mode 100644
index 0000000..e4f024f
--- /dev/null
+++ b/src/demos/storage-access-api/theme-selection.ejs
@@ -0,0 +1,22 @@
+<%- include(commonPath + '/header.ejs') %>
+
+ <%- include(commonPath + '/internal-page/header.ejs', {containerType: 'sm'}) %>
+
+
+ Load user preferences
+
+
+ <%- include(commonPath + '/internal-page/footer.ejs') %>
+
+
+
+
+<%- include(commonPath + '/footer.ejs') %>
diff --git a/src/scenarios/ecommerce/routes.js b/src/scenarios/ecommerce/routes.js
index 4eb9524..d725f52 100644
--- a/src/scenarios/ecommerce/routes.js
+++ b/src/scenarios/ecommerce/routes.js
@@ -14,7 +14,7 @@ router.use((req, res, next) => {
router.get('/', (req, res) => {
// Render the index view (homepage)
res.render(path.join(__dirname,'index'), {
- title: 'E-commerce'
+ title: 'Shopping Cart'
});
});
diff --git a/src/scenarios/personalization-localstorage/routes.js b/src/scenarios/personalization-localstorage/routes.js
index 983db1f..551c863 100644
--- a/src/scenarios/personalization-localstorage/routes.js
+++ b/src/scenarios/personalization-localstorage/routes.js
@@ -3,7 +3,9 @@ const path = require('path');
const router = express.Router();
router.get('/', (req, res) => {
- res.render(path.join(__dirname,'index'), {
+ const currentDomain = req.get('host');
+ const pageTemplate = currentDomain === res.locals.domainC ? 'theme-selection' : 'index';
+ res.render(path.join(__dirname, pageTemplate), {
title: 'Personalization with localStorage'
});
});
diff --git a/src/scenarios/personalization-localstorage/theme-selection.ejs b/src/scenarios/personalization-localstorage/theme-selection.ejs
index 87a0e37..9fea8c0 100644
--- a/src/scenarios/personalization-localstorage/theme-selection.ejs
+++ b/src/scenarios/personalization-localstorage/theme-selection.ejs
@@ -1,6 +1,6 @@
<%- include(commonPath + '/header.ejs') %>
-
- <%- include(commonPath + '/internal-page/header.ejs', {showHeader: false, containerType: 'sm'}) %>
+
+ <%- include(commonPath + '/internal-page/header.ejs', { containerType: 'sm'}) %>
@@ -19,7 +19,7 @@
// Check for saved theme preference in localStorage
const currentTheme = localStorage.getItem('theme');
- console.log(currentTheme)
+
if (currentTheme === 'dark') {
themeContainer.classList.add(currentTheme);
themeSwitcher.checked = true;
diff --git a/src/scenarios/personalization/index.ejs b/src/scenarios/personalization/index.ejs
index 867f594..d86e4d1 100644
--- a/src/scenarios/personalization/index.ejs
+++ b/src/scenarios/personalization/index.ejs
@@ -2,6 +2,7 @@
<%- include(commonPath + '/internal-page/header.ejs', {containerType: 'sm'}) %>
+
diff --git a/src/scenarios/personalization/personalization.ejs b/src/scenarios/personalization/personalization.ejs
index 9774e85..df30077 100644
--- a/src/scenarios/personalization/personalization.ejs
+++ b/src/scenarios/personalization/personalization.ejs
@@ -2,7 +2,8 @@ document.addEventListener('DOMContentLoaded', () => {
const baseURL = '<%= protocol %>://<%= domainC %><% if (isPortPresent) { %>:<%= port %><% } %>/personalization';
const pageContainer = document.getElementById('theme-container');
const themeSwitcher = document.getElementById('dark-mode-switch');
-
+ const statusMessage = document.getElementById('status-message');
+
function updateUserPreference() {
fetch(`${baseURL}/get-personalization`, {
method: 'GET',
@@ -16,12 +17,14 @@ document.addEventListener('DOMContentLoaded', () => {
})
.then(data => {
const theme = data.theme;
- console.log(data.theme)
- pageContainer.className = theme;
- themeSwitcher.checked = theme === 'dark';
+ if ( theme === 'dark' ) {
+ pageContainer.classList.add('dark');
+ themeSwitcher.checked = true;
+ }
})
.catch(error => {
console.error('There has been a problem with your fetch operation:', error);
+ statusMessage.textContent = `There was an error fetching your personalization settings: ${error.message}`;
});
}
@@ -32,9 +35,17 @@ document.addEventListener('DOMContentLoaded', () => {
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ theme: themeSwitcher?.checked ? 'dark' : 'light' })
})
- .then(response => response.json())
+ .then(response => {
+ if (!response.ok) {
+ throw new Error('Network response was not ok');
+ }
+ return response.json();
+ })
.then(data => {
pageContainer.className = data.theme;
+ }).catch(error => {
+ console.error('There has been a problem with your fetch operation:', error);
+ statusMessage.textContent = `There was an error updating your personalization settings: ${error.message}`;
});
}
window.toggleTheme = toggleTheme;
diff --git a/src/scenarios/social-media-comments/index.ejs b/src/scenarios/social-media-comments/index.ejs
new file mode 100644
index 0000000..1934867
--- /dev/null
+++ b/src/scenarios/social-media-comments/index.ejs
@@ -0,0 +1,12 @@
+<%- include(commonPath + '/header.ejs') %>
+
+ <%- include(commonPath + '/internal-page/header.ejs', {containerType: 'sm'}) %>
+
+
+
+
+
+
+
+ <%- include(commonPath + '/internal-page/footer.ejs') %>
+<%- include(commonPath + '/footer.ejs') %>
diff --git a/src/scenarios/social-media-comments/routes.js b/src/scenarios/social-media-comments/routes.js
new file mode 100644
index 0000000..0520177
--- /dev/null
+++ b/src/scenarios/social-media-comments/routes.js
@@ -0,0 +1,13 @@
+const express = require('express');
+const path = require('path');
+const router = express.Router();
+
+router.get('/', (req, res) => {
+ // Send the default page
+ const currentDomain = req.get('host');
+ res.render(path.join(__dirname,'index'), {
+ title: 'Facebook Comments'
+ });
+});
+
+module.exports = router;
diff --git a/src/scenarios/social-media/index.ejs b/src/scenarios/social-media/index.ejs
index 22c14bf..77d80b8 100644
--- a/src/scenarios/social-media/index.ejs
+++ b/src/scenarios/social-media/index.ejs
@@ -2,24 +2,19 @@
<%- include(commonPath + '/internal-page/header.ejs', {containerType: 'sm'}) %>
-
-
-
-
+
+
+
+
+
+ data-layout="button_count"
+ data-action="like"
+ data-size="large"
+ data-share="false">
+
<%- include(commonPath + '/internal-page/footer.ejs') %>
<%- include(commonPath + '/footer.ejs') %>
diff --git a/src/scenarios/social-media/routes.js b/src/scenarios/social-media/routes.js
index bdab96c..37a2401 100644
--- a/src/scenarios/social-media/routes.js
+++ b/src/scenarios/social-media/routes.js
@@ -6,7 +6,7 @@ router.get('/', (req, res) => {
// Send the default page
const currentDomain = req.get('host');
res.render(path.join(__dirname,'index'), {
- title: 'Facebook'
+ title: 'Facebook Like Button'
});
});