FastComments.com

Writing Extensions

Developing Extensions

Context

FastComments provides the ability to extend our core functionality via scripts we call Extensions.

An Extension can add additional markup to the comment widget, event listeners, and run arbitrary code.

Here you will find examples of extensions we have in production, as well as documentation on how to write extensions.

The Extension Lifecycle Internal Link

The script for each extension is fetched and invoked before the comment widget begins fetching the first set of comments and rendering the UI.

On initial load, the following data will be tagged onto the extension object:

  • config - A reference to the config object.
  • translations - A reference to the translations object.
  • commentsById - A reference to all comments by id.
  • root - A reference to the root DOM node.

Extensions should override the desired functions, which the comment widget will invoke at the appropriate times.

Defining an Extension Internal Link

The smallest extension possible would be:

A Simple Extension
Copy CopyRun External Link
1
2(function () {
3 const extension = FastCommentsUI.extensions.find((extension) => {
4 return extension.id === 'my-extension';
5 });
6})();
7

For the sake of this example, save this as my-extension.js, and make it available at https://example.com/my-extension.min.js.

This extension does not do anything, except on load it fetches the extension object created by the core comment library.

This Extension object is a singleton and is not shared with any other extensions.

Next, to load our extension, we have to tell the comment widget about it. For example:

Using a Custom Extension
Copy CopyRun External Link
1
2<script async src="https://cdn.fastcomments.com/js/embed-v2-async.min.js"></script>
3<div id="fastcomments-widget"></div>
4<script>
5window.fcConfigs = [{
6 "tenantId": "demo",
7 "extensions": [
8 {
9 "id": "my-extension",
10 "path": "https://example.com/my-extension.min.js"
11 }
12 ]
13}];
14</script>
15

For functional examples, see the next section.

The Extension Object Internal Link

The extension object consists of the following definition:

