Back
Menu
Close

    white flag

    Thank you for submitting your inquiry, we will get in touch with you shortly!

    Full Name*
    E-mail*
    Title
    Company
    Leave your message here
    • Home
    • Blog
    • Chat System: How to design a library (Part Three)
    UX/UI modernisation

    Chat System: How to design a library (Part Three)

    Chat System: How to design a library (Part Three)
    avatar

    Volodymyr Terebus

    December 5, 2018

    Overview

    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

    Here is a list of rules that each library should follow:

    1. Avoid overriding, changing, and modifying the global environment and the default CSS. Avoid adding a method to global objects and changing the behavior of the DOM, as well.
    2. Avoid using a 3rd party libraries that can make changes in the environment, or conflict with another library version or other incompatible libraries.
    3. To prevent conflicts avoid exposing objects, with common names, to the global environment.
    4. Use unique names for all CSS class names and id selectors to prevent conflict.
    5. Library must be able to load in ES6 module, Common JS, Require JS, and Windows.
    6. When using JS, the library should load using either script tag, defer/async attributes or asynchronously.
    7. Make the library as small as possible.
    8. For further use, it is good to embed the library version inside the actual library.

    This is how it will be.

    Looking for a reliable software development partner? Digicode is here for you.

    Contact us

    Let’s see how we did in following the rules

    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.

    • Will handle each case if website will load firebase library in its own way

    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.

    • Use a unique prefix for all CSS selectors (id, class, and attribute)
    • Use a unique prefix as well for all fields in 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

    • Use UMD

    Library should be minimized

    • Use Uglify JS

    Be able to debug library

    • A production (minimized) version with the ability to debug the library
    • Library version will be embedded within the 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

    As part of ui/ux modernization services, we offer suitable options to fit diverse requirements. 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

    • Compatible with all browsers
    • Has minimal dependencies
    • Doesn’t override the native environment
    • Minimal size
    • Many ways to integrate
    • Ability to debug production build

    Cons

    • Very few fields are exposed to global
    • Dependent on a Firebase library

    References

    white keyboard
    Want to begin a project that requires a tailored solution?
    Book a call

    Click on a star to rate it!

    Average rating 5 / 5. 1

    No votes so far! Be the first to rate this post.

    Top articles
    View all

    Related Articles

    custom-single-post__bottom-part--post-image
    custom-single-post__bottom-part--post-image
    custom-single-post__bottom-part--post-image
    custom-single-post__bottom-part--post-image
    custom-single-post__bottom-part--post-image
    custom-single-post__bottom-part--post-image