/* CONFIGURATION STARTS HERE */
/* Step 1: enter your domain name like fruitionsite.com */
const MY_DOMAIN = 'fruitionplus.com';
/*
* Step 2: enter your URL slug to page ID mapping
* The key on the left is the slug (without the slash)
* The value on the right is the Notion page ID
*/
const SLUG_TO_PAGE = {
'': '3ed984b125224fceb1ff94dc7dce7ce4',
};
/* Step 3: enter your page title and description for SEO purposes */
const PAGE_TITLE = 'Fruition Plus - Open source collection of custom CSS Styles, Templates and more for Notion websites made with Frutionsite.com';
const PAGE_DESCRIPTION = 'Notion powered websites based on Fruitionsite.com just got sexier. Fruition Plus collects CSS styling from creators, shares the lates tricks with the Fruitionsite.com script, has a marketplace for custom CSS Notion website templates and more ressources. ';
/* Step 4: enter a Google Font name, you can choose from <https://fonts.google.com> */
const GOOGLE_FONT = 'Muli';
/* Step 5: enter any custom scripts you'd like */
const CUSTOM_SCRIPT =
`<script async src="<https://www.googletagmanager.com/gtag/js?id=UA-143584615-1>"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'UA-143584615-1');
</script>
<script data-name="BMC-Widget" data-cfasync="false" src="<https://cdnjs.buymeacoffee.com/1.0.0/widget.prod.min.js>" data-id="heuschkelsimon" data-description="Support me on Buy me a coffee!" data-message="" data-color="#ebebeb" data-position="Right" data-x_margin="18" data-y_margin="18"></script>
`
;
/* CONFIGURATION ENDS HERE */
const PAGE_TO_SLUG = {};
const slugs = [];
const pages = [];
Object.keys(SLUG_TO_PAGE).forEach(slug => {
const page = SLUG_TO_PAGE[slug];
slugs.push(slug);
pages.push(page);
PAGE_TO_SLUG[page] = slug;
});
addEventListener('fetch', event => {
event.respondWith(fetchAndApply(event.request));
});
function generateSitemap() {
let sitemap = '<urlset xmlns="<http://www.sitemaps.org/schemas/sitemap/0.9>">';
slugs.forEach(
(slug) =>
(sitemap +=
'<url><loc>https://' + MY_DOMAIN + '/' + slug + '</loc></url>')
);
sitemap += '</urlset>';
return sitemap;
}
const corsHeaders = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, HEAD, POST, PUT, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type',
};
function handleOptions(request) {
if (request.headers.get('Origin') !== null &&
request.headers.get('Access-Control-Request-Method') !== null &&
request.headers.get('Access-Control-Request-Headers') !== null) {
// Handle CORS pre-flight request.
return new Response(null, {
headers: corsHeaders
});
} else {
// Handle standard OPTIONS request.
return new Response(null, {
headers: {
'Allow': 'GET, HEAD, POST, PUT, OPTIONS',
}
});
}
}
async function fetchAndApply(request) {
if (request.method === 'OPTIONS') {
return handleOptions(request);
}
let url = new URL(request.url);
url.hostname = 'www.notion.so';
if (url.pathname === '/robots.txt') {
return new Response('Sitemap: https://' + MY_DOMAIN + '/sitemap.xml');
}
if (url.pathname === '/sitemap.xml') {
let response = new Response(generateSitemap());
response.headers.set('content-type', 'application/xml');
return response;
}
let response;
if (url.pathname.startsWith('/app') && url.pathname.endsWith('js')) {
response = await fetch(url.toString());
let body = await response.text();
response = new Response(body.replace(/www.notion.so/g, MY_DOMAIN).replace(/notion.so/g, MY_DOMAIN), response);
response.headers.set('Content-Type', 'application/x-javascript');
return response;
} else if ((url.pathname.startsWith('/api'))) {
// Forward API
response = await fetch(url.toString(), {
body: url.pathname.startsWith('/api/v3/getPublicPageData') ? null : request.body,
headers: {
'content-type': 'application/json;charset=UTF-8',
'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.163 Safari/537.36'
},
method: 'POST',
});
response = new Response(response.body, response);
response.headers.set('Access-Control-Allow-Origin', '*');
return response;
} else if (slugs.indexOf(url.pathname.slice(1)) > -1) {
const pageId = SLUG_TO_PAGE[url.pathname.slice(1)];
return Response.redirect('https://' + MY_DOMAIN + '/' + pageId, 301);
} else {
response = await fetch(url.toString(), {
body: request.body,
headers: request.headers,
method: request.method,
});
response = new Response(response.body, response);
response.headers.delete('Content-Security-Policy');
response.headers.delete('X-Content-Security-Policy');
}
return appendJavascript(response, SLUG_TO_PAGE);
}
class MetaRewriter {
element(element) {
if (PAGE_TITLE !== '') {
if (element.getAttribute('property') === 'og:title'
|| element.getAttribute('name') === 'twitter:title') {
element.setAttribute('content', PAGE_TITLE);
}
if (element.tagName === 'title') {
element.setInnerContent(PAGE_TITLE);
}
}
if (PAGE_DESCRIPTION !== '') {
if (element.getAttribute('name') === 'description'
|| element.getAttribute('property') === 'og:description'
|| element.getAttribute('name') === 'twitter:description') {
element.setAttribute('content', PAGE_DESCRIPTION);
}
}
if (element.getAttribute('property') === 'og:url'
|| element.getAttribute('name') === 'twitter:url') {
element.setAttribute('content', MY_DOMAIN);
}
if (element.getAttribute('name') === 'apple-itunes-app'
|| element.getAttribute('name') === 'twitter:site'
|| element.getAttribute('property') === 'og:site_name') {
element.remove();
}
if ((element.getAttribute('name') === 'twitter:image'
|| element.getAttribute('property') === 'og:image')
&& element.getAttribute('content') === '<https://www.notion.so/images/meta/default.png>') {
// TODO: update content based on input field for the sharing image
element.remove();
}
}
}
class HeadRewriter {
element(element) {
if (GOOGLE_FONT !== '') {
element.append(`<link href="<https://fonts.googleapis.com/css?family=$>{GOOGLE_FONT.replace(' ', '+')}:Regular,Bold,Italic&display=swap" rel="stylesheet">
<style>* { font-family: "${GOOGLE_FONT}" !important; }</style>`, {
html: true
});
}
element.append(`<style>
/*BACKGROUND Color*/
/*Page Background overide*/
.notion-frame {background: #FFFEFC !important; }
.notion-scroller.vertical.horizontal > div {background: #FFFEFC !important; }
/*Background DB Table View (Inline & Linked) - Desktop & Mobile*/
div.notion-selectable.notion-collection_view-block > div { background: #FFFEFC !important; }
div.notion-selectable.notion-collection_view-block > div { box-shadow: none !important; }
/*Remove white background near search- Database Full Page - Desktop & Mobile*/
div.notion-scroller.vertical > div {
background: none !important;
}
/*Top Bar*/
div.notion-topbar { display: none !important; } /*Remove Top Bar - Desktop*/
div.notion-topbar-mobile { display: none !important; } /*Remove Top Bar - Mobile*/
div.notion-frame { height: calc(100vh) !important; } /*Remove Top bar White space*/
/*Inline DB Meta Data*/
/*Remove Views Toggle - Desktop & Mobile - Database (Inline & Linked)*/
div.notion-selectable.notion-collection_view-block > div > div.notion-selectable.notion-collection_view-block > div > div {
display: none !important; }
/*Remove Search Bar - Desktop - Database (Inline & Linked)*/
div.notion-selectable.notion-collection_view-block > div > div.notion-selectable.notion-collection_view-block > div > div > div:nth-child(1) {
display: none !important; }
/*Improved spacing (with elements)- Desktop - Database (Inline & Linked) */
div.notion-selectable.notion-collection_view-block > div {
min-height: 30px !important; }
/*Improved spacing (with elements)- Mobile - Database (Inline & Linked)*/
div.notion-selectable.notion-collection_view-block > div > div.notion-selectable.notion-collection_view-block > div {
min-height: 30px !important; }
/* Removes Border - Database (Inline & Linked)*/
div.notion-selectable.notion-collection_view-block > div > div {
border-top: none !important; }
/*Removes 'Link to original DB (Name)' - Desktop & Mobil - Linked Database (Inline)*/
div.notion-selectable.notion-collection_view-block > div > a > div {
display: none !important; }
/*Remoeves DB Name - Desktop & Mobile - Inline Database*/
div.notion-selectable.notion-collection_view-block > div > div.notion-selectable.notion-collection_view-block > div > div:nth-child(1) {
display: none !important; }
/*Removes open as page - Desktop - Database (Inline & Linked)*/
div.notion-selectable.notion-collection_view-block > div > div.notion-selectable.notion-collection_view-block > div > div > a {
display: none !important; }
/*Removes open as page - Mobile - Database (Inline & Linked)*/
div.notion-selectable.notion-collection_view-block > div > div.notion-selectable.notion-collection_view-block > div > div > div > a {
display: none !important; }
/*Remove Three-Dot menu - Desktop - Database (Inline & Linked)*/
div.notion-selectable.notion-collection_view-block > div > div.notion-selectable.notion-collection_view-block > div > div > div:nth-child(3) {
display: none !important; }
/*Remove Three-Dot menu - Mobile - Database (Inline & Linked)*/
div.notion-selectable.notion-collection_view-block > div > div > div > div > svg.dots {
display: none !important; }
/*Remove Background at Colum Names DB Table View (Full Page) - Desktop & Mobile*/
div.notion-table-view > div.notion-selectable.notion-collection_view_page-block > div {
background: #ebebeb !important;
box-shadow: none !important;
}
/*Remove Background at Colum Names DB Table View (Full Page) - Desktop & Mobile*/
div.notion-table-view > div.notion-selectable.notion-collection_view_page-block > div.pseudoSelection {
box-shadow: none !important;
clip-path: none !important;
margin-top: 15px !important;
}
/* Callout Blocks*/
/*Align Callout Icon & Content*/
/*Align Callout headline*/
div.notion-selectable.notion-callout-block > div > div > div:nth-child(2) > div:nth-child(1) {
padding-left: 35px !important;
}
/*Desktop - Images*/
div.notion-selectable.notion-callout-block > div > div > div > div.notion-record-icon.notranslate.notion-focusable > div > div > img {
margin-left: 30px !important;
}
/*Mobile - Images*/
div.notion-selectable.notion-callout-block > div > div > div > div.notion-record-icon.notranslate > div > div > img {
margin-left: 30px !important;
}
/*Desktop - Emoji*/
div.notion-selectable.notion-callout-block > div > div > div > div.notion-record-icon.notranslate.notion-focusable > div > div > span {
margin-left: 15px !important;
}
/*Mobile* - Emoji*/
div.notion-selectable.notion-callout-block > div > div > div > div.notion-record-icon.notranslate > div > div > span {
margin-left: 15px !important;
}
/*padding Callout block content text*/
div.notion-selectable.notion-callout-block > div > div > div {
margin-left: -10px !important;
}
/*padding Callout block content*/
div.notion-selectable.notion-callout-block > div > div {
padding: 15px 20px 15px 20px !important;
}
/* Margin first text in a Callout bloc*/
div.notion-selectable.notion-callout-block > div > div > div > div.notion-selectable.notion-text-block {
margin-bottom: 10px !important;
}
/*margin of Callouts within Callouts*/
div.notion-selectable.notion-callout-block > div > div > div > div.notion-selectable.notion-callout-block{
margin-top: 15px !important;
margin-bottom: 15px !important;
}
/*Callout Shadow*/
div.notion-selectable.notion-callout-block {
background: #FFFFFF !important;
border: 3px solid #000000 !important;
box-shadow: 6px 6px 0px 0.5px #000000 !important;
border-radius: 5px !important; /*Round Corners Callouts (both needed)*/
margin: 15px 0px 15px 0px !important; /*Margin Main Callouts*/
}
/*Round corner Callout */
div.notion-selectable.notion-callout-block > div > div {
border-radius: 5px !important; /*Round Corners Callouts (both needed)*/
}
/*Callout Background color*/
.notion-callout-block > div > div {
background: #FFFFFF !important; /*Callout Background overide*/
position: relative !important;
-webkit-transition: .3s ease-in-out!important;
padding: 30px 40px 30px 15px !important;
}
/*Hover Effect*/
div.notion-selectable.notion-callout-block:hover {
transform: scale(0.99,0.99);
box-shadow: 3px 3px 0px 0.5px #000000 !important;
-webkit-transition: .2s ease-in-out!important;
}
/*Custom Toggle Styles*/
div.notion-selectable.notion-toggle-block {
background: #FFFEFC !important; /*Background color*/
border: 3px solid #000000 !important; /*Border*/
border-radius: 3px !important; /*Round Corners */
margin: 0 auto 20px!important;
position: relative!important;
width: 100%!important;
-webkit-user-select: none!important;
-moz-user-select: none!important;
-ms-user-select: none!important;
margin-top: 25px!important;
margin-bottom: -0.5px!important;
}
div.notion-selectable.notion-toggle-block > div {
padding: 5px 0;
cursor: pointer;
list-style: none;
display: -webkit-flex;
display: flex;
-webkit-align-items: flex-start;
align-items: flex-start;
}
/* Full Page Databases*/
/* Gallary*/
/*Gallery View Round corners*/
div.notion-selectable.notion-collection_view_page-block > div > div > div.notion-selectable.notion-page-block.notion-collection-item > a {
border: 3px solid #000000 !important; /*Border*/
border-radius: 3px !important; /*Round Corners */
box-shadow: 6px 6px 0px 0.5px #000000 !important; /*Shadow */
margin-bottom: 30px !important;
height: 95% !important; /*decreased height for gallery item*/
}
/*Gallery better spacing*/
div.notion-selectable.notion-collection_view_page-block > div > div > div.notion-selectable.notion-page-block.notion-collection-item {
margin: 10px 10px 10px 10px !important;
}
/*Search bar Box Shadow*/
div.notion-selectable.notion-collection_view_page-block > div > div > div.notion-focusable {
background: #FFFFF !important; /*Background color*/
border: 3px solid #000000 !important; /*Border*/
border-radius: 3px !important; /*Round Corners */
box-shadow: 3px 3px 0px 0.5px #000000 !important; /*Shadow */
padding-bottom: 20px !important;
padding-top: 20px !important;
padding-left: 20px !important;
padding-right: 20px !important;
margin-top: 10px !important;
margin-right: 10px !important;
height: 0px !important;
color: black !important; /* Search Text color*/
font-weight: 600 !important; /* Make Search Text bold*/
}
/*Search Icon color change*/
div.notion-selectable.notion-collection_view_page-block > div > div > div.notion-focusable > svg.collectionSearch {
fill: black !important;
}
/*DB View Togggle Box Shadow*/
div.notion-selectable.notion-collection_view_page-block > div > div.notion-collection-view-select.notion-focusable {
background: #FFFFF !important; /*Background color*/
border: 3px solid #000000 !important; /*Border*/
border-radius: 3px !important; /*Round Corners */
box-shadow: 3px 3px 0px 0.5px #000000 !important; /*Shadow */
padding-bottom: 20px !important;
padding-top: 20px !important;
margin-top: 10px !important;
margin-left: 10px !important;
height: 0px !important;
}
/*DB View Togggle Open Round corners - Desktop*/
div.notion-overlay-container.notion-default-overlay-container > div > div > div > div > div > div > div {
background: #FFFEFC !important;
border: 3px solid #000000 !important; /*Round Corners */
box-shadow: 6px 6px 0px 0.5px #000000 !important;
border-radius: 3px !important; /*Round Corners */
}
/*Better spacing between Gallery Items and Controlls - DB Full Page - Desktop*/
div.notion-scroller.vertical.horizontal > div > div.notion-selectable.notion-collection_view_page-block {
margin-top: 20px !important;
margin-bottom: 20px !important;
}
/*Hover Effect*/
div.notion-selectable.notion-collection_view_page-block > div > div > div.notion-selectable.notion-page-block.notion-collection-item > a:hover {
transform: scale(0.99,0.99);
box-shadow: 3px 3px 0px 0.5px #000000 !important;
-webkit-transition: .2s ease-in-out!important;
}
/* Inline Databases*/
/*Spacing for last DB item to enw elenent*/
div.notion-selectable.notion-collection_view-block > div > div {
padding-bottom: 20px !important;
}
/*Gallery View Round corners*/
div.notion-selectable.notion-collection_view-block > div > div > div.notion-selectable.notion-page-block.notion-collection-item > a {
border: 3px solid #000000 !important; /*Border*/
border-radius: 3px !important; /*Round Corners */
box-shadow: 6px 6px 0px 0.5px #000000 !important; /*Shadow */
margin-bottom: 30px !important;
}
/* Not needed in this style
/* Database Gallery Item background*/
div.notion-selectable.notion-collection_view-block > div > div > div.notion-selectable.notion-page-block.notion-collection-item > a {
background: none !important; /*Takes default white Background*/
}
*/
/*Gallery better spacing*/
div.notion-selectable.notion-collection_view-block > div > div > div.notion-selectable.notion-page-block.notion-collection-item {
margin: 10px 10px 10px 10px !important;
}
/*Hover Effect*/
div.notion-selectable.notion-collection_view-block > div > div > div.notion-selectable.notion-page-block.notion-collection-item > a:hover {
transform: scale(0.99,0.99);
box-shadow: 3px 3px 0px 0.5px #000000 !important;
-webkit-transition: .2s ease-in-out!important;
}
</style>`, {
html: true
})
}
}
class BodyRewriter {
constructor(SLUG_TO_PAGE) {
this.SLUG_TO_PAGE = SLUG_TO_PAGE;
}
element(element) {
element.append(`<div style="display:none">Powered by <a href="<http://fruitionsite.com>">Fruition</a></div>
<script>
window.CONFIG.domainBaseUrl = '<https://$>{MY_DOMAIN}';
const SLUG_TO_PAGE = ${JSON.stringify(this.SLUG_TO_PAGE)};
const PAGE_TO_SLUG = {};
const slugs = [];
const pages = [];
const el = document.createElement('div');
let redirected = false;
Object.keys(SLUG_TO_PAGE).forEach(slug => {
const page = SLUG_TO_PAGE[slug];
slugs.push(slug);
pages.push(page);
PAGE_TO_SLUG[page] = slug;
});
function getPage() {
return location.pathname.slice(-32);
}
function getSlug() {
return location.pathname.slice(1);
}
function updateSlug() {
const slug = PAGE_TO_SLUG[getPage()];
if (slug != null) {
history.replaceState(history.state, '', '/' + slug);
}
}
function onDark() {
el.innerHTML = '<div title="Change to Light Mode" style="margin-left: auto; margin-right: 14px; min-width: 0px;"><div role="button" tabindex="0" style="user-select: none; transition: background 120ms ease-in 0s; cursor: pointer; border-radius: 44px;"><div style="display: flex; flex-shrink: 0; height: 14px; width: 26px; border-radius: 44px; padding: 2px; box-sizing: content-box; background: rgb(46, 170, 220); transition: background 200ms ease 0s, box-shadow 200ms ease 0s;"><div style="width: 14px; height: 14px; border-radius: 44px; background: white; transition: transform 200ms ease-out 0s, background 200ms ease-out 0s; transform: translateX(12px) translateY(0px);"></div></div></div></div>';
document.body.classList.add('dark');
__console.environment.ThemeStore.setState({ mode: 'dark' });
};
function onLight() {
el.innerHTML = '<div title="Change to Dark Mode" style="margin-left: auto; margin-right: 14px; min-width: 0px;"><div role="button" tabindex="0" style="user-select: none; transition: background 120ms ease-in 0s; cursor: pointer; border-radius: 44px;"><div style="display: flex; flex-shrink: 0; height: 14px; width: 26px; border-radius: 44px; padding: 2px; box-sizing: content-box; background: rgba(135, 131, 120, 0.3); transition: background 200ms ease 0s, box-shadow 200ms ease 0s;"><div style="width: 14px; height: 14px; border-radius: 44px; background: white; transition: transform 200ms ease-out 0s, background 200ms ease-out 0s; transform: translateX(0px) translateY(0px);"></div></div></div></div>';
document.body.classList.remove('dark');
__console.environment.ThemeStore.setState({ mode: 'light' });
}
function toggle() {
if (document.body.classList.contains('dark')) {
onLight();
} else {
onDark();
}
}
function addDarkModeButton(device) {
const nav = device === 'web' ? document.querySelector('.notion-topbar').firstChild : document.querySelector('.notion-topbar-mobile');
el.className = 'toggle-mode';
el.addEventListener('click', toggle);
nav.appendChild(el);
onLight();
}
const observer = new MutationObserver(function() {
if (redirected) return;
const nav = document.querySelector('.notion-topbar');
const mobileNav = document.querySelector('.notion-topbar-mobile');
if (nav && nav.firstChild && nav.firstChild.firstChild
|| mobileNav && mobileNav.firstChild) {
redirected = true;
updateSlug();
addDarkModeButton(nav ? 'web' : 'mobile');
const onpopstate = window.onpopstate;
window.onpopstate = function() {
if (slugs.includes(getSlug())) {
const page = SLUG_TO_PAGE[getSlug()];
if (page) {
history.replaceState(history.state, 'bypass', '/' + page);
}
}
onpopstate.apply(this, [].slice.call(arguments));
updateSlug();
};
}
});
observer.observe(document.querySelector('#notion-app'), {
childList: true,
subtree: true,
});
const replaceState = window.history.replaceState;
window.history.replaceState = function(state) {
if (arguments[1] !== 'bypass' && slugs.includes(getSlug())) return;
return replaceState.apply(window.history, arguments);
};
const pushState = window.history.pushState;
window.history.pushState = function(state) {
const dest = new URL(location.protocol + location.host + arguments[2]);
const id = dest.pathname.slice(-32);
if (pages.includes(id)) {
arguments[2] = '/' + PAGE_TO_SLUG[id];
}
return pushState.apply(window.history, arguments);
};
const open = window.XMLHttpRequest.prototype.open;
window.XMLHttpRequest.prototype.open = function() {
arguments[1] = arguments[1].replace('${MY_DOMAIN}', 'www.notion.so');
return open.apply(this, [].slice.call(arguments));
};
</script>${CUSTOM_SCRIPT}`, {
html: true
});
}
}
async function appendJavascript(res, SLUG_TO_PAGE) {
return new HTMLRewriter()
.on('title', new MetaRewriter())
.on('meta', new MetaRewriter())
.on('head', new HeadRewriter())
.on('body', new BodyRewriter(SLUG_TO_PAGE))
.transform(res);
}