Extension Object JSDoc
Copy CopyRun External Link
1
2/**
3 * The FastCommentsUI extension object. Used for lazy-loading certain components. For example, the review system is not
4 * used by all customers, so we only load that extension when we want it.
5 *
6 * @typedef {Object} FastCommentsUIExtension
7 * @property {string} id
8 * @property {Element} scriptNode
9 * @property {Element} root - The widget root dom node.
10 * @property {string} [css]
11 * @property {Object} config - The FastComments config object.
12 * @property {Object} commentsById - A reference to an object with all comments by id, which is kept up to date.
13 * @property {Object} translations - A reference to all translations.
14 * @property {Function} reRenderComment - A reference to a function that can be invoked to re-render a comment.
15 * @property {Function} removeCommentAndReRender - A reference to a function that can be invoked to remove a comment from memory and re-render the appropriate part of the DOM.
16 * @property {Function} newBroadcastId - A reference to a function that can be invoked create a new broadcast id and add it to the local list of broadcast ids to ignore.
17 * @property {FastCommentsUIExtensionSetupEventHandlers} [setupEventHandlers]
18 * @property {FastCommentsUIExtensionPrepareCommentForSavingCallback} [prepareCommentForSaving] - Called with the comment about to be posted. Return false to cancel the submit (for example when an attached poll is incomplete).
19 * @property {FastCommentsUIExtensionNewCommentCallback} [newComment]
20 * @property {FastCommentsUIExtensionReplyAreaFilter} [replyAreaFilter] - Filter HTML for the comment area.
21 * @property {FastCommentsUIExtensionWidgetFilter} [widgetFilter] - Filter HTML for the whole widget on render.
22 * @property {FastCommentsUIExtensionCommentTopFilter} [commentFilter] - Filter HTML for each comment before render.
23 * @property {FastCommentsUIExtensionReplyAreaFilter} [commentMenuFilter] - Filter HTML for each comment menu before render.
24 * @property {FastCommentsUIExtensionMenuFilter} [menuFilter] - Filter HTML for the whole widget on render.
25 * @property {FastCommentsUIExtensionReplyAreaTop} [replyAreaTop] - (LEGACY) Return HTML to add to the top of the reply area.
26 * @property {FastCommentsUIExtensionWidgetTopCallback} [widgetTop] - (LEGACY) Return HTML to add to the top of the widget.
27 * @property {FastCommentsUIExtensionCommentTopCallback} [commentTop] - (LEGACY) Return HTML to add to the top of the comment element.
28 * @property {FastCommentsUIExtensionCommentBottomCallback} [commentBottom] - (LEGACY) Return HTML to add to the bottom of the comment element.
29 * @property {FastCommentsUIExtensionCommentBottomCallback} [commentContentBottom] - Return HTML to add after the comment text, inside the comment content element (used by polls).
30 * @property {Function} [replyAreaInputBottom] - Return HTML to add inside the comment input frame, below the text input (used by polls for the in-place poll editor). Receives the parent comment id, or null for the root reply box.
31 * @property {Function} [onPollUpdate] - Called with the live event when the vote counts of a poll on the page change.
32 * @property {Function} isSiteAdmin - Returns whether the viewer is an admin or moderator of the tenant. Known after the first fetch.
33 * @property {FastCommentsUIExtensionMenuBottomCallback} [menuBottom] - (LEGACY) Return HTML to add to the bottom of the menu element for each comment.
34 * @property {FastCommentsUIExtensionRenderCallback} [onRender]
35 * @property {FastCommentsUIExtensionConnectionStatusCallback} [onLiveConnectionStatusUpdate]
36 * @property {FastCommentsUIExtensionInitialRenderCallback} [onInitialRenderComplete]
37 * @property {FastCommentsUIExtensionPresenceUpdateCallback} [onPresenceUpdate]
38 */
39
40/**
41 * @callback FastCommentsUIExtensionSetupEventHandlers
42 * @param {Element} element - The root element.
43 * @param {Object.<string, Function>} clickListeners - The event handlers for clicks, by class name, which can be modified by reference.
44 * @returns void
45 */
46
47/**
48 * @callback FastCommentsUIExtensionWidgetTopCallback
49 * @param {Object} moduleData
50 * @returns {string}
51 */
52
53/**
54 * @callback FastCommentsUIExtensionWidgetFilter
55 * @param {Object} moduleData
56 * @param {Object} html
57 * @returns {string}
58 */
59
60/**
61 * @callback FastCommentsUIExtensionCommentTopCallback
62 * @param {Object} comment
63 * @returns {string}
64 */
65
66/**
67 * @callback FastCommentsUIExtensionCommentTopFilter
68 * @param {Object} comment
69 * @param {string} html
70 * @returns {string}
71 */
72
73/**
74 * @callback FastCommentsUIExtensionCommentBottomCallback
75 * @param {Object} comment
76 * @returns {string}
77 */
78
79/**
80 * @callback FastCommentsUIExtensionMenuBottomCallback
81 * @param {Object} comment
82 * @returns {string}
83 */
84
85/**
86 * @callback FastCommentsUIExtensionMenuFilter
87 * @param {Object} comment
88 * @param {string} html
89 * @returns {string}
90 */
91
92/**
93 * @callback FastCommentsUIExtensionRenderCallback
94 * @returns {string}
95 */
96
97/**
98 * @callback FastCommentsUIExtensionConnectionStatusCallback
99 * @param {boolean} isConnected
100 * @returns {void}
101 */
102
103/**
104 * @callback FastCommentsUIExtensionInitialRenderCallback
105 * @returns {void}
106 */
107
108/**
109 * @callback FastCommentsUIExtensionReplyAreaTop
110 * @param {Object|null} currentUser
111 * @param {boolean} isSaving
112 * @param {boolean} isReplyOpen
113 * @param {string|null} parentId
114 * @returns {string}
115 */
116
117/**
118 * @callback FastCommentsUIExtensionReplyAreaFilter
119 * @param {Object|null} currentUser
120 * @param {boolean} isSaving
121 * @param {boolean} isReplyOpen
122 * @param {string|null} parentId
123 * @param {string|null} html
124 * @returns {string}
125 */
126
127/**
128 * @callback FastCommentsUIExtensionPrepareCommentForSavingCallback
129 * @param {Object} comment
130 * @param {string} parentId
131 */
132
133/**
134 * @callback FastCommentsUIExtensionNewCommentCallback
135 * @param {Object} comment
136 */
137
138/**
139 * @callback FastCommentsUIExtensionPresenceUpdateCallback
140 * @param {Object} update
141 */
142

The Extension API Internal Link

Interacting with the Extension is simple, as we simply define references to functions we want invoked.

To build off the example earlier, let's say we want to add HTML to the top of each comment:

A Simple Extension - Continued
Copy CopyRun External Link
1
2(function () {
3 const extension = FastCommentsUI.extensions.find((extension) => {
4 return extension.id === 'my-extension';
5 });
6
7 extension.commentFilter = function(comment, html) {
8 return `<h3>The user's name is ${comment.commenterName}!</h3>` + html;
9 }
10})();
11

Whenever you return HTML like this, it will get merged into the UI via a dom-diffing algorithm.

Manually triggering the re-render of a comment

We can wait for the initial page load and manually re-render a comment by invoking reRenderComment:

Re-Rending a Comment
Copy CopyRun External Link
1
2(function () {
3 const extension = FastCommentsUI.extensions.find((extension) => {
4 return extension.id === 'my-extension';
5 });
6
7 let renderCount = 0;
8
9 extension.commentFilter = function(comment, html) {
10 renderCount++;
11 return `<h3>The render count is ${renderCount}!</h3>` + html;
12 }
13
14 extension.onInitialRenderComplete = function() {
15 setInterval(function() {
16 extension.reRenderComment(extension.commentsById[Object.keys(extension.commentsById)[0]], function renderDone() {
17 console.log('Comment re-render done.');
18 });
19 }, 2000); // timeout not required, just an example.
20 }
21})();
22