/
client.ts
185 lines (163 loc) · 6.23 KB
/
client.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
(function () {
const GISCUS_SESSION_KEY = 'giscus-session';
const script = document.currentScript as HTMLScriptElement;
const giscusOrigin = new URL(script.src).origin;
function formatError(message: string) {
return `[giscus] An error occurred. Error message: "${message}".`;
}
function getMetaContent(property: string, og = false) {
const ogSelector = og ? `meta[property='og:${property}'],` : '';
const element = document.querySelector<HTMLMetaElement>(
ogSelector + `meta[name='${property}']`,
);
return element ? element.content : '';
}
// Set up session and clear the session param on load
const url = new URL(location.href);
let session = url.searchParams.get('giscus') || '';
const savedSession = localStorage.getItem(GISCUS_SESSION_KEY);
url.searchParams.delete('giscus');
url.hash = '';
const cleanedLocation = url.toString();
if (session) {
localStorage.setItem(GISCUS_SESSION_KEY, JSON.stringify(session));
history.replaceState(undefined, document.title, cleanedLocation);
} else if (savedSession) {
try {
session = JSON.parse(savedSession);
} catch (e) {
localStorage.removeItem(GISCUS_SESSION_KEY);
console.warn(`${formatError(e?.message)} Session has been cleared.`);
}
}
const attributes = script.dataset;
const params: Record<string, string> = {};
params.origin = cleanedLocation;
params.session = session as string;
params.theme = attributes.theme as string;
params.reactionsEnabled = attributes.reactionsEnabled || '1';
params.emitMetadata = attributes.emitMetadata || '0';
params.inputPosition = attributes.inputPosition || 'bottom';
params.repo = attributes.repo as string;
params.repoId = attributes.repoId as string;
params.category = attributes.category || '';
params.categoryId = attributes.categoryId as string;
params.strict = attributes.strict || '0';
params.description = getMetaContent('description', true);
params.backLink = getMetaContent('giscus:backlink') || cleanedLocation;
switch (attributes.mapping) {
case 'url':
params.term = cleanedLocation;
break;
case 'title':
params.term = document.title;
break;
case 'og:title':
params.term = getMetaContent('title', true);
break;
case 'specific':
params.term = attributes.term as string;
break;
case 'number':
params.number = attributes.term as string;
break;
case 'pathname':
default:
params.term =
location.pathname.length < 2
? 'index'
: location.pathname.substring(1).replace(/\.\w+$/, '');
break;
}
// Check anchor of the existing container and append it to origin URL
const existingContainer = document.querySelector('.giscus');
const id = existingContainer && existingContainer.id;
if (id) {
params.origin = `${cleanedLocation}#${id}`;
}
// Set up iframe src and loading attribute
const locale = attributes.lang ? `/${attributes.lang}` : '';
const src = `${giscusOrigin}${locale}/widget?${new URLSearchParams(params)}`;
const loading = attributes.loading === 'lazy' ? 'lazy' : undefined;
// Set up iframe element
const iframeElement = document.createElement('iframe');
const iframeAttributes = {
class: 'giscus-frame giscus-frame--loading',
title: 'Comments',
scrolling: 'no',
allow: 'clipboard-write',
src,
loading,
};
Object.entries(iframeAttributes).forEach(
([key, value]) => value && iframeElement.setAttribute(key, value),
);
// Prevent white flash on load
iframeElement.style.opacity = '0';
iframeElement.addEventListener('load', () => {
iframeElement.style.removeProperty('opacity');
iframeElement.classList.remove('giscus-frame--loading');
});
// Link default style and prepend as <head>'s first child to make override possible.
const style =
(document.getElementById('giscus-css') as HTMLLinkElement) || document.createElement('link');
style.id = 'giscus-css';
style.rel = 'stylesheet';
style.href = `${giscusOrigin}/default.css`;
document.head.prepend(style);
// Insert iframe element
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.`;
function signOut() {
delete params.session;
const src = `${giscusOrigin}/widget?${new URLSearchParams(params)}`;
iframeElement.src = src; // Force reload
}
// Listen to messages
window.addEventListener('message', (event) => {
if (event.origin !== giscusOrigin) return;
const { data } = event;
if (!(typeof data === 'object' && data.giscus)) return;
if (data.giscus.resizeHeight) {
iframeElement.style.height = `${data.giscus.resizeHeight}px`;
}
if (data.giscus.signOut) {
localStorage.removeItem(GISCUS_SESSION_KEY);
console.log(`[giscus] User has logged out. Session has been cleared.`);
signOut();
return;
}
if (!data.giscus.error) return;
const message: string = data.giscus.error;
if (
message.includes('Bad credentials') ||
message.includes('Invalid state value') ||
message.includes('State has expired')
) {
// 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.`);
signOut();
} 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}`);
}
});
})();