Skip to content

Commit

Permalink
Wrap client script in IIFE
Browse files Browse the repository at this point in the history
This is done to prevent declaring variables in the global scope.
  • Loading branch information
laymonage committed Jan 12, 2022
1 parent 6243df4 commit 3fadccd
Showing 1 changed file with 147 additions and 143 deletions.
290 changes: 147 additions & 143 deletions client.ts
Original file line number Diff line number Diff line change
@@ -1,107 +1,110 @@
const GISCUS_SESSION_KEY = 'giscus-session';
const script = document.currentScript as HTMLScriptElement;
const giscusOrigin = new URL(script.src).origin;
declare let iFrameResize: (options: Record<string, unknown>, selector: string) => void;

function formatError(message: string) {
return `[giscus] An error occurred. Error message: "${message}".`;
}
(function () {
const GISCUS_SESSION_KEY = 'giscus-session';
const script = document.currentScript as HTMLScriptElement;
const giscusOrigin = new URL(script.src).origin;

// Set up iframeResizer
declare let iFrameResize: (options: Record<string, unknown>, selector: string) => void;
function formatError(message: string) {
return `[giscus] An error occurred. Error message: "${message}".`;
}

function loadScript(url: string, callback: VoidFunction) {
const target = document.createElement('script');
target.setAttribute('src', url);
target.onload = callback;
script.insertAdjacentElement('beforeend', target);
}

loadScript(`${giscusOrigin}/js/iframeResizer.min.js`, () =>
iFrameResize({ checkOrigin: [giscusOrigin], resizeFrom: 'child' }, '.giscus-frame'),
);

// Set up iframe src URL and params
const url = new URL(location.href);
let session = url.searchParams.get('giscus');
const savedSession = localStorage.getItem(GISCUS_SESSION_KEY);
url.searchParams.delete('giscus');
const cleanedLocation = url.toString();

if (session) {
localStorage.setItem(GISCUS_SESSION_KEY, JSON.stringify(session));
history.replaceState(undefined, document.title, cleanedLocation);
} else {
try {
session = JSON.parse(savedSession) || '';
} catch (e) {
session = '';
localStorage.removeItem(GISCUS_SESSION_KEY);
console.warn(`${formatError(e?.message)} Session has been cleared.`);
// Set up iframeResizer
function loadScript(url: string, callback: VoidFunction) {
const target = document.createElement('script');
target.setAttribute('src', url);
target.onload = callback;
script.insertAdjacentElement('beforeend', target);
}
}

const attributes = script.dataset;
const params: Record<string, string> = {};
const ogDescriptionMeta = document.querySelector(
`meta[property='og:description'],meta[name='description']`,
) as HTMLMetaElement;

params.origin = cleanedLocation;
params.session = session;
params.theme = attributes.theme;
params.reactionsEnabled = attributes.reactionsEnabled || '1';
params.emitMetadata = attributes.emitMetadata || '0';
params.repo = attributes.repo;
params.repoId = attributes.repoId;
params.category = attributes.category || '';
params.categoryId = attributes.categoryId;
params.description = ogDescriptionMeta ? ogDescriptionMeta.content : '';

switch (attributes.mapping) {
case 'url':
params.term = cleanedLocation;
break;
case 'title':
params.term = document.title;
break;
case 'og:title':
{
const ogtitleMeta = document.querySelector(
`meta[property='og:title'],meta[name='og:title']`,
) as HTMLMetaElement;
params.term = ogtitleMeta ? ogtitleMeta.content : '';

loadScript(`${giscusOrigin}/js/iframeResizer.min.js`, () =>
iFrameResize({ checkOrigin: [giscusOrigin], resizeFrom: 'child' }, '.giscus-frame'),
);

// Set up iframe src URL and params
const url = new URL(location.href);
let session = url.searchParams.get('giscus');
const savedSession = localStorage.getItem(GISCUS_SESSION_KEY);
url.searchParams.delete('giscus');
const cleanedLocation = url.toString();

if (session) {
localStorage.setItem(GISCUS_SESSION_KEY, JSON.stringify(session));
history.replaceState(undefined, document.title, cleanedLocation);
} else {
try {
session = JSON.parse(savedSession) || '';
} catch (e) {
session = '';
localStorage.removeItem(GISCUS_SESSION_KEY);
console.warn(`${formatError(e?.message)} Session has been cleared.`);
}
break;
case 'specific':
params.term = attributes.term;
break;
case 'number':
params.number = attributes.term;
break;
case 'pathname':
default:
params.term =
location.pathname.length < 2 ? 'index' : location.pathname.substr(1).replace(/\.\w+$/, '');
break;
}

const locale = attributes.lang ? `/${attributes.lang}` : '';
const src = `${giscusOrigin}${locale}/widget?${new URLSearchParams(params)}`;

// Set up iframe element
const iframeElement = document.createElement('iframe');
const iframeAttributes = {
class: 'giscus-frame',
title: 'Comments',
scrolling: 'no',
src,
};
Object.entries(iframeAttributes).forEach(([key, value]) => iframeElement.setAttribute(key, value));

// Create default style and prepend as <head>'s first child to make override possible.
const style = document.getElementById('giscus-css') || document.createElement('style');
style.id = 'giscus-css';
style.textContent = `
}

const attributes = script.dataset;
const params: Record<string, string> = {};
const ogDescriptionMeta = document.querySelector(
`meta[property='og:description'],meta[name='description']`,
) as HTMLMetaElement;

params.origin = cleanedLocation;
params.session = session;
params.theme = attributes.theme;
params.reactionsEnabled = attributes.reactionsEnabled || '1';
params.emitMetadata = attributes.emitMetadata || '0';
params.repo = attributes.repo;
params.repoId = attributes.repoId;
params.category = attributes.category || '';
params.categoryId = attributes.categoryId;
params.description = ogDescriptionMeta ? ogDescriptionMeta.content : '';

switch (attributes.mapping) {
case 'url':
params.term = cleanedLocation;
break;
case 'title':
params.term = document.title;
break;
case 'og:title':
{
const ogtitleMeta = document.querySelector(
`meta[property='og:title'],meta[name='og:title']`,
) as HTMLMetaElement;
params.term = ogtitleMeta ? ogtitleMeta.content : '';
}
break;
case 'specific':
params.term = attributes.term;
break;
case 'number':
params.number = attributes.term;
break;
case 'pathname':
default:
params.term =
location.pathname.length < 2 ? 'index' : location.pathname.substr(1).replace(/\.\w+$/, '');
break;
}

const locale = attributes.lang ? `/${attributes.lang}` : '';
const src = `${giscusOrigin}${locale}/widget?${new URLSearchParams(params)}`;

// Set up iframe element
const iframeElement = document.createElement('iframe');
const iframeAttributes = {
class: 'giscus-frame',
title: 'Comments',
scrolling: 'no',
src,
};
Object.entries(iframeAttributes).forEach(([key, value]) =>
iframeElement.setAttribute(key, value),
);

// Create default style and prepend as <head>'s first child to make override possible.
const style = document.getElementById('giscus-css') || document.createElement('style');
style.id = 'giscus-css';
style.textContent = `
.giscus, .giscus-frame {
width: 100%;
}
Expand All @@ -111,50 +114,51 @@ style.textContent = `
color-scheme: normal;
}
`;
document.head.prepend(style);

// Insert iframe element
const existingContainer = document.querySelector('.giscus');
if (!existingContainer) {
const iframeContainer = document.createElement('div');
iframeContainer.setAttribute('class', 'giscus');
iframeContainer.appendChild(iframeElement);

script.insertAdjacentElement('afterend', iframeContainer);
} else {
while (existingContainer.firstChild) existingContainer.firstChild.remove();
existingContainer.appendChild(iframeElement);
}
const suggestion = `Please consider reporting this error at https://github.com/giscus/giscus/issues/new.`;

// Listen to error messages
window.addEventListener('message', (event) => {
if (event.origin !== giscusOrigin) return;

const { data } = event;
if (!(typeof data === 'object' && data?.giscus?.error)) return;

const message: string = data.giscus.error;

if (message.includes('Bad credentials') || message.includes('Invalid state value')) {
// Might be because token is expired or other causes
if (localStorage.getItem(GISCUS_SESSION_KEY) !== null) {
localStorage.removeItem(GISCUS_SESSION_KEY);
console.warn(`${formatError(message)} Session has been cleared.`);
document.head.prepend(style);

delete params.session;
const src = `${giscusOrigin}/widget?${new URLSearchParams(params)}`;
iframeElement.src = src; // Force reload
} else if (!savedSession) {
console.error(`${formatError(message)} No session is stored initially. ${suggestion}`);
}
} else if (message.includes('Discussion not found')) {
console.warn(
`[giscus] ${message}. A new discussion will be created if a comment/reaction is submitted.`,
);
} else if (message.includes('API rate limit exceeded')) {
console.warn(formatError(message));
// Insert iframe element
const existingContainer = document.querySelector('.giscus');
if (!existingContainer) {
const iframeContainer = document.createElement('div');
iframeContainer.setAttribute('class', 'giscus');
iframeContainer.appendChild(iframeElement);

script.insertAdjacentElement('afterend', iframeContainer);
} else {
console.error(`${formatError(message)} ${suggestion}`);
while (existingContainer.firstChild) existingContainer.firstChild.remove();
existingContainer.appendChild(iframeElement);
}
});
const suggestion = `Please consider reporting this error at https://github.com/giscus/giscus/issues/new.`;

// Listen to error messages
window.addEventListener('message', (event) => {
if (event.origin !== giscusOrigin) return;

const { data } = event;
if (!(typeof data === 'object' && data?.giscus?.error)) return;

const message: string = data.giscus.error;

if (message.includes('Bad credentials') || message.includes('Invalid state value')) {
// Might be because token is expired or other causes
if (localStorage.getItem(GISCUS_SESSION_KEY) !== null) {
localStorage.removeItem(GISCUS_SESSION_KEY);
console.warn(`${formatError(message)} Session has been cleared.`);

delete params.session;
const src = `${giscusOrigin}/widget?${new URLSearchParams(params)}`;
iframeElement.src = src; // Force reload
} else if (!savedSession) {
console.error(`${formatError(message)} No session is stored initially. ${suggestion}`);
}
} else if (message.includes('Discussion not found')) {
console.warn(
`[giscus] ${message}. A new discussion will be created if a comment/reaction is submitted.`,
);
} else if (message.includes('API rate limit exceeded')) {
console.warn(formatError(message));
} else {
console.error(`${formatError(message)} ${suggestion}`);
}
});
})();

0 comments on commit 3fadccd

Please sign in to comment.