LogoFastComments.com

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
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
External Link
1
2<script src="https://cdn.fastcomments.com/js/embed-v2.min.js"></script>
3<div id="fastcomments-widget"></div>
4<script>
5window.FastCommentsUI(document.getElementById('fastcomments-widget'), {
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.

Example Extensions Internal Link

At FastComments, we write our own extensions, using the same API. You can view the unminified code for these extensions at the following endpoints:

Dark Mode

The Dark Mode extension is conditionally loaded when a "dark" page is detected. This extension simply adds some CSS to the comment widget. This way we do not have to load the dark mode CSS when it is not needed.

https://fastcomments.com/js/comment-ui/extensions/comment-ui.dark.extension.js

Moderation

When the current user is a moderator, we use the moderation extension.

This is a good example for adding click-based event listeners, making API requests, adding to the comment menu and comment areas.

https://fastcomments.com/js/comment-ui/extensions/comment-ui.moderation.extension.js

Live Chat

The Live Chat extension (in combination with other configuration and styling) turns the comment widget into a live chat component.

https://fastcomments.com/js/comment-ui/extensions/live-chat.extension.js

The Extension Object Internal Link

The extension object consists of the following definition:

Extension Object JSDoc
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]
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 {FastCommentsUIExtensionMenuBottomCallback} [menuBottom] - (LEGACY) Return HTML to add to the bottom of the menu element for each comment.
30 * @property {FastCommentsUIExtensionRenderCallback} [onRender]
31 * @property {FastCommentsUIExtensionConnectionStatusCallback} [onLiveConnectionStatusUpdate]
32 * @property {FastCommentsUIExtensionInitialRenderCallback} [onInitialRenderComplete]
33 * @property {FastCommentsUIExtensionPresenceUpdateCallback} [onPresenceUpdate]
34 */
35
36/**
37 * @callback FastCommentsUIExtensionSetupEventHandlers
38 * @param {Element} element - The root element.
39 * @param {Object.<string, Function>} clickListeners - The event handlers for clicks, by class name, which can be modified by reference.
40 * @returns void
41 */
42
43/**
44 * @callback FastCommentsUIExtensionWidgetTopCallback
45 * @param {Object} moduleData
46 * @returns {string}
47 */
48
49/**
50 * @callback FastCommentsUIExtensionWidgetFilter
51 * @param {Object} moduleData
52 * @param {Object} html
53 * @returns {string}
54 */
55
56/**
57 * @callback FastCommentsUIExtensionCommentTopCallback
58 * @param {Object} comment
59 * @returns {string}
60 */
61
62/**
63 * @callback FastCommentsUIExtensionCommentTopFilter
64 * @param {Object} comment
65 * @param {string} html
66 * @returns {string}
67 */
68
69/**
70 * @callback FastCommentsUIExtensionCommentBottomCallback
71 * @param {Object} comment
72 * @returns {string}
73 */
74
75/**
76 * @callback FastCommentsUIExtensionMenuBottomCallback
77 * @param {Object} comment
78 * @returns {string}
79 */
80
81/**
82 * @callback FastCommentsUIExtensionMenuFilter
83 * @param {Object} comment
84 * @param {string} html
85 * @returns {string}
86 */
87
88/**
89 * @callback FastCommentsUIExtensionRenderCallback
90 * @returns {string}
91 */
92
93/**
94 * @callback FastCommentsUIExtensionConnectionStatusCallback
95 * @param {boolean} isConnected
96 * @returns {void}
97 */
98
99/**
100 * @callback FastCommentsUIExtensionInitialRenderCallback
101 * @returns {void}
102 */
103
104/**
105 * @callback FastCommentsUIExtensionReplyAreaTop
106 * @param {Object|null} currentUser
107 * @param {boolean} isSaving
108 * @param {boolean} isReplyOpen
109 * @param {string|null} parentId
110 * @returns {string}
111 */
112
113/**
114 * @callback FastCommentsUIExtensionReplyAreaFilter
115 * @param {Object|null} currentUser
116 * @param {boolean} isSaving
117 * @param {boolean} isReplyOpen
118 * @param {string|null} parentId
119 * @param {string|null} html
120 * @returns {string}
121 */
122
123/**
124 * @callback FastCommentsUIExtensionPrepareCommentForSavingCallback
125 * @param {Object} comment
126 * @param {string} parentId
127 */
128
129/**
130 * @callback FastCommentsUIExtensionNewCommentCallback
131 * @param {Object} comment
132 */
133
134/**
135 * @callback FastCommentsUIExtensionPresenceUpdateCallback
136 * @param {Object} update
137 */
138

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
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
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