Subscribe to
our newsletter
©Digicode 2011 — 2023
We need to create a Library that any customer can easily integrate to their website. The Library should provide a UI widget in the corner and have an API platform to intercommunicate, if a customer wants to override the default behavior.
Library Design
Common Rules for any JS Library
This is how it will be.
Firebase will be the only 3rd party library that we will use, considering it should be already in use. Firebase will not override any object in global environment and only exposes one global object – “firebase”. There may be conflicts if a customer’s website already uses firebase for personal reasons.
We won’t use any polyfills, shims, and CSS normalizers. We will have a defined CSS and have a few fields exposed to the global environment.
Our library should only work in the browser environment and use a browser feature that works with all browser versions. If not able to, use polyfills that will not expose itself to the global environment. Script in final stage should be in ES5.
Library is able to load using ES6 modules, script element, and Require JS
Library should be minimized
Be able to debug library
Let’s see how integration will work for our library:
<!-- Step 1. Option 1 - to add by script tag --> <!-- also you can remove 'async' and/or 'defer' attribute if want --> <script type="text/javascript" async defer src="//chat.com/chat.widget.min.js"></script> <!-- Step 1. Option 2 - to add by js in runtime --> <script> (function () { var lc = document.createElement('script'); lc.type = 'text/javascript'; lc.async = true; // can remove if need lc.defer = true; // can remove if need lc.src = '//chat.com/chat.widget.js'; var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(lc, s); })(); </script> <!-- Step 1. Option 3 - to add into website bundle by website developers --> <script type="text/javascript" async defer src="//website.bundle.min.js"></script> <!-- Step 2. For options 1 and 2 - use async init structure to start works with library --> <script> // (un)comment for debugging (enable)disable window.CFC_DEBUG = true; (function (f) { var g = window; if (g.cfc) f(); else (g.cfcAsyncInit ? g.cfcAsyncInit : g.cfcAsyncInit = []).push(f); })(function () { // widget is ready to init onWidgetReady(windows.cfc); </script> <!-- Step 2. For options 3 - use es6 modules for example to load chat library --> <script> import cfc from 'chat.widget'; // (un)comment for debugging (enable)disable cfc.CFC_DEBUG(true); // widget is ready to init onWidgetReady(cfc); </script> <!-- Step 3. Work with library --> <script> function onWidgetReady(cfc) { cfc.init({ autoLoadLibraries:true // optional (default:true) // in case if website owner has own way to load firebase library }); // login user to widget cfc.user .login({ brandId: 44, userId: 67146891 }) .then(function () { // widget if logged-in there }); } </script>
Let’s see how the library should be in high-level design:
(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory() : typeof define === 'function' && define.amd ? define(factory) : (global['cfc'] = factory()); }(this, (function () { 'use strict'; var isDebugVar = false; function isDebug() { return (typeof CFC_DEBUG !== "undefined" && CFC_DEBUG) || isDebugVar; } function log() { if (isDebug()) { var args = Array.prototype.slice.call(arguments); args.unshift(window.console); Function.prototype.bind.apply(window.console.log, args)(); } } // ... var ChatManager = function () { // .... }(); var JsNS = 'cfc'; if (window[JsNS] === undefined) { // expose chat instance (singleton by design) var manager = ChatManager.inst?ChatManager.inst:(ChatManager.inst=new ChatManager()); var widget = { user: { login: manager.login.bind(manager), // ... others fields }, init: manager.init.bind(manager), manager: function() { return isDebug()?manager:null; }; CFC_DEBUG: function(v) { isDebugVar = v; } // ... others fields VERSION: "0.0.1" }; // run all async functions setTimeout(function() { var asyncInit = window[JsNS + 'AsyncInit']; if (asyncInit && asyncInit.length > 0) { for (var i = 0; i < asyncInit.length; i++) { try { asyncInit[i](); } catch (e) { error(e); } } asyncInit.length = 0; } }); return widget; } else { log('seems window.' + JsNS + ' already exposed'); } })));
Conclusion
When all the rules from library design are followed, the result is a JS library that is not only fast and light, but can also be integrated in many different ways.
Pros
Cons
References