bim.sdk.js

// BIM SDK VERSION 3.0.0
/**
 * @global
 * @name BIMSDK
 * @since 23.08.17
 * @version 3.0.0
 * @author Mauricio <cto@hitbim.com>
 * @description
 * Add '{@link bim}' to global Window Object
 * @copyright
 * <summary>
 * {@link https://developer.hitbim.com Hitbim} © 2023 All Rights Reserved
 *
 * <details>
 * All files and information contained in this Software are copyright by {@link https://developer.hitbim.com Hitbim},
 * and may not be duplicated, copied, modified or adapted, in any way without our written permission.
 * <br/>
 *
 * Our Software, Tools, Website, and Services may contain our service marks or trademarks as well as those of our affiliates
 * or other companies, in the form of words, graphics, and logos.
 * <br/>
 *
 * Your use of our Software and Services does not constitute any right or license for you
 * to use our Services, Tools, Software, marks or trademarks, without the prior written permission of {@link https://developer.hitbim.com Hitbim}.
 * <br/>
 *
 * Our Content, as found within our Software, Website, Tools and Services, are protected under United States and foreign copyrights.
 * The copying, redistribution, use or publication by you of any such Content, is strictly prohibited.
 * Your use of our Software, Website, Tools and Services does not grant you any ownership rights to our Content.
 * <br/>
 *
 * Copyright © {@link https://developer.hitbim.com Hitbim} ©. All Rights Reserved.
 * </details>
 * </summary>
 */
(function (w) {
  "use strict";
  /**
   * @since 23.08.17
   * @version 3.0.0
   * @author Mauricio <cto@hitbim.com>
   * @exports bim
   * @namespace bim
   *
   * @property {Object} app
   * Collection which manages the overall functions of the App.
   * See {@link bim.app} for more details.
   *
   * @property {function} event
   * Function to add event listener to element on event type given.
   * See {@link bim.event} for more details.
   *
   * @property {function} querySelector
   * Function to get Element within the document that matches the specified selector.
   * See {@link bim.querySelector} for more details.
   *
   * @property {Object} plugin
   * Collection of functions that manage plugin.
   * See {@link bim.plugin} for more details.
   *
   * @property {Object} config
   * Object which has function to read files related to config.
   * See {@link bim.config} for more details.
   *
   * @property {Object} native
   * Collection of functions which control the App natively.
   * See {@link bim.native} for more details.
   *
   * @property {Object} component
   * Collection of functions that manage components.
   * See {@link bim.component} for more details.
   *
   * @property {function} readFile
   * Function to read the given file path and call the callback.
   * See {@link bim.readFile} for more details.
   *
   * @property {Object} crawling
   * Collection of Objects to control elements of the document.
   * See {@link bim.crawling} for more details.
   *
   * @property {Object} functions
   * Collection of useful general static functions.
   * See {@link bim.functions} for more details.
   */
  var __lib = (function () {
    /**
     * @name __pluginId__
     * @inner
     * @private
     * @memberof bim
     * @type {string}
     * @description
     * Variable which stores the plugin ID.
     *
     * Used in <code>bim.plugin.id</code>.
     */
    var __pluginId__ = "";

    /**
     * @name __token__
     * @inner
     * @private
     * @memberof bim
     * @type {string}
     * @description
     * Variable assigned by reading the token value of the plugin from the "key" file.
     */
    var __token__ = "";

    /**
     * @name __name__
     * @inner
     * @private
     * @memberof bim
     * @type {string}
     * @description
     * Variable which stores the plugin name.
     *
     * Used in <code>bim.plugin.name</code>.
     */
    var __name__ = "";

    /**
     * @name __PLUGINS_DIR__
     * @inner
     * @private
     * @memberof bim
     * @type {string}
     * @description
     * Variable which stores the directory path where plugins are located.
     */
    var __PLUGINS_DIR__ = "./PLUGINS";

    /**
     * @name pageNavigator
     * @inner
     * @private
     * @memberof bim
     * @type {Array}
     * @description
     * Container for page.
     * <code>bim.app.page</code> push
     * Object {
     *  index: {number},
     *  name: {string},
     *  OpenCallback: {function},
     *  CloseCallback: {function}
     * }
     *
     * <code>bim.app.close.page</code> pop
     */
    var pageNavigator = [];

    /**
     * @name disableBackPage
     * @inner
     * @private
     * @memberof bim
     * @type {boolean}
     * @description
     * Checks this private variable when native go back button is pressed.
     * If this is true, blocks the callback function.
     */
    var disableBackPage = false;

    /**
     * @constructor
     * @name DOM
     * @inner
     * @private
     * @memberof bim
     */
    var DOM = function DOM(arr) {
      var self = this;
      // Create array-like object
      for (var i = 0; i < arr.length; i += 1) {
        self[i] = arr[i];
      }
      self.length = arr.length;
      // Return collection with methods
      return this;
    };

    /**
     * @function tokenizer
     * @inner
     * @private
     * @memberof bim
     * @param {DOM | string} selector
     * Selector to match the Element.
     * Can be DOM, Document, Window Object or
     * string.
     * @param {*} context Context
     * @returns {DOM} DOM object
     */
    function tokenizer(selector, context) {
      var arr = [];
      var i = 0;
      if (selector && !context) {
        if (selector instanceof DOM) {
          return selector;
        }
      }
      if (selector) {
        // String
        if (typeof selector === "string") {
          var els;
          var tempParent;
          var html = selector.trim();
          if (html.indexOf("<") >= 0 && html.indexOf(">") >= 0) {
            var toCreate = "div";
            if (html.indexOf("<li") === 0) {
              toCreate = "ul";
            }
            if (html.indexOf("<tr") === 0) {
              toCreate = "tbody";
            }
            if (html.indexOf("<td") === 0 || html.indexOf("<th") === 0) {
              toCreate = "tr";
            }
            if (html.indexOf("<tbody") === 0) {
              toCreate = "table";
            }
            if (html.indexOf("<option") === 0) {
              toCreate = "select";
            }
            tempParent = document.createElement(toCreate);
            tempParent.innerHTML = html;
            for (i = 0; i < tempParent.childNodes.length; i += 1) {
              arr.push(tempParent.childNodes[i]);
            }
          } else {
            if (
              !context &&
              selector[0] === "#" &&
              !selector.match(/[ .<>:~]/)
            ) {
              // Pure ID selector
              els = [document.getElementById(selector.trim().split("#")[1])];
            } else {
              // Other selectors
              els = (context || document).querySelectorAll(selector.trim());
            }
            for (i = 0; i < els.length; i += 1) {
              if (els[i]) {
                arr.push(els[i]);
              }
            }
          }
        } else if (
          selector.nodeType ||
          selector === window ||
          selector === document
        ) {
          // Node/element
          arr.push(selector);
        } else if (selector.length > 0 && selector[0].nodeType) {
          // Array of elements or instance of Dom
          for (i = 0; i < selector.length; i += 1) {
            arr.push(selector[i]);
          }
        }
      }
      return new DOM(arr);
    }

    tokenizer.fn = DOM.prototype;
    tokenizer.Class = DOM;
    /**
     * tokenizer use
     */
    tokenizer.use = function use() {
      var args = [],
        len = arguments.length;
      while (len--) args[len] = arguments[len];

      args.forEach(function (methods) {
        var isUtils = "__utils" in methods;
        Object.keys(methods).forEach(function (methodName) {
          if (methodName === "__utils") {
            return;
          }
          if (isUtils) {
            tokenizer[methodName] = methods[methodName];
          } else {
            tokenizer.fn[methodName] = methods[methodName];
          }
        });
      });
    };

    /**
     * @description
     * By importing {@link BIMSDK}, functions are added to DOM Element.
     *
     * Use function {@link bim.app.element} to get DOM Element Object.
     *
     * Below methods can be called with DOM Element.
     *
     * <br/>
     *
     * @namespace DOM
     * @property {function} is
     * Function to check whether the element corresponds to the selector or not.
     * See {@link DOM.is} for more details.
     *
     * @property {function} find
     * Function to find Objects from the element with the selector.
     * See {@link DOM.find} for more details.
     *
     * @property {function} filter
     * Function to filter the element through a function given as a parameter.
     * See {@link DOM.filter} for more details.
     *
     * @property {function} append
     * Function to append array in arguments to the element.
     * See {@link DOM.append} for more details.
     *
     * @property {function} last
     * Function that returns the last element of the array.
     * See {@link DOM.last} for more details.
     *
     * @property {function} eq
     * Function that returns the element corresponding to the location of the array.
     * See {@link DOM.eq} for more details.
     *
     * @property {function} index
     * Function that returns the index number of the element from the array.
     * See {@link DOM.index} for more details.
     *
     * @property {function} forEach
     * Function to run the given callback function for each element.
     * See {@link DOM.forEach} for more details.
     *
     * @property {function} parent
     * Function to get the ancestor of each element matched to the selector.
     * See {@link DOM.parent} for more details.
     *
     * @property {function} parents
     * Function to get all the ancestors of each element matched to the selector.
     * See {@link DOM.parents} for more details.
     *
     * @property {function} closest
     * Function to get the closest DOM object from parent node and itself.
     * See {@link DOM.closest} for more details.
     *
     * @property {function} addClass
     * Function to add classes to current element.
     * See {@link DOM.addClass} for more details.
     *
     * @property {function} removeClass
     * Function to remove classes from current element.
     * See {@link DOM.removeClass} for more details.
     *
     * @property {function} hasClass
     * Function to check whether current element has specific class.
     * See {@link DOM.hasClass} for more details.
     *
     * @property {function} attr
     * Function to get or set the value of attribute of the element.
     * See {@link DOM.attr} for more details.
     *
     * @property {function} removeAttr
     * Function to remove the given attribute from the element.
     * See {@link DOM.removeAttr} for more details.
     *
     * @property {function} prop
     * Function to get or set the value of the given property of the element.
     * See {@link DOM.prop} for more details.
     *
     * @property {function} val
     * Function to get or set the value of the element.
     * See {@link DOM.val} for more details.
     *
     * @property {function} remove
     * Function to remove the current element from parentNode.
     * See {@link DOM.remove} for more details.
     *
     * @property {function} empty
     * Function to remove all the childNodes from the current element.
     * See {@link DOM.empty} for more details.
     *
     * @property {function} on
     * Function to bind eventListener and handleEvent to the element.
     * See {@link DOM.on} for more details.
     *
     * @property {function} off
     * Function to remove binded eventListener from the elemnet.
     * See {@link DOM.off} for more details.
     *
     * @property {function} text
     * Function to get or set the text value of the element.
     * See {@link DOM.text} for more details.
     */
    var Methods = {
      /**
       * @description
       * Function to check whether the element corresponds to the selector or not.
       * @method is
       * @memberof DOM
       * @param {string | DOM} selector
       * The selector to compare elements.
       * Should be in <code>string</code> or <code>DOM</code> Object types.
       * @returns {boolean}
       * Whether the element matches the selector.
       *
       * <code>true</code> | <code>false</code>
       * @example
       * if (bim.app.element(${1:selector}).is(${2:selector})) {
       *  // Do something ...
       * }
       * @example
       * if (bim.app.element("#bimId").is(".bimClass")) {
       *  // Do something ...
       * }
       */
      is: function is(selector) {
        var el = this[0];
        var compareWith;
        var i;
        if (!el || typeof selector === "undefined") {
          return false;
        }
        if (typeof selector === "string") {
          if (el.matches) {
            return el.matches(selector);
          } else if (el.webkitMatchesSelector) {
            return el.webkitMatchesSelector(selector);
          } else if (el.msMatchesSelector) {
            return el.msMatchesSelector(selector);
          }
          compareWith = __lib.app.element(selector);
          for (i = 0; i < compareWith.length; i += 1) {
            if (compareWith[i] === el) {
              return true;
            }
          }
          return false;
        } else if (selector === document) {
          return el === document;
        } else if (selector === window) {
          return el === window;
        }
        if (selector.nodeType || selector instanceof DOM) {
          compareWith = selector.nodeType ? [selector] : selector;
          for (i = 0; i < compareWith.length; i += 1) {
            if (compareWith[i] === el) {
              return true;
            }
          }
          return false;
        }
        return false;
      },

      /**
       * @description
       * Function to find all Objects with the selector from the element
       *
       * @method find
       * @memberof DOM
       * @param {string | Object} selector
       * The selector which would be used in the query.
       * Should be in <code>string</code> or <code>DOM</code> Object types.
       * @returns {Array}
       * Found Objects in <code>Array</code> type.
       * @example
       * bim.app.element(${1:selector}).find(${2:selector});
       * @example
       * let foundElements = bim.app.element("bim").find("div");
       */
      find: function find(selector) {
        var this$1 = this;

        var foundElements = [];
        for (var i = 0; i < this.length; i += 1) {
          var found = this$1[i].querySelectorAll(selector);
          for (var j = 0; j < found.length; j += 1) {
            foundElements.push(found[j]);
          }
        }
        return new DOM(foundElements);
      },

      /**
       * @description
       * Function to filter the element through the given function which is handed over as a parameter.
       * @method filter
       * @memberof DOM
       * @param {function} callback
       * Filter function ({DOM}, {number}, {DOM})
       *
       * returns {boolean}
       * @returns {Array<DOM>}
       * Filtered Objects in <code>Array<DOM></code> type.
       * @example
       * bim.app.element(${1:selector}).filter(${2:function});
       * @example
       * let filteredElements = bim.app.element(".bimElements").filter(function (obj) {
       *  // Return {boolean} ..
       *  return obj.is(".bimClass");
       * });
       */
      filter: function filter(callback) {
        var matchedItems = [];
        var dom = this;
        for (var i = 0; i < dom.length; i += 1) {
          if (callback.call(dom[i], i, dom[i])) {
            matchedItems.push(dom[i]);
          }
        }
        return new DOM(matchedItems);
      },

      /**
       * @description
       * Function to append array in arguments to the element.
       * @method append
       * @memberof DOM
       * @param {Array} args
       * list of objects expected to be appended to the element
       * @returns {DOM} self
       * @example
       * bim.app.element(${1:selector}).append(${2:array});
       * @example
       * bim.app.element("bim").append("<div>Hello Bim</div>");
       * @example
       * const p = document.createElement("p");
       * bim.app.element("body").append(p);
       */
      append: function append() {
        let this$1 = this;
        let args = [];
        let len = arguments.length;
        while (len--) args[len] = arguments[len];
        for (var k = 0; k < args.length; k += 1) {
          let newChild = args[k];
          for (var i = 0; i < this.length; i += 1) {
            if (typeof newChild === "string") {
              var tempDiv = document.createElement("div");
              tempDiv.innerHTML = newChild;
              while (tempDiv.firstChild) {
                this$1[i].appendChild(tempDiv.firstChild);
              }
            } else if (newChild instanceof DOM) {
              for (var j = 0; j < newChild.length; j += 1) {
                this$1[i].appendChild(newChild[j]);
              }
            } else {
              this$1[i].appendChild(newChild);
            }
          }
        }
        return this;
      },

      /**
       * @description
       * Function that returns the last element of the array.
       * @method last
       * @memberof DOM
       * @returns {boolean | DOM}
       * <code>false</code> if the element is undefined or its length equals to 0 |
       * <code>DOM</code> Object at the last of the given Array.
       * @example
       * bim.app.element(${1:selector}).last();
       * @example
       * bim.app.element("div").last();
       */
      last: function last() {
        if (!this || !this.length) {
          return false;
        }
        return new DOM([this[this.length - 1]]);
      },

      /**
       * @description
       * Function that returns the element corresponding to the location of the array.
       * @method eq
       * @memberof DOM
       * @param {number} index
       * A number for where Object in Array is located.
       * Should be a positive, or negative Integer.
       * @returns {DOM}
       * <code>DOM</code> Object
       * located in the array at the index.
       * @example
       * bim.app.element(${1:selector}).eq(${2:index});
       * @example
       * let listItem = bim.app.element("li").eq(3);
       */
      eq: function eq(index) {
        if (typeof index === "undefined") {
          return this;
        }
        var length = this.length;
        var returnIndex;
        if (index > length - 1) {
          return new DOM([]);
        }
        if (index < 0) {
          returnIndex = length + index;
          if (returnIndex < 0) {
            return new DOM([]);
          }
          return new DOM([this[returnIndex]]);
        }
        return new DOM([this[index]]);
      },

      /**
       * @description
       * Function that returns the index number of the element from the array
       * @method index
       * @memberof DOM
       * @returns {number}
       * The index of the element from the array.
       * @example
       * bim.app.element(${1:selector}).index();
       * @example
       * bim.app.element("li.bimClass").index();
       */
      index: function index() {
        var child = this[0];
        var i;
        if (child) {
          i = 0;
          while ((child = child.previousSibling) !== null) {
            if (child.nodeType === 1) {
              i += 1;
            }
          }
          return i;
        }
      },

      /**
       * @description
       * Function to run the given callback function for each element.
       * If callback function returns <code>false</code>, terminates the loop early.
       * @method forEach
       * @memberof DOM
       * @param {function} callback currentvalue, currentvalue, index as params
       * @returns {DOM} self
       * @example
       * bim.app.element(${1:selector}).forEach(${2:callback});
       * @example
       * bim.app.element("li").forEach(function (item, item2, index) {
       *  // Do Something ...
       *  console.log("li : ", item);
       * });
       */
      forEach: function forEach(callback) {
        var this$1 = this;

        if (!callback) {
          return this;
        }

        for (var i = 0; i < this.length; i += 1) {
          if (callback.call(this$1[i], this$1[i], i) === false) {
            // End the loop early
            return this$1;
          }
        }

        return this;
      },

      /**
       * @description
       * Function to get the ancestor of each element
       * in the current set of elements matched to the selector.
       * @method parent
       * @memberof DOM
       * @param {string= | DOM=} [selector]
       * <b><i>Optional.</i></b>
       * A string containing a selector expression to match elements against. |
       * <code>DOM</code> Object to compare with.
       * @returns {DOM}
       * element
       * @example
       * bim.app.element(${1:selector}).parent();
       * @example
       * bim.app.element(${1:selector}).parent(${2:selector});
       * @example
       * let bimElement = bim.app.element("li").parent("ul");
       */
      parent: function parent(selector) {
        var this$1 = this;
        var parents = [];
        for (var i = 0; i < this.length; i += 1) {
          var parent = this$1[i].parentNode;
          if (parent !== null) {
            if (selector) {
              if (__lib.app.element(parent).is(selector)) {
                parents.push(parent);
              }
            } else {
              parents.push(parent);
            }
          }
        }
        return __lib.app.element(__lib.functions.unique(parents));
      },

      /**
       * @description
       * Function to get all the ancestors of each element
       * in the current set of matched elements matched to the selector.
       * Similar to <code>{@link DOM.parent}</code>, except this travels
       * multiple level up the <code>DOM</code> tree.
       * @method parents
       * @memberof DOM
       * @param {string= | DOM=} [selector]
       * <b><i>Optional.</i></b>
       * A string containing a selector expression to match elements against. |
       * <code>DOM</code> Object to compare with.
       * @returns {DOM}
       * Matched parent <code>DOM</code> Element.
       * @example
       * bim.app.element(${1:selector}).parents();
       * @example
       * bim.app.element(${1:selector}).parents(${2:selector});
       * @example
       * let bimElements = bim.app.element("li").parents("ul");
       */
      parents: function parents(selector) {
        var this$1 = this;

        var parents = [];
        for (var i = 0; i < this.length; i += 1) {
          var parent = this$1[i].parentNode;
          while (parent) {
            if (selector) {
              if (__lib.app.element(parent).is(selector)) {
                parents.push(parent);
              }
            } else {
              parents.push(parent);
            }
            parent = parent.parentNode;
          }
        }
        return __lib.app.element(__lib.functions.unique(parents));
      },

      /**
       * @description
       * Function to get the closest DOM object from parent node and itself.
       * Traverses the element and its parents until it finds a node.
       * If the element is undefined, returns new DOM.
       * @method closest
       * @memberof DOM
       * @param {string | DOM} selector
       * <b><i>Required.</i></b>
       * Valid selector to match the element. |
       * <code>DOM</code> Object to compare with.
       * @returns {DOM}
       * New <code>DOM</code> Object if selector is undefined,
       * else closest parent <code>DOM</code> Object.
       * @example
       * bim.app.element(${1:selector}).closest(${2:selector});
       * @example
       * let closeElement = bim.app.element("li.bimList").closest("ul");
       */
      closest: function closest(selector) {
        var closest = this;
        if (typeof selector === "undefined") {
          return new DOM([]);
        }
        if (!closest.is(selector)) {
          closest = closest.parents(selector).eq(0);
        }
        return closest;
      },

      /**
       * @description
       * Function to add classes to current element.
       * Multiple classes can be added at the same time
       * seperated by "space".
       * @method addClass
       * @memberof DOM
       * @param {string} className
       * Class name
       * @returns {DOM} Self
       * @example
       * bim.app.element(${1:selector}).addClass(${2:className});
       * @example
       * bim.app.element("div").addClass("className1 className2 className3");
       */
      addClass: function addClass(className) {
        var this$1 = this;

        if (typeof className === "undefined") {
          return this;
        }
        var classes = className.split(" ");
        for (var i = 0; i < classes.length; i += 1) {
          for (var j = 0; j < this.length; j += 1) {
            if (typeof this$1[j].classList !== "undefined") {
              this$1[j].classList.add(classes[i]);
            }
          }
        }
        return this;
      },

      /**
       * @description
       * Function to remove classes from current element.
       * Multiple classes can be removed at the same time
       * seperated by "space".
       * @method removeClass
       * @memberof DOM
       * @param {string} className
       * Class name
       * @returns {DOM} Self
       * @example
       * bim.app.element(${1:selector}).removeClass(${2:className});
       * @example
       * bim.app.element("div").removeClass("className1 className2 className3");
       */
      removeClass: function removeClass(className) {
        var this$1 = this;

        var classes = className.split(" ");
        for (var i = 0; i < classes.length; i += 1) {
          for (var j = 0; j < this.length; j += 1) {
            if (typeof this$1[j].classList !== "undefined") {
              this$1[j].classList.remove(classes[i]);
            }
          }
        }
        return this;
      },

      /**
       * @description
       * Function to check whether current element has specific class given as parameter.
       * @method hasClass
       * @memberof DOM
       * @param {string} className
       * Class name
       * @returns {boolean}
       * Whether the element has the class or not.
       *
       * <code>true</code> | <code>false</code>
       * @example
       * bim.app.element(${1:selector}).hasClass(${2:className});
       * @example
       * if (bim.app.element("div").hasClass("className1")) {
       *  // Do something ...
       * }
       */
      hasClass: function hasClass(className) {
        if (!this[0]) {
          return false;
        }
        return this[0].classList.contains(className);
      },

      /**
       * @description
       * Function to get or set the value of attribute of the element.
       * @method attr
       * @memberof DOM
       * @param {string} attrs
       * @param {string=} value
       * @returns {string | DOM}
       * Value of attribute when 1 argument, else Self
       * @example
       * // Gets current element's attr
       * bim.app.element(${1:selector}).attr(${2:attrName});
       * @example
       * // Sets value to current element's attr
       * bim.app.element(${1:selector}).attr(${2:attrName}, ${3:value});
       * @example
       * // Sets values of the given attributes to the element
       * bim.app.element(${1:selector}).attr({
       *  ${2:attrName} : ${3:value},
       *  ${4:attrName} : ${5:value}
       * // ...
       * });
       * @example
       * let inputTitle = bim.app.element("input").attr("title");
       */
      attr: function attr(attrs, value) {
        var arguments$1 = arguments;
        var this$1 = this;

        if (arguments.length === 1 && typeof attrs === "string") {
          // Get attr
          if (this[0]) {
            return this[0].getAttribute(attrs);
          }
          return undefined;
        }

        // Set attrs
        for (var i = 0; i < this.length; i += 1) {
          if (arguments$1.length === 2) {
            // String
            this$1[i].setAttribute(attrs, value);
          } else {
            // Object
            for (var attrName in attrs) {
              this$1[i][attrName] = attrs[attrName];
              this$1[i].setAttribute(attrName, attrs[attrName]);
            }
          }
        }
        return this;
      },

      /**
       * @description
       * Function to remove the given attribute from the element.
       * @method removeAttr
       * @memberof DOM
       * @param {string} attr
       * Attribute expected to be removed
       * @returns {DOM} Self
       * @example
       * bim.app.element(${1:selector}).removeAttr(${2:attr});
       * @example
       * bim.app.element("input").removeAttr("title");
       */
      removeAttr: function removeAttr(attr) {
        var this$1 = this;

        for (var i = 0; i < this.length; i += 1) {
          this$1[i].removeAttribute(attr);
        }
        return this;
      },

      /**
       * @description
       * Function to get or set the value of the given property of the element.
       * @method prop
       * @memberof DOM
       * @param {string | Object} props
       * Property name
       * @param {string=} value
       * String value for property
       * @returns {string | DOM} value of the property of the element | self
       * @example
       * // Get the value of the given property from the element
       * bim.app.element(${1:selector}).prop(${2:propName});
       * @example
       * // Set the value of the given property to the element
       * bim.app.element(${1:selector}).prop(${2:propName}, ${3:value});
       * @example
       * // Set the values of the given properties to the element
       * bim.app.element(${1:selector}).prop({
       *  ${2:propName} : ${3:value},
       *  ${4:propName} : ${5:value}
       * // ...
       * });
       * @example
       * let checked = bim.app.element(".checkBox").prop("checked");
       */
      prop: function prop(props, value) {
        var arguments$1 = arguments;
        var this$1 = this;

        if (arguments.length === 1 && typeof props === "string") {
          // Get prop
          if (this[0]) {
            return this[0][props];
          }
        } else {
          // Set props
          for (var i = 0; i < this.length; i += 1) {
            if (arguments$1.length === 2) {
              // String
              this$1[i][props] = value;
            } else {
              // Object
              for (var propName in props) {
                this$1[i][propName] = props[propName];
              }
            }
          }
          return this;
        }
      },

      /**
       * @description
       * Function to get or set the value of the element.
       * @method val
       * @memberof DOM
       * @param {string=} value
       * Value to assign
       * @returns {string | string[] | DOM}
       * Value of the element | Self
       * @example
       * // Get the value of the element
       * bim.app.element(${1:selector}).val();
       * @example
       * // Set the value of the element
       * bim.app.element(${1:selector}).val(${2:value});
       * @example
       * bim.app.element("input").val("Hello Bim~");
       */
      val: function val(value) {
        var this$1 = this;

        if (typeof value === "undefined") {
          if (this[0]) {
            if (
              this[0].multiple &&
              this[0].nodeName.toLowerCase() === "select"
            ) {
              var values = [];
              for (var i = 0; i < this[0].selectedOptions.length; i += 1) {
                values.push(this$1[0].selectedOptions[i].value);
              }
              return values;
            }
            return this[0].value;
          }
          return undefined;
        }

        for (var i$1 = 0; i$1 < this.length; i$1 += 1) {
          this$1[i$1].value = value;
        }
        return this;
      },

      /**
       * @description
       * Function to remove the current element from parentNode.
       * @method remove
       * @memberof DOM
       * @returns {DOM} Self | parentNode
       * @example
       * bim.app.element(${1:selector}).remove();
       * @example
       * bim.app.element("li").remove();
       */
      remove: function remove() {
        var this$1 = this;

        for (var i = 0; i < this.length; i += 1) {
          if (this$1[i].parentNode) {
            this$1[i].parentNode.removeChild(this$1[i]);
          }
        }
        return this;
      },

      /**
       * @description
       * Function to remove all the childNodes from the current element.
       * @method empty
       * @memberof DOM
       * @returns {DOM} Self
       * @example
       * bim.app.element(${1:selector}).empty();
       * @example
       * bim.app.element("ul").empty();
       */
      empty: function empty() {
        var this$1 = this;

        for (var i = 0; i < this.length; i += 1) {
          var el = this$1[i];
          if (el.nodeType === 1) {
            for (var j = 0; j < el.childNodes.length; j += 1) {
              if (el.childNodes[j].parentNode) {
                el.childNodes[j].parentNode.removeChild(el.childNodes[j]);
              }
            }
            el.textContent = "";
          }
        }
        return this;
      },

      /**
       * @description
       * Function to bind eventListener and handleEvent to the element.
       * @method on
       * @memberof DOM
       * @argument {string} eventType
       * @argument {string=} targetSelector
       * @argument {function} listener
       * @argument {boolean=} [capture=false] true | false
       * @returns {DOM} Self
       * @example
       * // with 2 arguments
       * bim.app.element(${1:selector}).on(${2:eventType}, ${3:listener});
       * @example
       * // with 3 arguments
       * bim.app.element(${1:selector}).on(${2:eventType}, ${3:targetSelector}, ${4:listener});
       * @example
       * // with 4 arguments
       * bim.app.element(${1:selector}).on(${2:eventType}, ${3:targetSelector}, ${4:listener}, ${5:capture});
       */
      on: function on() {
        var this$1 = this;
        var args = [],
          len = arguments.length;
        while (len--) args[len] = arguments[len];

        var eventType = args[0];
        var targetSelector;
        var listener;
        var capture = false;
        if (typeof args[1] === "function") {
          targetSelector = false;
          listener = args[1];
          capture = args[2];
        } else {
          targetSelector = args[1];
          listener = args[2];
          capture = args[3];
        }

        function handleLiveEvent(e) {
          var target = e.target;
          if (!target) {
            return;
          }
          var eventData = e.target.domEventData || [];
          eventData.unshift(e);

          var eTarget = __lib.app.element(target);

          if (eTarget.is(targetSelector)) {
            listener.apply(target, eventData);
          } else {
            var parents = eTarget.parents();
            for (var k = 0; k < parents.length; k += 1) {
              var parentsTarget = __lib.app.element(parents[k]);
              if (parentsTarget.is(targetSelector)) {
                listener.apply(parents[k], eventData);
              }
            }
          }
        }

        function handleEvent(e) {
          var eventData = e && e.target ? e.target.domEventData || [] : [];
          eventData.unshift(e);
          listener.apply(this, eventData);
        }
        var events = eventType.split(" ");
        var j;
        for (var i = 0; i < this.length; i += 1) {
          var el = this$1[i];
          if (!targetSelector) {
            for (j = 0; j < events.length; j += 1) {
              if (!el.domListeners) {
                el.domListeners = [];
              }
              el.domListeners.push({
                type: eventType,
                listener: listener,
                proxyListener: handleEvent,
              });
              el.addEventListener(events[j], handleEvent, capture);
            }
          } else {
            // Live events
            for (j = 0; j < events.length; j += 1) {
              if (!el.domLiveListeners) {
                el.domLiveListeners = [];
              }
              el.domLiveListeners.push({
                type: eventType,
                listener: listener,
                proxyListener: handleLiveEvent,
              });
              el.addEventListener(events[j], handleLiveEvent, capture);
            }
          }
        }
        return this;
      },

      /**
       * @description
       * Function to remove binded eventListener from the elemnet.
       * @method off
       * @memberof DOM
       * @argument {string} eventType
       * @argument {string} targetSelector
       * @argument {function} listener
       * @argument {boolean=} [capture=false] true | false
       * @returns {DOM} self
       * @todo Check the stability
       * @example
       * // with 2 arguments
       * bim.app.element(${1:selector}).off(${2:eventType}, ${3:listener});
       * @example
       * // with 3 arguments
       * bim.app.element(${1:selector}).off(${2:eventType}, ${3:targetSelector}, ${4:listener});
       * @example
       * // with 4 arguments
       * bim.app.element(${1:selector}).off(${2:eventType}, ${3:targetSelector}, ${4:listener}, ${5:capture});
       */
      off: function off() {
        var this$1 = this;
        var args = [],
          len = arguments.length;
        while (len--) args[len] = arguments[len];

        var eventType = args[0];
        var targetSelector;
        var listener;
        var capture = false;
        if (typeof args[1] === "function") {
          targetSelector = false;
          listener = args[1];
          capture = args[2];
        } else {
          targetSelector = args[1];
          listener = args[2];
          capture = args[3];
        }
        var events = eventType.split(" ");
        for (var i = 0; i < events.length; i += 1) {
          for (var j = 0; j < this.length; j += 1) {
            var el = this$1[j];
            if (!targetSelector) {
              if (el.domListeners) {
                for (var k = 0; k < el.domListeners.length; k += 1) {
                  if (listener) {
                    if (el.domListeners[k].listener === listener) {
                      el.removeEventListener(
                        events[i],
                        el.domListeners[k].proxyListener,
                        capture
                      );
                    }
                  } else if (el.domListeners[k].type === events[i]) {
                    el.removeEventListener(
                      events[i],
                      el.domListeners[k].proxyListener,
                      capture
                    );
                  }
                }
              }
            } else if (el.domLiveListeners) {
              for (var k$1 = 0; k$1 < el.domLiveListeners.length; k$1 += 1) {
                if (listener) {
                  if (el.domLiveListeners[k$1].listener === listener) {
                    el.removeEventListener(
                      events[i],
                      el.domLiveListeners[k$1].proxyListener,
                      capture
                    );
                  }
                } else if (el.domLiveListeners[k$1].type === events[i]) {
                  el.removeEventListener(
                    events[i],
                    el.domLiveListeners[k$1].proxyListener,
                    capture
                  );
                }
              }
            }
          }
        }
        return this;
      },

      /**
       * @description
       * Function to get or set the text value of the element.
       * @method text
       * @memberof DOM
       * @param {string=} text$1
       * Text value to assign
       * @returns {string | DOM} Text value | self
       * @example
       * // Get text value of the element
       * bim.app.element(${1:selector}).text();
       * @example
       * // Set text value to the element
       * bim.app.element(${1:selector}).text(${2:text});
       * @example
       * bim.app.elemnt("h1").text("Hello Bim~");
       */
      text: function text(text$1) {
        var this$1 = this;

        if (typeof text$1 === "undefined") {
          if (this[0]) {
            return this[0].textContent.trim();
          }
          return null;
        }

        for (var i = 0; i < this.length; i += 1) {
          this$1[i].textContent = text$1;
        }
        return this;
      },
    };

    tokenizer.use(Methods);

    return {
      /**
       * @description
       * Collection which manages the overall functions of the App.
       * @namespace bim.app
       * @type {Object}
       * @property {function} formData
       * Function to get data from form.
       * See {@link bim.app.formData} for more details.
       *
       * @property {function} checkAppVersion
       * Function to check app version natively.
       * See {@link bim.app.checkAppVersion} for more details.
       *
       * @property {function} checkAppVersionCallback
       * Callback function called after checking app version natively.
       * See {@link bim.app.checkAppVersionCallback} for more details.
       *
       * @property {function} onGoBack
       * Callback function when called on go back.
       * See {@link bim.app.onGoBack} for more details.
       *
       * @property {function} scrollTop
       * Function to change scroll position to top.
       * See {@link bim.app.scrollTop} for more details.
       *
       * @property {function} init
       * Function to initialize the App by setting up
       * the information of current plugin.
       * See {@link bim.app.init} for more details.
       *
       * @property {Object} data
       * Core System, Collection of functions for sharing data
       * between diffrent plugins.
       * See {@link bim.app.data} for more details.
       *
       * @property {Object} user
       * Collection of functions to control data of user of the App.
       * See {@link bim.app.user} for more details.
       *
       * @property {function} session
       * Function to get or set user session.
       * See {@link bim.app.session} for more details.
       *
       * @property {function} getAppId
       * Function to get the App's ID.
       * See {@link bim.app.getAppId} for more details.
       *
       * @property {function} setAppId
       * Function to set App's ID.
       * See {@link bim.app.setAppId} for more details.
       *
       * @property {function} popup
       * Function to show popup.
       * See {@link bim.app.popup} for more details.
       *
       * @property {function} page
       * Function to create new page in plugin.
       * See {@link bim.app.page} for more details.
       *
       * @property {function} listener
       * Function to add Event Listener to document.
       * See {@link bim.app.listener} for more details.
       *
       * @property {Object} listeners
       * Object which has a Function to make the last step to firing an event.
       * See {@link bim.app.listeners} for more details.
       *
       * @property {function} navigation
       * Function to add Event Listener "navigation" to document.
       * See {@link bim.app.navigation} for more details.
       *
       * @property {Object} close
       * Object, which has a Function to close opened page in plugin.
       * See {@link bim.app.close} for more details.
       *
       * @property {function} element
       * Function to return {@link DOM} Element.
       * See {@link bim.app.element} for more details.
       *
       * @property {Object} logicless
       * See {@link bim.app.logicless} for more details.
       *
       * @property {function} template
       * Function to return templated "bim" element
       * See {@link bim.app.template} for more details.
       *
       * @property {Object} storyboard
       * Collection of functions to handle the storyboard of the App.
       * See {@link bim.app.storyboard} for more details.
       *
       * @property {function} appendHTML
       * Funciton to append HTML on document.
       * See {@link bim.app.appendHTML} for more details.
       *
       * @property {function} isHTML
       * Function to check whether is a HTML tag or not.
       * See {@link bim.app.isHTML} for more details.
       *
       * @property {function} translate
       * Function to translate language.
       * See {@link bim.app.translate} for more details.
       */
      app: {
        /**
         * @description
         * Function to get data from form.
         * @since 23.03.07
         * @memberof bim.app
         * @function formData
         * @param {DOM | string} params
         * <b><i>Required</i></b>.
         * Selector to match the Element of <code>form</code>.
         * @returns {boolean | Object}
         * Returns <code>false</code>
         * if parameter is empty or
         * there is not only one inquired <code>form</code>. |
         *
         * Returns extract data from the inquired
         * <code>form</code> in <code>Object</code> format.
         */
        formData: function (params) {
          if (!params || params == "") return false;

          var form = __lib.app.element(params);
          if (form.length !== 1) return false;

          var formData = {};

          var skipTypes = ["submit", "image", "button", "file"];
          var skipNames = [];
          form.find("input, select, textarea").each(function () {
            var input = __lib.app.element(this);
            var name = input.attr("name");
            var type = input.attr("type");
            var tag = this.nodeName.toLowerCase();
            if (skipTypes.indexOf(type) >= 0) return;
            if (skipNames.indexOf(name) >= 0 || !name) return;
            if (tag === "select" && input.prop("multiple")) {
              skipNames.push(name);
              formData[name] = [];
              form.find('select[name="' + name + '"] option').each(function () {
                if (this.selected) formData[name].push(this.value);
              });
            } else {
              switch (type) {
                case "checkbox":
                  skipNames.push(name);
                  formData[name] = [];
                  form.find('input[name="' + name + '"]').each(function () {
                    if (this.checked) formData[name].push(this.value);
                  });
                  break;
                case "radio":
                  skipNames.push(name);
                  form.find('input[name="' + name + '"]').each(function () {
                    if (this.checked) formData[name] = this.value;
                  });
                  break;
                default:
                  formData[name] = input.val();
                  break;
              }
            }
          });

          return formData;
        },

        /**
         * @description
         * Function to send a request to native to check
         * whether there's an update in app store or not.
         * @deprecated
         * ONLY USED IN SPECIFIC APP NOW.
         * APP VERSION CAN BE CHECKED NATIVELY BY ITSELF.
         * @since 22.08.17
         * @function checkAppVersion
         * @memberof bim.app
         * @param {Object} params
         * <b><i>Required.</i></b>
         * @param {string} params.title
         * @param {string} params.message
         * @returns {}
         */
        checkAppVersion: function (params) {
          if (!params) {
            return;
          }
          console.log(
            "HTB_NATIVE_LOG",
            "bim.app.checkAppVersion()",
            "App version check called .."
          );
        },

        /**
         * @description
         * Callback function for app version check.
         * Show alert with <code>title</code> and <code>message</code>.
         * Redirects to external link natively.
         * @deprecated
         * ONLY USED IN SPECIFIC APP NOW.
         * APP VERSION CAN BE CHECKED NATIVELY BY ITSELF.
         * @since 22.08.17
         * @function checkAppVersionCallback
         * @memberof bim.app
         * @param {Object} params
         * <b><i>Required.</i></b>
         * @param {boolean} params.app_update_check
         * <b><i>Required.</i></b>
         * @param {string} params.app_name
         * <b><i>Required.</i></b>
         * Used to show externalLink in Android.
         * @param {string} params.trackId
         * Used to show externalLink in iOS.
         * @param {string} params.title
         * @param {string} params.message
         * @returns {}
         */
        checkAppVersionCallback: function (params) {
          if (
            !params ||
            params == "" ||
            !params.app_update_check ||
            !params.app_name
          ) {
            return;
          }
          console.log(
            "HTB_NATIVE_LOG",
            "bim.app.checkAppVersionCallback()",
            "App version callback called .."
          );
        },

        /**
         * @description
         * Callback function when called on go back.
         * @memberof bim.app
         * @function onGoBack
         */
        onGoBack: function () {},

        /**
         * @description
         * Function to change scroll position of Element to top.
         * @since 22.03.17
         * @memberof bim.app
         * @function scrollTop
         * @param {string | DOM} params
         * <b><i>Required.</i></b>
         * selector
         * @returns {}
         */
        scrollTop: function (params) {
          if (!params || params == "") return;

          if (params.trim().charAt(0) === ".") {
            bim.app.element(params)[0].scroll(0, 0);
          } else if (params.trim().charAt(0) === "#") {
            bim.app.element(params).scroll(0, 0);
          }
        },

        /**
         * @description
         * Function to initialize the App by setting up
         * the information of current plugin.
         *
         * Sets
         * <code>id</code>,
         * <code>name</code>,
         * <code>token</code>
         * of the plugin to private variables.
         *
         * Load external JavaScript if received through parameters
         * <code>params.onReady.js</code>.
         * @since 22.03.17
         * @memberof bim.app
         * @async
         * @function init
         * @param {Object} params
         * <b><i>Required.</i></b>
         * @param {string} params.pluginId
         * <b><i>Required.</i></b>
         * Plugin id
         * @param {string} params.name
         * <b><i>Required.</i></b>
         * Plugin name
         * @param {Object=} params.onReady
         * @param {Array<string>} params.onReady.js
         * Should specify the path and filename
         * where the script is located.
         * @returns {}
         */
        init: async function (params) {
          if (!params) return console.error("bim.init() need to insert object");
          if (!params.pluginId) return console.error("plugin id is necessary");
          if (!params.name) return console.error("plugin name is necessary");

          // set token
          try {
            await __lib.plugin.token.set();
          } catch (e) {
            console.error(e);
          }

          __lib.plugin.id.set(params.pluginId);
          __lib.plugin.name.set(params.name);
          __lib.app.data.set(
            JSON.stringify({
              navigation: {
                plugin: params.pluginId,
                href: window.location.href,
                pathname: window.location.pathname,
                protocol: window.location.protocol,
                port: window.location.port,
                origin: window.location.origin,
                hostname: window.location.hostname,
              },
            })
          );

          document.onreadystatechange = function () {
            if (document.readyState === "complete") {
              bim.plugin.onResume();
            }
          };
          __lib.crawling.inject.css.apply(
            ".bim-singleView{position:absolute;top:0;height:100%;background-color:white;width:100%;z-index:6666;}.bim-singleView-init{left:120%;}.bim-page-from-left{transform:translateX(0.1%);transition:0.3s;left:0%;}.bim-page-hide-from-left{transform:translateX(120%);transition:0.7s;}"
          );

          if (params.onReady && params.onReady !== "") {
            for (var x = 0; x < params.onReady.js.length; x++) {
              var response = {};
              // load external JS libraries
              var script = document.createElement("script");
              script.src = params.onReady.js[x] + ".js";
              script.async = true;
              script.onload = () => {};
              script.onerror = ee => {
                response.error = true;
                response.message = "Err-98enrr: loading library";

                console.error(response);
              };

              //var firstScriptTag = document.getElementsByTagName('script')[0];
              //firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
              document.body.appendChild(script);
            }
          }
        },

        /**
         * @description
         * Core System.
         * Collection of functions to control data
         * through internal database for sharing data
         * between diffrent plugins as an App.
         * @since 22.03.17
         * @namespace bim.app.data
         * @type {Object}
         * @property {function} bundle
         * Function to get data from the internal database (indexedDB).
         * See {@link bim.app.data.bundle} for more details.
         *
         * @property {Object} db
         * See {@link bim.app.data.db} for more details.
         *
         * @property {function} set
         * See {@link bim.app.data.set} for more details.
         *
         * @property {function} get
         * See {@link bim.app.data.get} for more details.
         *
         * @property {function} deleteItem
         * See {@link bim.app.data.deleteItem} for more details.
         */
        data: {
          /**
           * @memberof bim.app.data
           * @callback bundleCallback
           * @param {Object} params
           * @param {boolean} params.error
           * Passes whether failed or not.
           * @param {string} params.message
           * Passes error message when fails.
           * @param {Error} params.description
           * Passes <code>Error</code> Object when fails.
           * <code>Error</code>객체 자체로 넘겨줌.
           * @param {string} params.errno
           * Passes error number when fails.
           * @param {Object} params.data
           * Passes the result data when success.
           */
          /**
           * @see {@link https://w3c.github.io/IndexedDB/#introduction IndexedDB Description}
           * @description
           * Function to get item from the internal database.
           * Uses <code>indexedDB</code>.
           * @memberof bim.app.data
           * @function bundle
           * @param {Object} params
           * <b><i>Required.</i></b>
           * @param {Object} params.db
           * <b><i>Required.</i></b>
           * @param {string} params.db.name
           * <b><i>Required.</i></b>
           * Used for opening <code>indexedDB</code>.
           * @param {string} params.db.table
           * <b><i>Required.</i></b>
           * Used for getting <code>ObjectStore</code> from <code>transaction</code>.
           * @param {string} params.db.key
           * <b><i>Required.</i></b>
           * Used for getting item from <code>ObjectStore</code>.
           * @param {bim.app.data.bundleCallback} callback
           * <b><i>Required.</i></b>
           * Callback function called with result data.
           * See {@link bim.app.data.bundleCallback} for more details.
           * @example
           * bim.app.data.bundle({
           *  db: {
           *    name: ${1: name},
           *    table: ${2: table},
           *    key: ${3: key}
           *  }
           * }, ${4: callback});
           * @example
           * bim.app.data.bundle({
           *  db: {
           *    name: "storyboard", // Name of indexedDB
           *    table: "plugins",   // Which ObjectStore would be used
           *    key: "PLUGINS"      // The key of the item
           *  }
           * }, function (res) {
           *  if (res.error) {
           *    // Do Something ...
           *    console.error(res.message);
           *  } else {
           *    // Do Something ...
           *    bim.app.storyboard.moveTo({
           *      page: page,
           *      plugin: plugin_name,
           *      index: true
           *    });
           *  }
           * });
           */
          bundle: function (params, callback) {
            var db = {};

            // HANDLE ERROR
            db.onerror = function (e) {
              callback({
                error: true,
                message: "There was a problem reading app database",
              });
            };

            var req = window.indexedDB.open(params.db.name);

            req.onsuccess = event => {
              db.database = req.result;
              db.database.onclose = () => {
                console.log("Database connection closed");
              };

              try {
                var transaction = db.database.transaction(
                  [params.db.table],
                  "readwrite"
                );
                var objectStore = transaction.objectStore(params.db.table);
                var objectStoreRequest = objectStore.get(params.db.key);
                objectStoreRequest.onsuccess = function (event) {
                  var result = objectStoreRequest.result;
                  return callback({ error: false, data: result.data });
                };
              } catch (e) {
                return callback({
                  error: true,
                  description: e,
                  errno: "e927b362",
                  message: "Something went wrong creating bundle!",
                });
              }
            };
          },

          /**
           * @description
           * DB
           * @namespace bim.app.data.db
           * @type {Object}
           * @property {Object} version
           * See {@link bim.app.data.db.version} for more details.
           *
           * @property {function} addTable
           * See {@link bim.app.data.db.addTable} for more details.
           *
           * @property {function} delete
           * See {@link bim.app.data.db.delete} for more details.
           *
           * @property {function} create
           * See {@link bim.app.data.db.create} for more details.
           *
           * @property {function} addItem
           * See {@link bim.app.data.db.addItem} for more details.
           *
           * @property {function} addFile
           * See {@link bim.app.data.db.addFile} for more details.
           *
           * @property {function} read
           * See {@link bim.app.data.db.read} for more details.
           *
           * @property {function} deleteFile
           * See {@link bim.app.data.db.deleteFile} for more details.
           *
           * @property {function} deleteItem
           * See {@link bim.app.data.db.deleteItem} for more details.
           */
          db: {
            /**
             * @description
             * Version
             * @memberof bim.app.data.db
             * @name version
             * @type {Object}
             * @property {function} set
             * Push the version of database which is Object in {db: {string}, version: {string}} to private variable <code>\_\_versions\_\_ {Array}</code>.
             * @property {function} get
             * See {@link bim.app.data.db.version.get} for more details
             */
            version: (function () {
              var __versions__ = [];

              return {
                /**
                 * @description
                 * Set version for db
                 * @memberof bim.app.data.db.version
                 * @function set
                 * @param {Object} params
                 * @param {string} params.db
                 * Database name
                 * @param {string} params.version
                 * Version of the database
                 * @example
                 * bim.app.data.db.set({db: ${1:_db}, version: ${2:_version}});
                 */
                set: function (params) {
                  var item = {};
                  item.db = params.db;
                  item.version = params.version;
                  __versions__.push(item);
                },

                /**
                 * @description
                 * Get version of db.
                 * @memberof bim.app.data.db.version
                 * @function get
                 * @param {Object} params
                 * @param {string} params.db
                 * @example
                 * bim.app.data.db.get({db: ${1:_db});
                 */
                get: function (params) {
                  for (var i = 0; i < __versions__.length; i++) {
                    if (__versions__[i].db === params.db) {
                      return __versions__[i];
                    }
                  }
                },
              };
            })(),

            /**
             * @description
             * Add Table
             * @memberof bim.app.data.db
             * @function addTable
             * @param {Object} params
             * @param {Object} params.db
             * @param {string} params.db.name
             * @param {string} params.db.version
             * @param {string} params.db.table
             * @param {string} params.db.keyPath
             * @param {string} params.db.columns
             * @param {function} callback
             */
            addTable: function (params, callback) {
              var db = {};

              var request = indexedDB.open(params.db.name, params.db.version);

              request.onupgradeneeded = function (e) {
                db.database = e.target.result;
                //const objectStore = db.database.createObjectStore(config.db.table);
                const objectStore = db.database.createObjectStore(
                  params.db.table,
                  { keyPath: params.db.keyPath }
                );

                for (var i = 0; i < params.db.columns.length; i++) {
                  objectStore.createIndex(
                    params.db.columns[i],
                    params.db.columns[i],
                    { unique: false }
                  );
                }

                if (typeof callback === "function")
                  callback({ error: false, description: "All set!" });
              };

              request.onsuccess = function (e) {
                var database = e.target.result;

                //code to verify that the table was created
                database.objectStoreNames.contains(params.db.table);

                database.close();
              };

              request.onerror = function (e) {
                return callback({
                  error: true,
                  message: "We couldn't add tables",
                  errno: "e0734g37",
                  description: e,
                });
              };
            },

            /**
             * @description
             * Delete
             * @memberof bim.app.data.db
             * @function delete
             * @param {*} params
             * @param {*} callback
             */
            delete: function (params, callback) {
              var req = indexedDB.deleteDatabase(params.db);
              req.onsuccess = function () {
                console.log("Deleted database successfully");
              };
              req.onerror = function (e) {
                return callback({
                  error: true,
                  message: "We couldn't delete database",
                  errno: "e0734g36",
                  description: e,
                });
              };
              req.onblocked = function () {
                console.warn(
                  "Couldn't delete database due to the operation being blocked"
                );
              };
            },

            /**
             * @description
             * Create
             * @memberof bim.app.data.db
             * @function create
             * @param {*} params
             * @param {*} callback
             */
            create: function (params, callback) {
              var db = {};

              // HANDLE ERROR
              db.onerror = function (e) {
                //console.warn('Full description: ', e);
                if (typeof callback === "function")
                  callback({
                    error: true,
                    errno: "e0782b6bnN",
                    description: e,
                  });
              };

              // SPECIFICALLY FOR DATABASE CREATION ONLY
              db.createDatabase = function (config) {
                var req = window.indexedDB.open(config.db.name);

                // CREATE DATABASE IF NOT EXISTS
                var upgraded = false;
                req.onupgradeneeded = function (e) {
                  db.database = e.target.result;
                  //const objectStore = db.database.createObjectStore(config.db.table);
                  const objectStore = db.database.createObjectStore(
                    config.db.table,
                    { keyPath: config.db.keyPath }
                  );

                  for (var i = 0; i < config.db.columns.length; i++) {
                    objectStore.createIndex(
                      config.db.columns[i],
                      config.db.columns[i],
                      { unique: false }
                    );
                  }

                  upgraded = true;
                  if (typeof callback === "function")
                    return callback({ error: false, description: "All set!" });
                };

                req.onsuccess = e => {
                  db.database = req.result;

                  // db.database.version
                  if (req.result.objectStoreNames.length == 0) {
                    // it means it wasnt created and need to be upgraded
                    if (typeof callback === "function")
                      return callback({
                        error: true,
                        description: "DB version should be upgraded",
                        message: "Something went wrong processing files",
                      });
                  } else if (
                    e.target.result.name &&
                    typeof e.target.result.name == "string" &&
                    e.target.result.name.length > 0
                  ) {
                    if (typeof callback === "function")
                      return callback({
                        error: false,
                        process: 3,
                        description: "DB exists already!",
                      });
                  }

                  db.database.onclose = () => {
                    console.warn("Database connection closed");
                  };
                };

                req.onerror = e => {
                  console.error("onerror ", e);
                };
              };

              db.createDatabase(params);
            },

            /**
             * @description
             * Add Item
             * @memberof bim.app.data.db
             * @function addItem
             * @param {*} params
             */
            addItem: function (params) {
              var db = {};

              // HANDLE ERROR
              db.onerror = function (e) {
                // ON ERROR HERE IT MEANS VALUE ALREADY EXISTS!
                //console.error('Full description: ', e);
              };

              // ADD DATA TO DATABASE
              db.addItems = function (data) {
                let newItem = {};
                newItem[data.keyPath.name] = data.keyPath.value;
                if (
                  Object.prototype.toString.call(data.items.data) ==
                  "[object Array]"
                ) {
                  newItem.data = data.items.data[0];
                } else if (typeof data.items.data == "string") {
                  newItem.data = data.items.data;
                }

                try {
                  var transaction = db.database.transaction(
                    [data.config.db.table],
                    "readwrite"
                  );
                  let objectStore = transaction.objectStore(
                    data.config.db.table
                  );
                  let objectStoreRequest = objectStore.add(newItem);

                  objectStoreRequest.onsuccess = function (e) {
                    console.log("Successfully added item: ", e);
                  };

                  objectStoreRequest.onerror = db.onerror;
                } catch (e) {
                  return callback({
                    error: true,
                    description: e,
                    errno: "e0n2b36",
                    message: "Something went wrong adding Item!",
                  });
                }
              };

              var req = window.indexedDB.open(params.db.name);

              req.onsuccess = event => {
                db.database = req.result;
                db.database.onclose = () => {
                  console.log("Database connection closed");
                };

                if (params.db.items) {
                  for (var i = 0; i < params.db.items.length; i++) {
                    db.addItems({
                      config: params,
                      items: params.db.items[i],
                      keyPath: params.db.keyPath,
                    });
                  }
                }

                if (params.db.item) {
                  db.addItems({
                    config: params,
                    items: params.db.item,
                    keyPath: params.db.keyPath,
                  });
                }
              };
            },

            /**
             * @description
             * Add File
             * @memberof bim.app.data.db
             * @function addFile
             * @param {*} params
             * @param {*} callback
             */
            addFile: function (params, callback) {
              var db = {};

              db.onerror = function (e) {
                console.error("Full description: ", e);
              };

              db.addItems = function (data) {
                let newItem = {};
                newItem[data.keyPath.name] = data.keyPath.value;
                newItem.data = data.items;

                try {
                  var transaction = db.database.transaction(
                    [data.config.db.table],
                    "readwrite"
                  );
                  if (!transaction || !transaction.objectStore)
                    return callback({ error: true, description: transaction });
                  var objectStore = transaction.objectStore(
                    data.config.db.table
                  );
                  var objectStoreRequest = objectStore.put(newItem);

                  objectStoreRequest.onsuccess = function (e) {
                    if (typeof callback === "function")
                      return callback({ error: false, description: e });
                  };

                  objectStoreRequest.onerror = db.onerror;
                } catch (e) {
                  return callback({
                    error: true,
                    description: e,
                    errno: "eO9bn2633v55A",
                    message: "Something went wrong adding files!",
                  });
                }
              };

              var req = window.indexedDB.open(params.db.name);

              req.onsuccess = event => {
                db.database = req.result;
                db.database.onclose = () => {
                  console.log("Database connection closed");
                };

                if (params.db.items) {
                  for (var i = 0; i < params.db.items.length; i++) {
                    db.addItems({
                      config: params,
                      items: params.db.items[i],
                      keyPath: params.db.keyPath,
                    });
                  }

                  console.log("ALL FILES ADDED! ");
                }

                if (params.db.item) {
                  db.addItems({
                    config: params,
                    items: params.db.item,
                    keyPath: params.db.keyPath,
                  });
                }
              };
            },

            /**
             * @description
             * Read
             * @memberof bim.app.data.db
             * @function read
             * @param {*} params
             * @param {*} callback
             */
            read: function (params, callback) {
              var db = {};

              // HANDLE ERROR
              db.onerror = function (e) {
                return callback({
                  error: true,
                  message: "There was a problem reading app database",
                });
              };

              var req = window.indexedDB.open(params.db.name);

              req.onsuccess = event => {
                db.database = req.result;
                db.database.onclose = () => {
                  console.log("Database connection closed");
                };

                var transaction = db.database.transaction(
                  [params.db.table],
                  "readwrite"
                );

                try {
                  var objectStore = transaction.objectStore(params.db.table);
                  var objectStoreRequest = objectStore.get(params.db.key);

                  objectStoreRequest.onsuccess = function (event) {
                    var result = objectStoreRequest.result;
                    var error = objectStoreRequest.error;

                    console.log("objectStoreRequest ", objectStoreRequest);

                    if (!error && result && result.data) {
                      if (typeof callback === "function")
                        return callback({ error: false, data: result.data });
                    } else if (!error && !result) {
                      if (typeof callback === "function")
                        return callback({
                          error: false,
                          data: "Storage is empty",
                        });
                    } else {
                      if (typeof callback === "function")
                        return callback({
                          error: true,
                          message: "No file was found",
                          errno: "e09273b8",
                        });
                    }
                  };
                } catch (e) {
                  return callback({
                    error: true,
                    description: e,
                    errno: "eO902b63vv5",
                    message: "Something went wrong reading the file!",
                  });
                }
              };
            },

            /**
             * @description
             * Delete File
             * @memberof bim.app.data.db
             * @function deleteFile
             * @param {*} params
             * @param {*} callback
             */
            deleteFile: function (params, callback) {
              var db = {};

              db.onerror = function (e) {
                return callback({
                  error: true,
                  message: "There was a problem reading app database",
                });
              };

              var req = window.indexedDB.open(params.db.name);

              req.onsuccess = event => {
                db.database = req.result;
                // console.log("db.database:", db.database);
                // console.log("db.database:", req.result.version);
                db.database.onclose = () => {
                  console.log("Database connection closed");
                };

                if (db.database.objectStoreNames.length > 0) {
                  try {
                    var transaction = db.database.transaction(
                      [params.db.table],
                      "readwrite"
                    );
                    // console.log("transaction:", transaction);
                    var objectStore = transaction.objectStore(params.db.table);
                    var objectStoreRequest = objectStore.delete(params.db.key);

                    objectStoreRequest.onsuccess = function (event) {
                      var error = objectStoreRequest.error;
                      if (!error) {
                        if (typeof callback === "function")
                          return callback({
                            error: false,
                            version: db.database.version,
                            data: "File deleted successfully!",
                          });
                      } else {
                        if (typeof callback === "function")
                          return callback({
                            error: true,
                            message: "Oops! Something went wrong",
                            errno: "e07b363b4",
                          });
                      }
                    };
                  } catch (e) {
                    return callback({
                      error: true,
                      description: e,
                      errno: "eO8b990172",
                      message: "Something went wrong deleting the file!",
                    });
                  }
                } else {
                  return callback({
                    error: false,
                    length: 0,
                    version: db.database.version,
                    description: "No files stored found",
                  });
                }
              };
            },

            /**
             * @description
             * Delete Item
             * @memberof bim.app.data.db
             * @function deleteItem
             * @param {*} params
             * @param {*} callback
             */
            deleteItem: function (params, callback) {
              var db = {};

              db.onerror = function (e) {
                console.error("error ", e);
                return callback({
                  error: true,
                  message: "There was a problem reading app database",
                });
              };

              var req = window.indexedDB.open(params.db.name);

              req.onsuccess = event => {
                db.database = req.result;
                db.database.onclose = () => {
                  console.log("Database connection closed");
                };

                var transaction = db.database.transaction(
                  [params.db.table],
                  "readwrite"
                );
                var objectStore = transaction.objectStore(params.db.table);
                var objectStoreRequest = objectStore.delete(params.db.item);

                objectStoreRequest.onsuccess = function (event) {
                  try {
                    var result = objectStoreRequest.result;
                    return callback({ error: false, data: result });
                  } catch (e) {
                    return callback({
                      error: true,
                      description: e,
                      errno: "eDn2723b",
                      message: "Something went wrong deleting the Item!",
                    });
                  }
                };
              };
            },
          },

          /**
           * @description
           * Set
           * @memberof bim.app.data
           * @function set
           * @param {*} params
           * @returns {}
           */
          set: function (params) {
            if (!params || params == "") return;

            if (
              !params.file &&
              Object.prototype.toString.call(params) === "[object Object]"
            ) {
              var objectKey = Object.keys(params);
              if (!objectKey || !objectKey[0]) return;
              if ((!params[objectKey[0]] !== params[objectKey[0]]) == "")
                return;

              if (
                Object.prototype.toString.call(params[objectKey[0]]) ===
                "[object String]"
              ) {
                window.localStorage.setItem(objectKey[0], params[objectKey[0]]);
              } else if (
                Object.prototype.toString.call(params[objectKey[0]]) ===
                "[object Object]"
              ) {
                window.localStorage.setItem(
                  objectKey[0],
                  JSON.stringify(params[objectKey[0]])
                );
              }
            }

            return;
          },

          /**
           * @description
           * Get
           * @memberof bim.app.data
           * @function get
           * @param {*} params
           * @param {*} callback
           * @returns {}
           */
          get: function (params, callback) {
            if (!params || params == "") return;

            if (typeof callback == "function")
              return callback(window.localStorage.getItem(params));
            return window.localStorage.getItem(params);
          },

          /**
           * @description
           * Delete Item
           * @memberof bim.app.data
           * @function deleteItem
           * @param {*} params
           * @param {*} callback
           * @returns {}
           */
          deleteItem: function (params, callback) {
            if (!params || params == "" || typeof params !== "string")
              return callback({
                error: true,
                errno: "e0o384",
                description: "Item is required!",
              });

            try {
              window.localStorage.removeItem(params);

              return callback({
                error: false,
                message: "Item delete successfully!",
              });
            } catch (e) {
              return { error: true, errno: "o08723bX", description: e };
            }
          },
        },

        /**
         * @description
         * User API.
         * Collection of functions to control data of user with <code>localStorage</code>
         * @since 22.03.17
         * @namespace bim.app.user
         * @property {Object} _uid
         * Object which contains functions to generate texts for user ID.
         * See {@link bim.app.user._uid} for more details.
         *
         * @property {Object} token
         * Object which contains functions to set or get user token.
         * See {@link bim.app.user.token} for more details.
         *
         * @property {function} name
         * Function to get user name from local storage if exists.
         * See {@link bim.app.user.name} for more details.
         *
         * @property {function} uid
         * Function to get user ID from local storage if exists.
         * See {@link bim.app.user.uid} for more details.
         *
         * @property {function} loggedIn
         * Function to check whether user is logged in or not.
         * See {@link bim.app.user.loggedIn} for more details.
         *
         * @property {function} register
         * Function to register user information to local storage.
         * See {@link bim.app.user.register} for more details.
         *
         * @property {function} logout
         * Function to log out user.
         * See {@link bim.app.user.logout} for more details.
         */
        user: (function () {
          var user = window.localStorage;

          return {
            /**
             * @description
             * Object which contains functions to generate texts
             * for user ID.
             * @memberof bim.app.user
             * @name _uid
             * @type {Object}
             * @property {function} text
             * Function to serve randomly generated text.
             * @example
             * bim.app.user._uid.text();
             * @property {function} generate
             * Function to serve randomly generated text with date.
             * @example
             * bim.app.user._uid.generate();
             */
            _uid: {
              /**
               * @description
               * Function that returns randomly generated text.
               * @memberof bim.app.user._uid
               * @function text
               * @param {Number} [n=16]
               * @returns {string}
               * Randomly generated text.
               * @example
               * bim.app.user._uid.text();
               * @example
               * bim.app.user._uid.text(${1:number});
               * @example
               * let randomText = bim.app.user._uid.text(20);
               */
              text: function (n) {
                if (typeof n !== "number") {
                  n = 16;
                }
                var text = "";
                var possible =
                  "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";

                for (var i = 0; i < n; i++) {
                  text += possible.charAt(
                    Math.floor(Math.random() * possible.length)
                  );
                }

                return text;
              },

              /**
               * @description
               * Function to serve randomly generated text with date.
               * @memberof bim.app.user._uid
               * @function generate
               * @returns {string}
               * Randomly generated text with date.
               * @example
               * bim.app.user._uid.generate();
               */
              generate: function () {
                var n = Date.now();

                return (
                  __lib.functions.text() + n + Math.floor(Math.random() * 100)
                );
              },
            },

            /**
             * @description
             * Object which contains functions to set or get the item,
             * user token, to <code>localStorage</code>
             * using "token" as the key.
             * @memberof bim.app.user
             * @name token
             * @type {Object}
             * @property {function} set
             * Function to set <code>localStorage</code> item using "token" as a key.
             * @property {function} get
             * Function to get <code>localStorage</code> item using "token" as a key.
             * @example
             * bim.app.user.token.get();
             */
            token: (function () {
              return {
                /**
                 * @description
                 * Function to set <code>localStorage</code> item using "token" as a key.
                 * @memberof bim.app.user.token
                 * @function set
                 * @param {string} token
                 * @returns {}
                 */
                set: function (token) {
                  user.setItem("token", token);
                },

                /**
                 * @description
                 * Function to get <code>localStorage</code> item using "token" as a key.
                 * @memberof bim.app.user.token
                 * @function get
                 */
                get: function () {
                  localStorage.getItem("token");
                },
              };
            })(),

            /**
             * @description
             * Searches by "user" from local storage,
             * if exists, calls callback function with it as a parameter and returns it.
             * Else, returns false.
             * @memberof bim.app.user
             * @function name
             * @param {callback} callback
             * @returns {string | boolean}
             * <code>localStorage</code> item using key "user" | <code>false</code>
             * @example
             * bim.app.user.name();
             */
            name: function (callback) {
              if (user.getItem("user")) {
                if (typeof callback == "function")
                  callback(localStorage.getItem("user"));

                return localStorage.getItem("user");
              } else {
                return false;
              }
            },

            /**
             * @description
             * Searches by "uid" from local storage,
             * if exists, calls callback function with it as a parameter and returns it.
             * Else, returns false.
             * @memberof bim.app.user
             * @function uid
             * @param {callback} callback
             * @returns {* | boolean} localStorage's item using key "uid" | false
             * @example
             * bim.app.user.uid();
             */
            uid: function (callback) {
              if (user.getItem("uid")) {
                if (typeof callback == "function")
                  callback(localStorage.getItem("uid"));

                return localStorage.getItem("uid");
              } else {
                return false;
              }
            },

            /**
             * @description
             * Function to check whether user is logged in or not.
             * Check items with "uid" & "user", as keys, from localstorage.
             * If callback is a function,
             * calls it with existence of items (true, false) as parameter.
             * Else, returns whether they exist or not.
             * @memberof bim.app.user
             * @function loggedIn
             * @param {function=} callback
             * <b><i>Optional.</i></b>
             * Callback function
             * @returns {* | boolean}
             * Result of callback if it is a function | Whether logged in or not. <code>true</code>, <code>false</code>
             * @example
             * bim.app.user.loggedIn(${1:callback});
             */
            loggedIn: function (callback) {
              if (__lib.app.user.uid() && __lib.app.user.name()) {
                if (typeof callback == "function") return callback(true);
                return true;
              }

              if (typeof callback == "function") return callback(false);
              return false;
            },

            /**
             * @description
             * Function to register user information to local storage.
             * Saves data "user", "uid" into <code>localStorage</code>.
             * @memberof bim.app.user
             * @param {Object} params
             * <b><i>Required.</i></b>
             * @param {string} params.user
             * @param {string} params.uid
             * @param {function=} callback
             * @returns {* | Object}
             * If callback is function, returns the result of callback
             * with Object using {name, uid} property as a parameter.
             * Else, returns Object having property {name, uid}.
             * @example
             * // Without callback function
             * bim.app.user.register({name : ${1:userName}, uid : ${2:userId}});
             * @example
             * // With callback function
             * bim.app.user.register({name : ${1:userName}, uid : ${2:userId}}, ${3:callback});
             */
            register: function (params, callback) {
              if (!params) {
                if (callback == "function") callback(false);
                return false;
              }

              user.setItem("user", params.user);
              user.setItem("uid", params.uid);

              if (typeof callback === "function") {
                return callback({
                  name: __lib.app.user.name(),
                  uid: __lib.app.user.uid(),
                });
              }

              return { name: __lib.app.user.name(), uid: __lib.app.user.uid() };
            },

            /**
             * @description
             * Function to log out user.
             * Removes data "user", "uid" from <code>localStorage</code>.
             * @memberof bim.app.user
             * @function logout
             * @returns {}
             * @example
             * bim.app.user.logout();
             */
            logout: function () {
              user.removeItem("user");
              user.removeItem("uid");
              user.clear();
            },
          };
        })(),

        /**
         * @description
         * Function to get or set user session.
         *
         * If parameter is empty, returns current user data.
         *
         * If not empty, sets the user data.
         * @since 22.03.17
         * @memberof bim.app
         * @function session
         * @param {Object=} params
         * <b><i>Optional</i></b>
         * @param {string} params.user
         * User Name
         * @param {string} params.uid
         * User ID
         * @param {function=} callback
         * Callback Function
         * @returns {Object | * | void}
         * When gets empty parameter, returns Object
         * {uid: {string}, name: {string}, guest: {boolean}}
         *
         * When <code>callback</code> is function,
         * returns the result of callback.
         *
         * Else, returns void.
         * @example
         * // Get Session (uid & name & guest)
         * bim.app.session();
         * @example
         * // Get Session (uid & name & guest) & Calls Callback
         * bim.app.session(${1:callback});
         * @example
         * // Set Session & Calls Callback
         * bim.app.session({"user" : ${1:userName}, "uid" : ${2:userID}}, ${3:callback});
         */
        session: function (params, callback) {
          // CREATE A GUEST USER
          if (!params || (typeof params === "function" && !callback)) {
            callback = params;

            return __lib.app.user.loggedIn(function (IsLogin) {
              if (IsLogin) {
                return {
                  uid: __lib.app.user.uid(),
                  name: __lib.app.user.name(),
                  guest: false,
                };
              } else {
                return {
                  uid: __lib.app.user.uid(),
                  name: __lib.app.user.name(),
                  guest: true,
                };
              }
            });
          } else if (
            params.user &&
            params.uid &&
            params.user !== "" &&
            params.uid !== ""
          ) {
            return __lib.app.user.register(params, function (res) {
              if (typeof callback === "function") {
                return callback(res);
              } else {
                return res;
              }
            });
          }

          return;
        },

        /**
         * @description
         * Function to get the App's ID from Native side.
         * Currently, only works for Android OS now.
         * @since 22.03.17
         * @memberof bim.app
         * @function getAppId
         * @example
         * bim.app.getAppId();
         */
        getAppId: function () {
          console.log("HTB_NATIVE_LOG", "bim.app.getAppId() ");
        },

        /**
         * @description
         * Function to set App's ID to Native side.
         * Currently, only works for Android OS now.
         * @since 22.03.17
         * @memberof bim.app
         * @function setAppId
         * @param {Object} params
         * <b><i>Required.</i></b>
         * Parameter would be sent to native after stringfied.
         * @returns {Object | void}
         * Error Object if params empty.
         * @example
         * bim.app.setAppId({$1:Object});
         */
        setAppId: function (params) {
          if (!params || params == "")
            return {
              error: true,
              errno: "eO072bx3",
              description:
                "Parameters cannot be empty, read the documentation to know more about",
              message: "Oops! Something went wrong implementing native plugin",
            };

          console.log("HTB_NATIVE_LOG", "bim.app.setAppId : ", params);
        },

        /**
         * @description
         * Function to create new page in plugin.
         * @since 22.03.17
         * @memberof bim.app
         * @function page
         * @param {Object} params
         * <b><i>Required.</i></b>
         * Contain page objects for Hitbim
         * @param {Object} params.page
         * <b><i>Required.</i></b>
         * @param {string} params.page.name
         * Name of the new screen.
         * @param {Object} params.page.context
         * Context to fill template dynamically.
         * @param {Object} params.page.context.lang
         * Path of the language.
         * @param {Object} params.page.context.detect
         * Detect in case of load automatically a language
         * by detecting device language.
         * @param {Object} params.page.content
         * Template of the new screen.
         * Placeholder should match the json keyNames from
         * context lang key.
         * @param {boolean} params.page.animate
         * Load page with animation.
         * @param {function | Object} callback
         * Function to generate the new page,
         * can be synchronously or asynchronously.
         *
         * callback can have two types
         * 1. callback can be called when after creating page
         *
         * 2. callback can be Object contain two functions
         *
         * <code>onOpen: function(){}, // This runs like type 1 (called when after creating page)</code>
         *
         * <code>onClose: function(){} // Called when bim.app.close.page();</code>
         * @param {function} callback.onOpen
         * Called after creating new page
         * @param {function} callback.onClose
         * Called after deleting this page by
         * <code>{@link bim.app.close bim.app.close.page();}</code>
         * @example
         * var page = {
         *  page: {
         *    name: {$1:name},
         *    context: {
         *      lang: {$2:lang},
         *      detect: {$3:detect}
         *    },
         *    content: {$4:content},
         *    animate: {$5:animate}
         *  }
         * };
         * bim.app.page(page, {$6:callback});
         * @example
         * var page = {
         *  page: {
         *    name: 'login',
         *    context: {
         *      lang: 'lang/en',
         *      detect: false
         *    },
         *    content: 'templates/login.html',
         *    animate: true
         *  }
         * };
         *
         * bim.app.page(page, function() {
         *  console.log('trigger after opened', this);
         *  // Do Something ...
         * });
         */
        page: function (params, callback) {
          if (params && params !== "") {
            __lib.app
              .template({
                id: __lib.plugin.id.get(),
                html: params.page.content,
                context: {
                  lang: params.page.context.lang,
                  detect: params.page.context.detect,
                },
                name: params.page.name,
              })
              .then(function (res) {
                if (!res.error) {
                  // temporary DOM
                  var bimElement = document.createElement("div");
                  bimElement.innerHTML = res.template;
                  var elementHTML = __lib.app.element(bimElement);

                  elementHTML.addClass("bim-singleView bim-singleView-init");

                  __lib.app.element("body").append(elementHTML);
                  var el = __lib.app.element("body").find(".bim-singleView");
                  for (var i = 0; i < el.length - 1; i++) {
                    el.eq(i).attr("visible", "false");
                  }
                  const lastDiv = document.querySelector(
                    ".bim-singleView:last-child"
                  );
                  lastDiv.classList.remove("bim-page-from-left");
                  lastDiv.classList.add("bim-singleView-center");
                  lastDiv.setAttribute("singleView-name", params.page.name);
                  lastDiv.setAttribute("index", el.length - 1);

                  let OpenCallback =
                    typeof callback == "function"
                      ? callback
                      : typeof callback == "object"
                      ? callback.onOpen
                      : undefined;
                  let CloseCallback =
                    typeof callback == "object" ? callback.onClose : undefined;

                  // Set pageNavigator When create new page inside plugin
                  pageNavigator.push({
                    index: el.length - 1,
                    name: params.page.name,
                    OpenCallback: OpenCallback,
                    CloseCallback: CloseCallback,
                  });

                  for (var i = 0; i < el.length; i++) {
                    el.eq(i).attr("index", i);
                  }

                  setTimeout(function () {
                    el.addClass("bim-page-from-left");
                    __lib.app.listeners.transition(
                      {
                        event: "create",
                        name: params.page.name,
                        index: el.length - 1,
                      },
                      function (e) {
                        return OpenCallback(e);
                      }
                    );
                  }, 0);
                } else {
                  if (typeof OpenCallback === "function")
                    return OpenCallback(res);
                  return res;
                }
              });
          } else
            console.error("bim.app.page() need parameter. Check document!");
        },

        /**
         * @description
         * Function to add Event Listener to document.
         * @memberof bim.app
         * @function listener
         * @param {string} eventType
         * @param {string} elementQuerySelector
         * Used as a selector
         * @param {function} cb
         * Callback function for the event
         * @example
         * bim.app.listener(${1:eventType}, ${2:selector}, ${3:callback});
         */
        listener: function (eventType, elementQuerySelector, cb) {
          document.addEventListener(eventType, function (event) {
            var qs = document.querySelectorAll(elementQuerySelector);

            if (qs) {
              var el = event.target,
                index = -1;
              while (
                el &&
                (index = Array.prototype.indexOf.call(qs, el)) === -1
              ) {
                el = el.parentElement;
              }

              if (index > -1) {
                if (/^[^{]+?=>/.test(cb.toString())) {
                  event.element = el;
                  return cb(event);
                } else {
                  cb.call(el, event);
                }
              }
            }
          });
        },

        /**
         * @description
         * Object which has a Function to make the last step to firing an event.
         * @memberof bim.app
         * @name listeners
         * @type {Object}
         * @property {function} transition
         * Function to make the last step to firing an event.
         * @example
         * bim.app.listeners.transition({
         *  event: ${1:event},
         *  name: ${2:name},
         *  index: ${3:index},
         *  on: ${4:on}
         * }, ${5:callback});
         * @example
         * bim.app.listeners.transition({
         *  event: "create",
         *  name: _name,
         *  index: _index,
         * }, function (e) {
         *  // Do Something ...
         * });
         */
        listeners: {
          /**
           * @memberof bim.app.listeners
           * @function transition
           * @param {Object} e
           * <b><i>Required.</i></b>
           * @param {*} e.on
           * @param {*} e.event
           * @param {*} e.name
           * @param {*} e.index
           * @param {function} callback
           * Callback Function
           * @returns {*}
           * The result of callback
           */
          transition: function (e, callback) {
            if (!e || e == "") return;
            //var element = (!e.listen || e.listen == '') ? 'document' : e.listen;
            //if (!e.on || e.on == '') return;

            var delay = true;
            var evCallback;

            switch (e.on) {
              case "before":
                evCallback = "transitionrun";
                break;
              case "onstart":
              case "start":
                evCallback = "transitionstart";
                break;
              case "cancel":
                evCallback = "transitioncancel";
                break;
              case "after":
              case "end":
              case "finish":
                delay = true;
                evCallback = "transitionend";
                break;
            }

            var option = {
              detail: {
                listener: "navigation",
                event: e.event,
                page: e.name,
                index: e.index,
              },
            };

            if (delay) {
              setTimeout(function () {
                document.dispatchEvent(new CustomEvent("navigation", option));
                return callback(option);
              }, 300);
            } else {
              document.dispatchEvent(new CustomEvent("navigation", option));
              return callback(option);
            }
          },
        },

        /**
         * @description
         * Function to add Event Listener "navigation" to document.
         * @memberof bim.app
         * @function navigation
         * @param {function} callback
         * @example
         * bim.app.navigation(${1:callback});
         */
        navigation: function (callback) {
          document.addEventListener("navigation", function (e) {
            if (typeof callback === "function") return callback(e.detail);
          });
        },

        /**
         * @description
         * Object, which has a Function to close opened page in plugin.
         * @since 22.03.17
         * @memberof bim.app
         * @name close
         * @type {Object}
         * @property {function} page
         * Function to close opened page in plugin.
         * @example
         * bim.app.close.page();
         * @example
         * bim.app.close.page({animatePages:true});
         */
        close: {
          /**
           * @description
           * Function to close opened page in plugin.
           * @memberof bim.app.close
           * @function page
           * @param {*} params
           * Close option
           * @param {function} callback
           * IF callback exists, run callback after close page
           * IF not exists callback AND when set onClose function in
           * <code>{@link bim.app.page}</code>,
           * run onClose function automatically
           * @returns {*}
           * Result of callback function.
           * @example
           * bim.app.close.page(${1:params}, ${2:callback});
           */
          page: function (params, callback) {
            if (pageNavigator.length == 0) return;

            let page_elements = bim.app.element(
              ".bim-singleView-center.bim-page-from-left"
            );

            if (page_elements.length == 0) return;

            let last_element = page_elements[page_elements.length - 1];
            last_element.remove();

            let CloseCallback =
              callback && typeof callback == "function"
                ? callback
                : pageNavigator[pageNavigator.length - 1].CloseCallback;
            pageNavigator.pop();

            if (CloseCallback) return CloseCallback(false);
          },
        },

        /**
         * @description
         * Function to return {@link DOM} Element.
         * @memberof bim.app
         * @function element
         * @param {DOM | string} selector
         * Selector to match the Element.
         * Can be {@link DOM}, Document, Window Object or
         * string.
         * @returns {DOM}
         * Returns {@link DOM} Element matched by selector.
         * @example
         * bim.app.element(${1:string});
         */
        element: function (selector) {
          return tokenizer(selector);
        },

        /**
         * @description
         * TEMPLATER LOGICLESS
         * Tokenizing fraction in template.
         * Pass the string and create structure to parse template object.
         * @namespace bim.app.logicless
         * @property {Object} htmlTemplate See {@link bim.app.logicless.htmlTemplate} for more details
         * @property {Object} lang See {@link bim.app.logicless.lang} for more details
         * @property {function} tokenizer See {@link bim.app.logicless.tokenizer} for more details
         * @property {function} compiler See {@link bim.app.logicless.compiler} for more details
         */
        logicless: {
          /**
           * @description
           * HTML Template
           * @memberof bim.app.logicless
           * @name htmlTemplate
           * @type {Object}
           * @property {function} get See {@link bim.app.logicless.htmlTemplate} for more details
           * @property {function} tokenizer See {@link bim.app.logicless.htmlTemplate.tokenizer} for more details
           * @property {function} compiler See {@link bim.app.logicless.htmlTemplate.compiler} for more details
           */
          htmlTemplate: {
            /**
             * @memberof bim.app.logicless.htmlTemplate
             * @function get
             * @param {*} params
             * @returns {}
             */
            get: function (params) {
              return new Promise(function (resolve, reject) {
                if (!params || !params.path || params.path == "")
                  return resolve({
                    error: true,
                    message: "Error loading template",
                    errno: "98b674bb55",
                  });
                // Load lang file
                var ext = params.path.split(".");
                var file;

                if (ext.length > 1) {
                  ext = params.path.split(/[#?]/)[0].split(".").pop().trim();

                  if (ext == "html") {
                    file = params.path.split(".")[0] + ".html";
                  } else {
                    return;
                  }

                  ext = ".html";
                } else {
                  file = ext[0] + ".html";
                }

                if (!file)
                  return resolve({
                    error: true,
                    message: "Error loading template",
                    errno: "93b3464vf5",
                  });

                __lib.readFile(
                  { file: file, onErrorMessage: "Error loading template" },
                  function (res) {
                    if (res.error) {
                      return resolve(res);
                    } else {
                      return resolve({ error: false, data: res.data });
                    }
                  }
                );
              });
            },

            /**
             * @memberof bim.app.logicless.htmlTemplate
             * @function tokenizer
             * @param {*} string
             * @returns {}
             */
            tokenizer: function (string) {
              return new Promise(function (rv) {
                if (!string || string == "")
                  return rv({
                    error: true,
                    message: "Params can be empty iterating template",
                    errno: "js7263in",
                  });

                var current = 0;
                var chunk = "";
                var crawlHtml = "";
                var methods = 0;
                var allowCralwing = true;
                var fk = "";

                while (current <= string.length) {
                  var char = string[current];

                  if (/\s/.test(chunk)) {
                    if (chunk.indexOf("{{#") > -1) {
                      if (methods > 0) {
                        fk +=
                          ',\n{\n "method": "' +
                          chunk.replace("{{#", "").trim() +
                          '",\n';
                      } else {
                        fk +=
                          '{\n "method": "' +
                          chunk.replace("{{#", "").trim() +
                          '",\n';
                      }
                      allowCralwing = true;
                      methods++;
                    } else if (
                      chunk.indexOf("}}") > -1 &&
                      chunk.indexOf(">") < 0 &&
                      chunk.indexOf("<") < 0 &&
                      chunk.indexOf("{{/") < 0
                    ) {
                      fk +=
                        '"key": "' + chunk.replace("}}", "").trim() + '",\n ';
                    }

                    // TEMPLATE
                    else {
                      if (allowCralwing) {
                        crawlHtml += chunk;
                      }
                    }

                    chunk = "";
                  } else {
                    // END LOGILESS TEMPLATE LOOP METHOD
                    if (chunk.indexOf("{{/") > -1 && chunk.indexOf("}}") > -1) {
                      fk +=
                        '"template": ' +
                        JSON.stringify(crawlHtml.trim()) +
                        "\n}";
                      crawlHtml = "";
                      allowCralwing = false;
                    }
                  }

                  chunk += char;
                  current++;
                }

                try {
                  return rv({ error: false, data: JSON.parse("[" + fk + "]") });
                } catch (e) {
                  return rv({
                    error: true,
                    description: e,
                    errno: "87h4s56a62",
                  });
                }
              });
            },

            /**
             * @memberof bim.app.logicless.htmlTemplate
             * @function compiler
             * @param {*} params
             * @returns {}
             */
            compiler: function (params) {
              return new Promise(function (rs) {
                // LENGTH ITERATIONS
                var fullbuilder = "";
                var mainBuilder = [];
                var item = {};

                String.prototype.buildTemplate = function (result) {
                  return this.replace(/{{(.+?)}}/g, function (_, g1) {
                    return result[g1] || g1;
                  });
                };

                for (var a = 0; a < params.tokens.data.length; a++) {
                  var template = params.tokens.data[a].template;
                  var key = params.tokens.data[a].key;
                  var builder = "";

                  if (
                    params.parse &&
                    params.parse.data &&
                    params.parse.data[key]
                  ) {
                    for (var b = 0; b < params.parse.data[key].length; b++) {
                      builder += template.buildTemplate(
                        params.parse.data[key][b]
                      );
                    }
                  }
                }

                return rs(builder);
              });
            },
          },

          /**
           * @description
           * Language
           * @memberof bim.app.logicless
           * @name lang
           * @type {Object}
           * @property {function} get See {@link bim.app.logicless.lang.get} for more details
           */
          lang: {
            /**
             * @description
             * Function to read language file.
             * Returns void when extension is not in "json"
             * @memberof bim.app.logicless.lang
             * @function get
             * @param {Object} params
             * <b><i>Required.</i></b>
             * parameter.
             * @param {string} params.lang
             * <b><i>Required.</i></b>
             * Language file name with extension (.json)
             * @returns {void | Object}
             * If has extension and it's not ".json", void |
             *
             * Error data or read file data
             */
            get: function (params) {
              return new Promise(function (resolve, reject) {
                if (!params || !params.lang || params.lang == "")
                  return resolve({
                    error: true,
                    message: "Error loading languaje file",
                    errno: "eO9b62v35",
                  });
                // Load lang file
                var ext = params.lang.split(".");
                var file;

                if (ext.length > 1) {
                  ext = params.lang.split(/[#?]/)[0].split(".").pop().trim();

                  if (ext == "json") {
                    file = params.lang.split(".")[0] + ".json";
                  } else {
                    return;
                  }

                  ext = ".json";
                } else {
                  file = ext[0] + ".json";
                }

                if (!file)
                  return resolve({
                    error: true,
                    message: "Error loading language file",
                    errno: "eO9b364v",
                  });

                __lib.readFile(
                  { file: file, onErrorMessage: "Error reading language file" },
                  function (res) {
                    if (!res.error) {
                      try {
                        var parsedLanguage = JSON.parse(res.data);

                        return resolve({ error: false, data: parsedLanguage });
                      } catch (err) {
                        return resolve({
                          error: true,
                          message: "Oops! Error loading language file!",
                          description: err,
                          errno: "8745b4745bgv",
                        });
                      }
                    } else {
                      return resolve({
                        error: true,
                        message: "Oops! Error loading language file!",
                        description: err,
                        errno: "3b37843b6",
                      });
                    }
                  }
                );
              });
            },
          },

          /**
           * @description
           * Tokenizer
           * @memberof bim.app.logicless
           * @function tokenizer
           * @param {*} string
           * @returns {Object} Object with error report or parsed JSON
           */
          tokenizer: function (string) {
            var blocks = [];
            var i;
            var j;
            if (!string || typeof string !== "string" || string == "") {
              return [];
            }
            var stringBlocks = string.split(/({{[^{^}]*}})/);
            for (i = 0; i < stringBlocks.length; i += 1) {
              var block = stringBlocks[i];
              if (block === "") {
                continue;
              }
              if (block.indexOf("{{") < 0) {
                blocks.push({
                  type: "plain",
                  content: block,
                });
              } else {
                if (block.indexOf("{/") >= 0) {
                  continue;
                }
                if (
                  block.indexOf("{#") < 0 &&
                  block.indexOf(" ") < 0 &&
                  block.indexOf("else") < 0
                ) {
                  // Simple variable
                  blocks.push({
                    type: "variable",
                    contextName: block.replace(/[{}]/g, ""),
                  });
                  continue;
                }
                // Helpers
                var helperSlices = helperToSlices(block);
                var helperName = helperSlices[0];
                var isPartial = helperName === ">";
                var helperContext = [];
                var helperHash = {};
                for (j = 1; j < helperSlices.length; j += 1) {
                  var slice = helperSlices[j];
                  if (isArray(slice)) {
                    // Hash
                    helperHash[slice[0]] =
                      slice[1] === "false" ? false : slice[1];
                  } else {
                    helperContext.push(slice);
                  }
                }

                if (block.indexOf("{#") >= 0) {
                  // Condition/Helper
                  var helperContent = "";
                  var elseContent = "";
                  var toSkip = 0;
                  var shiftIndex = void 0;
                  var foundClosed = false;
                  var foundElse = false;
                  var depth = 0;
                  for (j = i + 1; j < stringBlocks.length; j += 1) {
                    if (stringBlocks[j].indexOf("{{#") >= 0) {
                      depth += 1;
                    }
                    if (stringBlocks[j].indexOf("{{/") >= 0) {
                      depth -= 1;
                    }
                    if (stringBlocks[j].indexOf("{{#" + helperName) >= 0) {
                      helperContent += stringBlocks[j];
                      if (foundElse) {
                        elseContent += stringBlocks[j];
                      }
                      toSkip += 1;
                    } else if (
                      stringBlocks[j].indexOf("{{/" + helperName) >= 0
                    ) {
                      if (toSkip > 0) {
                        toSkip -= 1;
                        helperContent += stringBlocks[j];
                        if (foundElse) {
                          elseContent += stringBlocks[j];
                        }
                      } else {
                        shiftIndex = j;
                        foundClosed = true;
                        break;
                      }
                    } else if (
                      stringBlocks[j].indexOf("else") >= 0 &&
                      depth === 0
                    ) {
                      foundElse = true;
                    } else {
                      if (!foundElse) {
                        helperContent += stringBlocks[j];
                      }
                      if (foundElse) {
                        elseContent += stringBlocks[j];
                      }
                    }
                  }
                  if (foundClosed) {
                    if (shiftIndex) {
                      i = shiftIndex;
                    }
                    blocks.push({
                      type: "helper",
                      helperName: helperName,
                      contextName: helperContext,
                      content: helperContent,
                      inverseContent: elseContent,
                      hash: helperHash,
                    });
                  }
                } else if (block.indexOf(" ") > 0) {
                  if (isPartial) {
                    helperName = "_partial";
                    if (helperContext[0]) {
                      helperContext[0] =
                        '"' + helperContext[0].replace(/"|'/g, "") + '"';
                    }
                  }
                  blocks.push({
                    type: "helper",
                    helperName: helperName,
                    contextName: helperContext,
                    hash: helperHash,
                  });
                }
              }
            }
            return blocks;
          },

          /**
           * @description
           * Compiler
           * @memberof bim.app.logicless
           * @function compiler
           * @param {*} params
           * @param {*} params.html
           * @param {*} params.parse
           * @param {*} callback
           * @example
           * bim.app.logicless.compiler(${1:params}, ${2:callback});
           */
          compiler: function (params, callback) {
            // CLEAN HTML

            var bimElement = document.createElement("div");
            bimElement.innerHTML = params.html;
            var elementHTML = __lib.app.element(bimElement);
            var selector = "bim";

            elementHTML.find(selector).filter(function (index, el) {
              var stringHTML = __lib.app.element(el)[0].innerHTML;
              var ast = __lib.app.logicless.tokenizer(stringHTML);
              var builder = "";

              for (var i = 0; i < ast.length; i++) {
                if (ast[i].type === "plain") {
                  builder += ast[i].content;
                }
                if (ast[i].type === "variable") {
                  builder += params.parse[ast[i].contextName];
                }
              }

              if (typeof callback === "function") return callback(builder);
            });
          },
        },

        /**
         * @description
         * Function to return templated "bim" element
         * with context language.
         * @memberof bim.app
         * @function template
         * @param {Object} param
         * <b><i>Required.</i></b>
         * @param {string} param.id
         * Plugin ID
         * @param {string} param.html
         * <b><i>Required.</i></b>
         * @param {Object} param.context
         * @param {string} param.context.lang
         * The path of language file in json type.
         * @param {boolean} param.context.detect
         * @param {boolean} param.component
         * @param {function} callback
         * Callback function.
         * @returns {{ error:boolean, template:string, message:string, errno:string }}
         * {
         *  error {boolean},
         *  template {string},
         *  message: {string},
         *  errno: {string}
         * }
         * @example
         * bim.app.template({
         *  id: bim.plugin.id.get(),
         *  html: "templates/your_template.html",
         *  context: {
         *    lang: "lang/en",
         *    detect: false
         *  },
         *  name: bim.plugin.name.get()
         * }).then(function (compiled) {
         *  // Do Something ...
         * });
         */
        template: function (param, callback) {
          return new Promise(function (rs, rj) {
            if (!param)
              return rs({ error: true, message: "Params cannot be empty" });
            if (!param.html || param.html == "")
              return rs({
                error: true,
                message:
                  "Template path is missing or <bim> tag missing in template file",
                errno: "83gv364",
              });
            if (!__lib.plugin.id.get() || __lib.plugin.id.get() == "")
              return rs({
                error: true,
                message: "Initialize app is required, plugin id is missing",
                errno: "ue73g463",
              });
            if (!__lib.plugin.name.get() || __lib.plugin.name.get() == "")
              return rs({
                error: true,
                message: "Initialize app is required, plugin name is missing",
                errno: "93b343b",
              });

            if (typeof __lib.plugin.id.get() !== "string")
              return rs({
                error: true,
                message: "Initialize app is required, plugin id is missing",
                errno: "93b33nrv4",
              });

            var pluginId = __lib.plugin.id.get();

            async function _run(s) {
              var contextLang =
                Object.prototype.toString.call(s.context) ==
                  "[object Object]" &&
                s.context.lang &&
                s.context.lang !== ""
                  ? s.context.lang
                  : s.lang;
              var contextDetect =
                Object.prototype.toString.call(s.context) ==
                  "[object Object]" &&
                s.context.detect &&
                s.context.detect !== ""
                  ? s.context.detect
                  : s.detect;

              var langObject = await __lib.app.logicless.lang.get({
                lang: contextLang,
                detect: contextDetect,
              });
              var templateObject = await __lib.app.logicless.htmlTemplate.get({
                path: s.html,
              });

              if (langObject.error) {
                return rs(langObject);
              } else if (templateObject.error) {
                return rs(templateObject);
              } else if (langObject.data == "" || templateObject.data == "") {
                return rs({
                  error: true,
                  message: "Initialize app is required, plugin id is missing",
                  errno: "93n273aa33rv4",
                });
              } else {
                // BUILD THE TEMPLATE
                __lib.app.logicless.compiler(
                  {
                    html: templateObject.data,
                    parse: langObject.data,
                    lang: contextLang,
                  },
                  function (compiled) {
                    let compiledWithParams =
                      '<bim bim-id="' +
                      pluginId +
                      '" data-lang="' +
                      contextLang +
                      '">' +
                      compiled +
                      "</bim>";

                    return rs({ error: false, template: compiledWithParams });
                  }
                );
              }
            }

            _run(param);
          });
        },

        /**
         * @description
         * Collection of functions to handle the storyboard of the App.
         * @namespace bim.app.storyboard
         * @type {Object}
         * @property {function} containsStack
         * Function to check whether contains stack or not.
         * See {@link bim.app.storyboard.containsStack} for more details.
         *
         * @property {function} getStack
         * Function to get stack from local storage.
         * See {@link bim.app.storyboard.getStack} for more details.
         *
         * @property {function} setStack
         * Function to set stack to local storage.
         * See {@link bim.app.storyboard.setStack} for more details.
         *
         * @property {function} setCurrent
         * Function to set current plugin to localstorage.
         * See {@link bim.app.storyboard.setCurrent} for more details.
         *
         * @property {function} current
         * Function to get current plugin from localstorage.
         * See {@link bim.app.storyboard.current} for more details.
         *
         * @property {function} next
         * Function to move to next plugin.
         * See {@link bim.app.storyboard.next} for more details.
         *
         * @property {function} setPrevious
         * Function to set previous plugin.
         * See {@link bim.app.storyboard.setPrevious} for more details.
         *
         * @property {function} previous
         * Function to get previous plugin.
         * See {@link bim.app.storyboard.previous} for more details.
         *
         * @property {function} moveTo
         * Function to move with data to other plugin.
         * See {@link bim.app.storyboard.moveTo} for more details.
         *
         * @property {function} setGoBack
         * Function to set "goBack" function in plugin.
         * See {@link bim.app.storyboard.setGoBack} for more details.
         *
         * @property {function} resetGoBack
         * Function to reset "goBack" function in plugin.
         * See {@link bim.app.storyboard.resetGoBack} for more details.
         *
         * @property {function} goBack
         * Function called directly or indirectly when user goes back.
         * See {@link bim.app.storyboard.goBack} for more details.
         */
        storyboard: {
          /**
           * @description
           * Function which checks whether the storyboard
           * contains stack in stack objects or not.
           * Checked by second parameter's paths.
           * @memberof bim.app.storyboard
           * @function containsStack
           * @param {string} stackObject
           * @param {Array} stackObjects
           * @returns {boolean} true | false
           * @example
           * bim.app.storyboard.containStack(${1:stackObject}, ${2:stackObjects});
           */
          containsStack: function (stackObject, stackObjects) {
            var i;

            for (i = 0; i < stackObjects.length; i++) {
              if (stackObjects[i].pathname === stackObject) {
                return true;
              }
            }

            return false;
          },

          /**
           * @description
           * Function to get stack from local storage.
           * @memberof bim.app.storyboard
           * @function getStack
           * @returns {string}
           * Returns the item in local storage which has key "navigation"
           * @example
           * bim.app.storyboard.getStack();
           */
          getStack: function () {
            return window.localStorage.getItem("navigation");
          },

          /**
           * @description
           * Function to set stack to local storage.
           * Checks whether there is already existing stack or not before setting it.
           * @memberof bim.app.storyboard
           * @function setStack
           * @param {Object} params
           * <b><i>Required.</i></b>
           * @param {string} params.pluginId
           * Plugin ID.
           * @returns {}
           * @example
           * bim.app.storyboard.setStack();
           */
          setStack: function (params) {
            if (!params || params == "") return;

            // GET STACK IF EXISTS OR NOT
            var stack = __lib.app.storyboard.getStack();

            if (!stack || stack == "") {
              __lib.app.data.set({
                navigation: JSON.stringify([
                  {
                    plugin: params.pluginId,
                    href: window.location.href,
                    pathname: window.location.pathname,
                    protocol: window.location.protocol,
                    port: window.location.port,
                    origin: window.location.origin,
                    hostname: window.location.hostname,
                  },
                ]),
              });
            } else {
              //
              try {
                var readStack = JSON.parse(stack);

                if (
                  !__lib.app.storyboard.containsStack(
                    window.location.pathname,
                    readStack
                  )
                ) {
                  readStack.push({
                    plugin: __lib.app.storyboard.current(),
                    href: window.location.href,
                    pathname: window.location.pathname,
                    protocol: window.location.protocol,
                    port: window.location.port,
                    origin: window.location.origin,
                    hostname: window.location.hostname,
                  });

                  __lib.app.data.set({ navigation: JSON.stringify(readStack) });
                }
              } catch (e) {
                console.warn("Warning: ", e);
              }
            }
          },

          /**
           * @description
           * Function to set current plugin to localstorage.
           * @memberof bim.app.storyboard
           * @function setCurrent
           * @param {string} params
           * <b><i>Required.</i></b>
           * Plugin ID.
           * @returns {}
           * @example
           * bim.app.storyboard.setCurrent(${1:pluginID});
           */
          setCurrent: function (params) {
            if (!params || params == "") return;
            window.localStorage.setItem("current", params);
            return;
          },

          /**
           * @description
           * Function to get current plugin from localstorage.
           * @memberof bim.app.storyboard
           * @function current
           * @returns {string}
           * Plugin ID.
           * Get item using key "current" from local storage.
           */
          current: function () {
            return window.localStorage.getItem("current");
          },

          /**
           * @description
           * Function to move to next plugin.
           * Get plugins data, save current plugin as previous,
           * and move to next plugin.
           * @memberof bim.app.storyboard
           * @function next
           * @param {function} callback
           * @returns {}
           * @example
           * bim.app.storyboard.next(${1: callback});
           */
          next: function (callback) {
            // TODO:
            // GET PLUGINS STORYBOARD
            bim.app.data.bundle(
              {
                db: {
                  name: "storyboard",
                  table: "plugins",
                  key: "PLUGINS",
                },
              },
              function (res) {
                if (res.error) {
                  if (typeof callback === "function") return callback(res);
                } else {
                  // SEARCH FOR CURRENT PLUGIN LOADED
                  // GET CURRENT SCREEN-PLUGIN ON STORYBOARD
                  if (
                    __lib.app.storyboard.current() &&
                    __lib.app.storyboard.current() !== ""
                  ) {
                    var next;

                    var plugin_names = Object.keys(res.data);

                    for (var i = 0; i < plugin_names.length; i++) {
                      if (next) {
                        if (
                          res.data[plugin_names[i]].id &&
                          res.data[plugin_names[i]].id !== ""
                        ) {
                          __lib.app.storyboard.setCurrent(
                            res.data[plugin_names[i]].id
                          );

                          __lib.app.storyboard.moveTo({
                            page: res.data[plugin_names[i]].page,
                            plugin: plugin_names[i],
                          });
                        }

                        break;
                      } else if (
                        res.data[plugin_names[i]].id ==
                        __lib.app.storyboard.current()
                      ) {
                        // SET AS PREVIOUS
                        __lib.app.storyboard.setPrevious(
                          res.data[plugin_names[i]].id
                        );
                        next = true;
                      }
                    }
                  } else {
                  }
                }
              }
            );
          },

          /**
           * @description
           * Function to set previous plugin.
           * Set plugin item in local storage using key "previous".
           * @memberof bim.app.storyboard
           * @function setPrevious
           * @param {string} params
           * <b><i>Required.</i></b>
           */
          setPrevious: function (params) {
            if (!params || params == "") return;

            window.localStorage.setItem("previous", params);
            return;
          },

          /**
           * @description
           * Function to get previous plugin.
           * Get previous plugin item from local storage using key "previous".
           * @memberof bim.app.storyboard
           * @function previous
           * @returns {string}
           * Previous plugin item.
           */
          previous: function () {
            return window.localStorage.getItem("previous");
          },

          /**
           * @description
           * Function to move with data to other plugin.
           * Move with data to specific plugin's specific page.
           * @memberof bim.app.storyboard
           * @param {Object} params Required.
           * @param {string} params.page
           * <b><i>Required.</i></b>
           * Specific page of the plugin wanted to move to.
           * @param {Object} params.data Data wanted to be sent.
           * @param {boolean} params.reload
           * Whether reload or not.
           * Sent to the plugin as <code>reloadBimPlugin=true</code>
           * @param {string} params.scrollTop Scroll Top
           * @param {string} params.animation Animation
           * @returns {Object | boolean} Error detail object | <code>false</code> when success
           */
          moveTo: function (params) {
            if (!params || params == "")
              return {
                error: true,
                message:
                  "There is no plugin screen to load, verify you added the correct parameters",
              };
            if (
              !params.page ||
              params.page == "" ||
              typeof params.page !== "string"
            )
              return {
                error: true,
                message: "Index page name of plugin is missing {{page}}",
              };

            var re = /(?:\.([^.]+))?$/;
            var ext = re.exec(params.page)[1];

            // Prevent params dosent contain specific key
            if (params.reload) {
              if (!params.data || typeof params.data !== "object") {
                params.data = {};
              }
              params.data.reloadBimPlugin = true;
            }
            if (params.scrollTop) {
              if (!params.data || typeof params.data !== "object") {
                params.data = {};
              }
              params.data.scrollTop = '"' + params.scrollTop + '"';
            }
            if (params.animation) {
              if (!params.data || typeof params.data !== "object") {
                params.data = {};
              }
              params.data.animation = params.animation;
            }

            if (!ext || ext.trim() !== "html") params.page += ".html";

            var bURl = "";

            if (
              params.data &&
              typeof params.data == "object" &&
              params.data.key &&
              params.data.value
            ) {
              bURl = "?" + params.data.key + "=" + params.data.value;
            } else if (
              params.data &&
              Object.prototype.toString.call(params.data) === "[object Object]"
            ) {
              var keys = Object.keys(params.data);
              var urlParts = "";
              for (var i = 0; i < keys.length; i++) {
                if (
                  keys[i] &&
                  keys[i] !== "" &&
                  params.data[keys[i]] &&
                  params.data[keys[i]] !== ""
                ) {
                  var suffix = "&";
                  if (i == keys.length - 1) {
                    suffix = "";
                  }
                  urlParts += keys[i] + "=" + params.data[keys[i]] + suffix;
                }
              }
              bURl = "?" + urlParts;
            }

            if (params.index) {
              window.location =
                __PLUGINS_DIR__ +
                "/" +
                params.plugin +
                "/" +
                params.page +
                bURl;
              return false;
            }

            var url = window.location.toString();
            url = url.split(window.location.pathname)[0];
            var plugins_path = __PLUGINS_DIR__;
            plugins_path = plugins_path.replace(".", "");

            window.location = `${url}${plugins_path}/${params.plugin}/${params.page}${bURl}`;

            return false;
          },

          /**
           * @description
           * Function to set "goBack" function in plugin.
           *
           * Set
           * <code>bim.app.storyboard.goBack()</code>
           * in plugin.
           *
           * Rewrites the default {@link bim.app.storyboard.goBack} made.
           * @memberof bim.app.storyboard
           * @function setGoBack
           * @param {function} input
           * @example
           * bim.app.storyboard.setGoBack(function () {
           *  // Would be called when native back button pressed
           * });
           */
          setGoBack: function (input) {
            if (input && typeof input == "function") {
              __lib.app.storyboard.goBack = input;
            }
          },

          /**
           * @description
           * Function to reset "goBack" function in plugin.
           *
           * Reset bim.app.storyboard.goBack() to default status
           * It must be same with bim.app.storyboard.goBack()
           * @memberof bim.app.storyboard
           * @function resetGoBack
           * @example
           * bim.app.storyboard.resetGoBack();
           */
          resetGoBack: function () {
            __lib.app.storyboard.goBack = function () {
              if (pageNavigator.length > 0 && !disableBackPage) {
                bim.app.close.page({ animatePages: true });
                return;
              }
              console.log(
                "HTB_NATIVE_LOG",
                "bim.app.storyboard.goBack()",
                "Move to previous plugin in APP.."
              );
            };
          },

          /**
           * @description
           * Function called directly or indirectly when user goes back.
           *
           * Run by <code>bim.app.storyboard.goBack();</code> or Android Backbutton.
           *
           * If page exists, close page with
           * <code>bim.app.close.page({ animatePages: true });</code>
           *
           * If there is no page, go to previous plugin
           * @memberof bim.app.storyboard
           * @function goBack
           * @example
           * bim.app.storyboard.goBack();
           */
          goBack: function () {
            if (pageNavigator.length > 0 && !disableBackPage) {
              bim.app.close.page({ animatePages: true });
              return;
            }
            console.log(
              "HTB_NATIVE_LOG",
              "bim.app.storyboard.goBack()",
              "Move to previous plugin in APP.."
            );
          },
        },

        /**
         * @description
         * Funciton to append HTML on document.
         * @memberof bim.app
         * @function appendHTML
         * @argument {string} selector
         * Selector, used to find
         * parent element, which child would be appended,
         * by calling
         * <code>document.querySelectorAll(arguments[0]);</code>         *
         * @argument {DOM} nodes
         * Child nodes which would be append to parent.
         * @returns {}
         * @example
         * bim.app.appendHTML(${1:selector}, ${2:nodes})
         */
        appendHTML: function appendHTML() {
          var this$1;
          var content = arguments[1];

          if (!arguments[0] || arguments[0].length == 0) return;

          this$1 = document.querySelectorAll(arguments[0]);
          if (this$1.length == 0) return;

          this$1 = this$1[0];

          if (!this$1) return;

          var args = [];
          var nodes =
            typeof arguments[1] == "string"
              ? arguments
              : arguments[1].childNodes;
          var len = nodes.length;

          while (len--) args[len] = nodes[len];
          var newChild;
          for (var k = 0; k < args.length; k++) {
            newChild = args[k];

            if (typeof newChild === "string") {
              if (__lib.app.isHTML(newChild)) {
                var tempDiv = document.createElement("div");
                tempDiv.innerHTML = newChild;
                while (tempDiv.firstChild) {
                  this$1.appendChild(tempDiv.firstChild);
                }
              }
            } else {
              this$1.appendChild(newChild);
            }
          }
        },

        /**
         * @description
         * Function to check whether the given string parameter is a tag of HTML or not.
         * @memberof bim.app
         * @function isHTML
         * @param {string} html
         * @returns {boolean}
         * <code>true</code> |
         * <code>false</code>
         * @example
         * if (bim.app.isHTML(${1:html})) ..;
         */
        isHTML: function isHTML(html) {
          if (!html) return false;

          var isHTML = RegExp.prototype.test.bind(/(<([^>]+)>)/i);

          if (isHTML(html)) {
            return true;
          }

          return false;
        },

        /**
         * @description
         * Function to translate language.
         * @memberof bim.app
         * @async
         * @function translate
         * @param {Object} params
         * <b><i>Required.</i></b>
         * @param {*} params.template
         * @param {boolean=} params.detect
         * @returns {}
         */
        translate: function (params) {
          return new Promise(function (rs, rj) {
            if (!params || params == "")
              return rj({
                error: true,
                message: "Params are empty",
                errno: "js8374bt",
              });

            var detect = params.detect || params.detect !== "" ? true : false;

            async function _run() {
              var langObject = await __lib.app.logicless.lang.get({
                lang: params.lang,
                detect: detect,
              });
              if (langObject.error) {
                return rj(langObject);
              }

              var tmp = await __lib.app.logicless.tokenizer(params.template);
              var ast = await __lib.app.logicless.methods.ast(tmp);

              return rs({ error: false, data: "builder" });
            }

            _run();
          });
        },
      },

      /**
       * @description
       * Function to add event listener to element($) on event type given.
       * @memberof bim
       * @function event
       * @param {Object} params parameters
       * @param {string} params.$
       * element wanted listener to be added
       *
       * @param {string} params.on
       * Used as event type
       *
       * @param {function} callback
       * The callback binded to the listener (element, event) as params
       * @param {boolean} capture
       * <code>true</code> | <code>false</code>
       */
      event: function (params, callback, capture) {
        if (!params.$ || params.$.length == 0) return;

        var ev = params.on || params.on !== "" ? params.on : false;
        var el = document.querySelector(params.$);

        if (!ev || !el) {
          // Live event
          var e = params.$.trim();
          var elType = e.charAt(0);

          document.addEventListener(
            ev,
            function (event) {
              var element = event.target;
              var found;

              if (elType == "#" && element.getAttribute("id")) {
                var cl = "#" + element.getAttribute("id").trim();
                if (cl === e) {
                  callback.call(element, event);
                }
              } else if (elType == ".") {
                var matches = [];

                if (element.tagName == "IMG") {
                  matches = element.parentElement
                    .getAttribute("class")
                    .trim()
                    .split(" ");
                  element = element.parentElement;
                } else if (element.getAttribute("class")) {
                  matches = element.getAttribute("class").trim().split(" ");
                }

                for (var i = 0; i < matches.length; i++) {
                  var cl = "." + matches[i].trim();

                  if (cl === e) {
                    callback.call(element, event);
                  }
                }
              }
            },
            false
          );
        } else {
          el.addEventListener(params.on, callback);
        }
      },

      /**
       * @description
       * Returns the first <code>Element</code> within the document
       * that matches the specified selector, or group of selectors.
       * If no matches are found, <code>null</code> is returned.
       * @memberof bim
       * @function querySelector
       * @param {string} element
       * one or more selectors to match. must be a valid CSS selector string.
       * @returns {Element}
       * Object representing the first element in the document that matches the selectors.
       * <code>null</code> is returned if there are no matches.
       */
      querySelector: function (element) {
        // BYPASS ONLY
        return document.querySelector(element);
      },

      /**
       * @description
       * Collection of functions that manage plugin.
       * Manages the overall life cycle of the plugin,
       * such as created, resumed, and destroyed.
       * Manages the information of the plugin by offering
       * getters and setters.
       * @namespace bim.plugin
       * @type {Object}
       * @property {function} onCreate
       * Callback function called when plugin is created.
       * See {@link bim.plugin.onCreate} for more details.
       *
       * @property {function} onResume
       * Callback function called when plugin is resumed.
       * See {@link bim.plugin.onResume} for more details.
       *
       * @property {function} onDestroy
       * Callback function called when plugin is destroyed.
       * See {@link bim.plugin.onDestroy} for more details.
       *
       * @property {Object} name
       * Object to control the name of the plugin.
       * See {@link bim.plugin.name} for more details.
       *
       * @property {Object} token
       * Object to control the token of the plugin.
       * See {@link bim.plugin.token} for more details.
       *
       * @property {Object} id
       * Object to control the id of the plugin.
       * See {@link bim.plugin.id} for more details.
       *
       * @property {Object} path
       * Object saving the location information of the plugin.
       * See {@link bim.plugin.path} for more details.
       */
      plugin: {
        /**
         * @description
         * Callback function called when plugin is created.
         * @memberof bim.plugin
         * @function onCreate
         * @param {*} params
         */
        onCreate: function (params) {},

        /**
         * @description
         * Callback function called when plugin is resumed.
         * @memberof bim.plugin
         * @function onResume
         * @param {*} params
         */
        onResume: function (params) {},

        /**
         * @description
         * Callback function called when plugin is destroyed.
         * @memberof bim.plugin
         * @function onDestroy
         * @param {*} params
         */
        onDestroy: function (params) {},

        /**
         * @description
         * Manages Plugin Name with private variable.
         * Provides getter & setter.
         * @memberof bim.plugin
         * @name name
         * @type {Object}
         * @property {function} set
         * Set Plugin Name to private variable <code>\_\_name\_\_</code> with parameter passed over.
         *
         * @property {function} get
         * Get Plugin Name from private variable <code>\_\_name\_\_</code>.
         */
        name: {
          /**
           * @description
           * Set Plugin Name
           * @memberof bim.plugin.name
           * @function set
           * @param {string} v Required value for plugin name.
           * @example
           * bim.plugin.name.set(${1:name});
           */
          set: function (v) {
            if (!v) return;

            __name__ = v;
          },

          /**
           * @description
           * Get Plugin Name
           * @memberof bim.plugin.name
           * @function get
           * @returns {string} Plugin Name
           * @example
           * bim.plugin.name.get();
           */
          get: function () {
            return __name__;
          },
        },

        /**
         * @description
         * Manage Plugin Token with private variable.
         * Provides getter & setter.
         * @memberof bim.plugin
         * @name token
         * @type {Object}
         * @property {function} set
         * Set Plugin Token to private variable <code>\_\_token\_\_</code>.
         *
         * Makes an error when reading file "key" fails.
         * @property {function} get
         * Get Plugin Token from private variable <code>\_\_token\_\_</code>.
         */
        token: {
          /**
           * @memberof bim.plugin.token
           * @function set
           * @returns {Object} Error report Object when error occurs.
           */
          set: function () {
            return new Promise(function (rs, rj) {
              __lib.readFile({ file: "key" }, function (token) {
                if (token.error) {
                  return rj({
                    errno: "e83b364",
                    description:
                      "Verify key file exists in your Plugin root directory and contains your token key",
                  });
                } else {
                  __token__ = token.data;
                  return rs();
                }
              });
            });
          },

          /**
           * @memberof bim.plugin.token
           * @function get
           * @returns {string} Get token data from private variable.
           */
          get: function () {
            return __token__;
          },
        },

        /**
         * @description
         * Manage Plugin ID with private variable.
         * Provides getter & setter.
         * @memberof bim.plugin
         * @name id
         * @type {Object}
         * @property {function} set
         * Set Plugin ID to private variable <code>\_\_pluginId\_\_</code> with parameter passed over.
         * @property {function} get
         * Get Plugin ID from private variable <code>\_\_pluginId\_\_</code>.
         */
        id: {
          /**
           * @description
           * Set Plugin ID to private variable.
           * @memberof bim.plugin.id
           * @function set
           * @param {string} v Plugin ID
           */
          set: function (v) {
            if (!v) return;

            __pluginId__ = v;
          },

          /**
           * @description
           * Get Plugin ID from private variable.
           * @memberof bim.plugin.id
           * @function get
           * @returns {string} Plugin ID from private variable.
           */
          get: function () {
            return __pluginId__;
          },
        },

        /**
         * @description
         * Object saving the location information of the plugin.
         * @memberof bim.plugin
         * @name path
         * @type {Object}
         * @property {string} current <code>window.location.href</code>
         * @property {Object} options
         * @property {string} options.hash <code>window.location.hash</code>
         * @property {string} options.host <code>window.location.host</code>
         * @property {string} options.hostname <code>window.location.hostname</code>
         * @property {string} options.href <code>window.location.href</code>
         * @property {string} options.origin <code>window.location.origin</code>
         * @property {string} options.pathname <code>window.location.pathname</code>
         * @property {string} options.port <code>window.location.port</code>
         * @property {string} options.protocol <code>window.location.protocol</code>
         * @property {string} options.search <code>window.location.search</code>
         */
        path: (function () {
          return {
            current: window.location.href,
            options: {
              hash: window.location.hash,
              host: window.location.host,
              hostname: window.location.hostname,
              href: window.location.href,
              origin: window.location.origin,
              pathname: window.location.pathname,
              port: window.location.port,
              protocol: window.location.protocol,
              search: window.location.search,
            },
          };
        })(),
      },

      /**
       * @description
       * Object which has function to read files related to config.
       * @namespace bim.config
       * @property {function} plugins_installed
       * Function to read and pass data of installed plugins in config file.
       * See {@link bim.config.plugins_installed} for more details
       */
      config: {
        /**
         * @description
         * Function to read config file and run callback function with read data.
         * File must have ".json" extension.
         * @function plugins_installed
         * @memberof bim.config
         * @param {string} file
         * File Name of ".json".
         * Must be sent without an extension.
         * @param {function} callback
         * Callback Function.
         * Called with below parameter in Object type.
         *
         * { error: {boolean}, data: {string}, message: {Error} }
         * @example
         * bim.config.plugins_installed('plugins', function (loaded) {
         *  if (!loaded.error) {
         *    try {
         *      let json = JSON.parse(loaded.data);
         *      let tempJson = JSON.stringfy(json[0]);
         *      let keys = Object.keys(json[0]);
         *
         *      // Do Something ...
         *    } catch (e) {
         *      // Do Something ...
         *    }
         *  } else {
         *    // Do Something ...
         *  }
         * });
         */
        plugins_installed: function (file, callback) {
          if (!file) return false;

          var request = new XMLHttpRequest();
          request.open("GET", file + ".json", true);
          request.onload = function () {
            if (
              (request.status >= 200 && request.status < 400) ||
              this.status == 0
            ) {
              var response = {};
              response.error = false;
              response.data = request.responseText;
              return callback(response);
            }
          };
          request.onerror = function (err) {
            var response = {};
            response.error = true;
            response.message = err;
            return callback(response);
          };
          request.send();
        },
      },

      /**
       * @description
       * Collection of functions which control the App natively.
       * @namespace bim.native
       * @property {Object} device
       * Object which stores the OS of the current device.
       * See {@link bim.native.device} for more details.
       *
       * @property {Object} backButton
       * Native backbutton API.
       * See {@link bim.native.backButton} for more details.
       *
       * @property {Object} keyboard
       * Native keyboard setting API.
       * See {@link bim.native.keyboard} for more details.
       *
       * @property {function} removeHistory
       * Function to remove history of visited plugins.
       * See {@link bim.native.removeHistory} for more details.
       *
       * @property {function} internalBrowser
       * Function to open URL with internal browser.
       * See {@link bim.native.internalBrowser} for more details.
       *
       * @property {function} shareUrl
       * Function to share url through native feature.
       * See {@link bim.native.shareUrl} for more details.
       *
       * @property {function} loadService
       * Function to load service.
       * See {@link bim.native.loadService} for more details.
       *
       * @property {function} exec
       * Function to call native functions.
       * See {@link bim.native.exec} for more details.
       *
       * @property {function} cameraChooser
       * Function to make file-input to save file data internally.
       * See {@link bim.native.cameraChooser} for more details.
       *
       * @property {function} plugin
       * Function to run native functions.
       * See {@link bim.native.plugin} for more details.
       *
       * @property {function} load
       * Function to run native functions.
       * See {@link bim.native.load} for more details.
       *
       * @property {function} call
       * Function to call native functions.
       * See {@link bim.native.call} for more details.
       *
       * @property {function} handleCallback
       * Callback function saved by {@link bim.native.call}.
       * See {@link bim.native.handleCallback} for more details.
       *
       * @property {Object} handleCallbacks
       * Object saving callback functions seperated by name.
       * See {@link bim.native.handleCallbacks} for more details.
       *
       * @property {function} callback
       * Function to read media file and save into local database.
       * See {@link bim.native.callback} for more details.
       *
       * @property {Object} fileSystem
       * Object to manage local files in the device.
       * See {@link bim.native.fileSystem} for more details.
       */
      native: (function () {
        return {
          /**
           * @description
           * Object which stores separately whether OS of the current device matches each iOS or Android.
           * @memberof bim.native
           * @name device
           * @property {boolean} ios
           * Whether current device is ios or not
           * @property {boolean} android
           * Whether current device is android or not
           */
          device: (function () {
            return {
              ios: false,
              android: false,
            };
          })(),

          /**
           * @description
           * Native backbutton API.
           * Currently, only works for Android.
           * @memberof bim.native
           * @name backButton
           * @property {function} set
           * Function to set how native backbutton works.
           * @example
           * bim.native.backButton.set({
           *  backplugin: ${1:backplugin},
           *  backpage: ${2:backpage},
           *  backButton: ${3:backButton}
           * });
           * @example
           * bim.native.backButton.set({
           *  backplugin: MAIN_PLUGIN,
           *  backpage: true,
           *  backButton: "enabled"
           * });
           * @example
           * bim.native.backButton.set({
           *  backpage: false
           * });
           */
          backButton: {
            /**
             * @description
             * Function to set how native backbutton works.
             * @memberof bim.native.backButton
             * @function set
             * @param {Object} params
             * <b><i>Required.</i></b>
             * @param {string} params.backplugin
             * @param {boolean} params.backpage
             * Value, which would be the value of
             * private variable <code>disableBackPage</code>
             * @param {boolean | string} params.backButton
             * <code>true</code>, <code>false</code> |
             * "disable", "disabled", "enable", "enabled"
             */
            set: function (params) {
              if (!params || params == "") return;

              let data = {};

              if (params.hasOwnProperty("backplugin"))
                data.backplugin = params.backplugin;
              if (params.hasOwnProperty("backpage"))
                disableBackPage = !params.backpage;

              switch (params.backButton) {
                case false:
                case "disable":
                case "disabled":
                  data.set = "disable";
                  break;
                case true:
                case "enable":
                case "enabled":
                  data.set = "enable";
                  break;
              }

              console.log(
                "HTB_NATIVE_LOG",
                "bim.native.backButton.set()",
                "Android backbutton setting : ",
                params
              );
              return;
            },
          },

          /**
           * @description
           * Native keyboard setting API.
           * Currently, only works for Android OS now.
           * @memberof bim.native
           * @name keyboard
           *
           * @property {function} set
           * Function to set callback function, which runs
           * when native keyboard is opened or closed.
           * Currently only works for Android OS now.
           *
           * @property {function} onOpen
           * Callback function which runs when keyboard is opened.
           * Must be set through function
           * <code>bim.native.keyboard.set</code>.
           *
           * @property {function} onClose
           * Callback function which runs when keyboard is closed.
           * Must be set through function
           * <code>bim.native.keyboard.set</code>.
           *
           * @example
           * bim.native.keyboard.set({
           *  onOpen: function () {
           *    // Do Something ...
           *  },
           *  onClose: function () {
           *    // Do Something ...
           *  }
           * });
           */
          keyboard: {
            /**
             * @description
             * Function to set callback function, which runs
             * when native keyboard is opened or closed.
             * Currently only works for Android OS now.
             * @memberof bim.natvie.keyboard
             * @function set
             * @param {Object} params
             * @param {function} params.onOpen
             * Callback function which runs when keyboard is opened.
             * @param {function} params.onClose
             * Callback function which runs when keyboard is closed.
             * @returns {}
             */
            set: function (params) {
              console.log(
                "HTB_NATIVE_LOG",
                "bim.native.keyboard.set(params)",
                "Keyboard Open & Close callback is set for App .."
              );
            },

            /**
             * @description
             * Callback function which runs when keyboard is opened.
             * Must be set through function
             * <code>bim.native.keyboard.set</code>.
             * @memberof bim.natvie.keyboard
             * @function onOpen
             * @param {*} params
             */
            onOpen: function (params) {},
            /**
             * @description
             * Callback function which runs when keyboard is closed.
             * Must be set through function
             * <code>bim.native.keyboard.set</code>.
             * @memberof bim.natvie.keyboard
             * @function onClose
             * @param {*} params
             */
            onClose: function (params) {},
          },

          /**
           * @description
           * Function to remove history of visited plugins
           * and reload whole plugins except current plugin.
           *
           * Currently only works for Android OS now.
           *
           * :: PROBLEM ::
           *
           * HARD TO USE WITH
           * {@link bim.app.storyboard.moveTo},
           * DUE TO SYNC PROBLEM.
           *
           * RECOMMENDED TO USE AFTER
           * {@link bim.app.storyboard.moveTo},
           * NOT BEFORE.
           * @memberof bim.native
           * @function removeHistory
           * @returns {}
           * @example
           * bim.native.removeHistory();
           */
          removeHistory: function () {
            console.log(
              "HTB_NATIVE_LOG",
              "bim.native.removeHistory()",
              "Remove history of visited plugin in App .."
            );
          },

          /**
           * @description
           * Function to open URL with internal browser.
           * Currently only works for Android OS now.
           * @memberof bim.native
           * @function internalBrowser
           * @param {string} url
           * <b><i>Required.</i></b>
           * @returns {Object | void}
           * if url is empty,
           * returns error object with errno, message, description. |
           * void
           * @example
           * bim.native.internalBrowser(${1:url});
           */
          internalBrowser: function (url) {
            if (!url || url == "" || typeof url !== "string")
              return {
                error: true,
                errno: "031623b",
                message: "No url found",
                description: "To use internalBrowser, url value is necessary",
              };
            console.log(
              "HTB_NATIVE_LOG",
              "bim.native.internalBrowser(url)",
              `${url} is opened in new view in APP ..`
            );
          },

          /**
           * @description
           * Function to share url through native feature.
           * Currently only works for Android OS now.
           * @memberof bim.native
           * @function shareUrl
           * @param {string} url
           * <b><i>Required.</i></b>
           * @returns {Object | void}
           * if url is empty, returns error object with errno, message, description.
           * @example
           * bim.native.shareUrl(${1:url});
           */
          shareUrl: function (url) {
            if (!url || url == "" || typeof url !== "string")
              return {
                error: true,
                errno: "071622b",
                message: "No url found",
                description:
                  "To use a share function, it is necessary to add an url",
              };
            console.log(
              "HTB_NATIVE_LOG",
              "bim.native.shareUrl(url)",
              `${url} is shared in APP ..`
            );
          },

          /**
           * @description
           * Function to load service.
           * Used in bundle.js in APP.
           * Currently only works for Android now.
           * @memberof bim.native
           * @function loadService
           * @param {Object} params
           * <b><i>Required.</i></b>
           * @returns {Object | void}
           * if params is empty, returns error object with errno, message, description.
           */
          loadService: function (params) {
            if (!params || params == "") {
              console.error({
                error: true,
                errno: "eO072bx3",
                description:
                  "Parameters cannot be empty, read the documentation to know more about",
                message:
                  "Oops! Something went wrong implementing native plugin",
              });
            }
          },

          /**
           * @description
           * Function to call native functions.
           * Currently, only works for Android OS now.
           * @memberof bim.native
           * @function exec
           * @param {Object} params
           * <b><i>Required.</i></b>
           * @param {string} params.hardware
           * @param {string} params.event
           * Custom parameters for a native function
           * @param {string} params.fn
           * The name of callback function
           * @param {string} params.callback
           * @param {Object | string} params.data
           * @returns {}
           * @example
           * bim.native.exec({
           *  hardware: 'gallery',
           *  event: 'event_name',
           *  fn: 'my_callback_function_name',
           *  data: 'my_data'
           * });
           */
          exec: function (params) {},

          /**
           * @description
           * Function to make file-input and
           * save data to internal database of the App.
           * Used in AnimalZ - post feature in App.
           * @memberof bim.native
           * @function cameraChooser
           * @param {Object=} params
           */
          cameraChooser: function (params) {},

          /**
           * @description
           * Function to run native functions.
           * Currently only works for Android OS now.
           * @memberof bim.native
           * @function plugin
           * @param {Object} params
           * <b><i>Required.</i></b>
           * @example
           * bim.native.plugin(${1:params});
           */
          plugin: function (params) {},

          /**
           * @description
           * Function to run native functions.
           * Currently only works for Android OS now.
           * @memberof bim.native
           * @function load
           * @param {Object} params
           * <b><i>Required.</i></b>
           * @example
           * bim.native.load(${1:params});
           */
          load: function (params) {},

          /**
           * @description
           * Function to call native functions.
           * Sets {@link bim.native.handleCallback} and
           * {@link bim.native.handleCallbacks}
           * with this function.
           * @memberof bim.native
           * @function call
           * @param {Object} params
           * <b><i>Required.</i></b>
           * @param {string} params.name
           * @param {string} params.method
           * @param {function} params.callback
           * @param {Object} params.params
           * @example
           * bim.native.call({
           *  'name': ${1:name},
           *  'method': ${2:method},
           *  'callback': ${3:callback},
           *  'params': ${4:params}
           * });
           * @example
           * bim.native.call({
           *  'name': 'Login',
           *  'method': 'kakaoLogin',
           *  'callback': () => {
           *    // Do Something ...
           *  },
           *  'params': null
           * });
           */
          call: function (params) {},

          /**
           * @description
           * Callback function saved by {@link bim.native.call}.
           * Used in native source codes.
           * @memberof bim.native
           * @function handleCallback
           */
          handleCallback: function () {},

          /**
           * @description
           * Object saving callback functions seperated by name.
           * Values are set by {@link bim.native.call}.
           * @memberof bim.native
           * @name handleCallbacks
           * @type {Object}
           */
          handleCallbacks: {},

          /**
           * @description
           * Function to read media file and save into local database.
           * This method runs in native source camera feature.
           * @memberof bim.native
           * @function callback
           * @param {string} data
           * <b><i>Required.</i></b>
           * Stringfied data. Would be used after
           * <code>JSON.parse(data);</code>
           * @param {string} data.source
           * File full path url.
           * @param {string} data.type
           * Type of the file. Should be in ["image", "video"]
           * @param {string} data.page
           * The page to go to after all the work is done.
           * @param {string} data.plugin
           * The plugin to go to after all the work is done.
           * @returns {}
           */
          callback: function (data) {},

          /**
           * @description
           * Object to manage local files in the device.
           * @memberof bim.native
           * @name fileSystem
           * @type {Object}
           * @property {function} read
           * Function to read.
           *
           * @property {function} readByPath
           * Function to read file from device by file path.
           *
           * @property {function} get
           * Function to get recently read file data.
           *
           * @property {function} reset
           * Function to remove reserved read file data.
           */
          fileSystem: {
            /**
             * @description
             * Open file chooser in native
             * @memberof bim.native.fileSystem
             * @function read
             */
            read: function () {
              // 1. OPEN FILE CHOOSER IN NATIVE
              // 2. AND RUN BELOW readByPath METHOD AND READ THAT FILE IN JS!
              console.log(
                "Run File Selector in Native And read that file in JS!"
              );
            },

            /**
             * @description
             * Function to read file from device by file path.
             * @memberof bim.native.fileSystem
             * @function readByPath
             * @async
             * @param {string} params
             * <b><i>Required.</i></b>
             * Stringfied JSON Object
             * @param params.db USE DB TABLE NAME, IF NULL, USE "Htb_FileSystem"
             * @param params.path FILE PATH ARRAY
             */
            readByPath: async function (params, callback) {
              // IF THIS IS NOT CALLBACK, ADD DEFAULT CALLBACK
              if (!callback || typeof callback != "function")
                callback = function (err, data) {
                  if (err) return console.error(err);
                };

              let parsedParams; // parsed params
              let data; // urlData
              let IDBName; // IDB name

              try {
                parsedParams = JSON.parse(params);
                IDBName = parsedParams.db ? parsedParams.db : "Htb_FileSystem";
                data = parsedParams.path ? parsedParams.path : false;
              } catch (e) {
                return callback({
                  error: true,
                  errno: "e492416",
                  message: "Failed to send JSON data",
                });
              }

              if (!data)
                return callback({
                  error: true,
                  errno: "e192416",
                  message: "Failed to get filepath data",
                });

              let fileContainer = [];

              for (let i = 0; i < data.length; i++) {
                try {
                  let file = await fileReadRequest(data[i]);
                  fileContainer.push(file);
                } catch (e) {
                  return callback(e);
                }
              }

              let IDB_params = {
                delete: {
                  db: {
                    name: IDBName,
                    table: "resources",
                    key: "files",
                  },
                },

                create: {
                  db: {
                    name: IDBName,
                    table: "resources",
                    columns: ["data"],
                    keyPath: "config",
                  },
                },

                addtable: {
                  db: {
                    table: "newOne",
                    name: IDBName,
                    version: 0, // res1.version,
                    keyPath: "config",
                    columns: ["data"],
                  },
                },

                addFile: {
                  db: {
                    name: IDBName,
                    table: "resources",
                    item: fileContainer, // File
                    keyPath: {
                      name: "config",
                      value: "files",
                    },
                  },
                },
              };

              bim.app.data.db.create(IDB_params.create, function (res1) {
                bim.app.data.db.deleteFile(IDB_params.delete, function (res2) {
                  if (res2 && res2.length == 0) {
                    IDB_params.addtable.db.version = res2.version + 1;

                    bim.app.data.db.addTable(
                      IDB_params.addtable,
                      function (res3) {
                        addFileIDB();
                      }
                    );
                  } else {
                    addFileIDB();
                  }
                });
              });

              function fileReadRequest(fileurl) {
                return new Promise(function (rs, rj) {
                  let request = new XMLHttpRequest();
                  let filePath = "file://" + fileurl;
                  let filename = fileurl.split("/").pop();
                  request.responseType = "arraybuffer";
                  request.open("GET", filePath, true);
                  request.onload = function () {
                    let blob = new Blob([request.response]);
                    let file = new File([blob], filename);
                    return rs(file);
                  };
                  request.onerror = function (err) {
                    return rj({
                      error: true,
                      errno: "e292811",
                      message: "Failed to read file in device",
                    });
                  };
                  request.send();
                });
              }

              function addFileIDB() {
                bim.app.data.db.addFile(IDB_params.addFile, function (res4) {
                  if (res4.error) {
                    return callback({
                      error: true,
                      errno: "e292819",
                      message: "Failed to save readed files in memory",
                    });
                  }
                  return callback(false, fileContainer);
                });
              }
            },

            /**
             * @description
             * Function to get recently read file data.
             * Can share files by this method in every plugin in the app.
             *
             * Calls internally,
             *
             * <code>bim.app.data.db.read</code>.
             * @memberof bim.native.fileSystem
             * @function get
             * @async
             * @param {Object} params
             * <b><i>Required.</i></b>
             * @param {string} params.db
             * <b><i>Required.</i></b>
             * Would be used as
             * <code>db.name</code>
             * for parameter of
             * <code>bim.app.data.db.read</code>
             * @param {function} callback
             * <b><i>Required.</i></b>
             * argument 1 : {boolean} Error Y/N
             * argument 2 : {Object} Read file data
             * @returns {*}
             * Result of callback function.
             */
            get: async function (params, callback) {
              if (!callback || typeof callback != "function")
                return console.warn({
                  error: true,
                  errno: "e824219",
                  message: "Use this method with callback",
                });

              let IDBName = params && params.db ? params.db : "Htb_FileSystem";
              if (!IDBName || IDBName == "")
                return console.warn({
                  error: true,
                  errno: "e2824219",
                  message: "db name must be not empty string",
                });

              try {
                let filesData = await readDB();
                return callback(false, filesData);
              } catch (e) {
                return callback(e);
              }

              async function readDB() {
                return new Promise(function (rs, rj) {
                  let IDB_ReadParam = {
                    db: {
                      name: IDBName,
                      table: "resources",
                      key: "files",
                    },
                  };

                  bim.app.data.db.read(IDB_ReadParam, function (res) {
                    if (res.error)
                      return rj({
                        error: true,
                        errno: "e192819",
                        message: "Failed to get reserved file data",
                      });
                    return rs(res.data);
                  });
                });
              }
            },

            /**
             * @description
             * Function to remove reserved read file data.
             *
             * Calls internally,
             *
             * <code>bim.app.data.db.deleteFile</code>
             * @memberof bim.native.fileSystem
             * @function reset
             * @param {Object} params
             * <b><i>Required.</i></b>
             * @param {string} params.db
             * <b><i>Required.</i></b>
             * @returns {} void
             */
            reset: function (params) {
              let IDBName = params && params.db ? params.db : "Htb_FileSystem";
              if (!IDBName || IDBName == "")
                return console.warn({
                  error: true,
                  errno: "e1824219",
                  message: "db name must be not empty string",
                });

              let IDB_DeleteParam = {
                db: {
                  name: IDBName,
                  table: "resources",
                  key: "files",
                },
              };

              bim.app.data.db.deleteFile(IDB_DeleteParam, function (res) {
                if (res.error) {
                  return console.error("Failed to reset read file");
                } else {
                  console.log("File data deleted successfully!");
                }
              });
            },
          },
        };
      })(),

      /**
       * @description
       * An Object which is a collection of functions that manage components.
       * The component currently has "tabbar" only.
       * @namespace bim.component
       * @property {function} hide
       * Function to hide component.
       * See {@link bim.component.hide} for more details.
       *
       * @property {function} show
       * Function to show component.
       * See {@link bim.component.show} for more details.
       *
       * @property {function} active
       * Function to activate component.
       * See {@link bim.component.active} for more details.
       *
       * @property {function} set
       * Function to set component.
       * See {@link bim.component.set} for more details.
       *
       * @property {function} build
       * Function to build component.
       * See {@link bim.component.build} for more details
       */
      component: {
        /**
         * @description
         * Function to hide component.
         * Currently, only "tabbar" is supported.
         * Defaultly sets the animation delay to
         * <code>500</code> ms.
         * @memberof bim.component
         * @function hide
         * @param {Object} params
         * <b><i>Required.</i></b>
         * @param {string} params.type
         * Currently, only "tabbar" is supported.
         * @param {number} [params.delay=500]
         * Delay of the animation.
         * @returns {}
         * @example
         * bim.component.hide({type: "tabbar", delay: 300});
         */
        hide: function (params) {
          if (!params || params == "") return;
          if (params.type == "tabbar") {
            if (!params.delay) params.delay = 500;
            console.log(
              "HTB_NATIVE_LOG",
              "bim.component.hide()",
              `${params.type} is hidden ..`
            );
          }
        },

        /**
         * @description
         * Function to show component.
         * Currently, only "tabbar" is supported.
         * @memberof bim.component
         * @function show
         * @param {Object} params
         * <b><i>Required.</i></b>
         * @param {string} params.type
         * Currently, only "tabbar" is supported.
         * @returns {}
         * @example
         * bim.component.show({type: "tabbar"});
         */
        show: function (params) {
          if (!params || params == "") return;
          if (params.type == "tabbar") {
            console.log(
              "HTB_NATIVE_LOG",
              "bim.component.show()",
              `${params.type} is opened ..`
            );
          }
        },

        /**
         * @description
         * Function to activate component.
         * Currently, only "tabbar" is supported.
         * @memberof bim.component
         * @function active
         * @param {Object} params
         * <b><i>Required.</i></b>
         * @param {string} params.type
         * Currently, only "tabbar" is supported.
         * @returns {}
         * @example
         * bim.component.active({type: "tabbar"});
         */
        active: function (params) {
          if (!params || params == "") return;
          if (params.type == "tabbar") {
            if (bim.native.device.android) {
              window.native.activeTabbar(JSON.stringify(params));
            } else if (bim.native.device.ios) {
              window.webkit.messageHandlers.activeTabbar.postMessage(
                JSON.stringify(params)
              );
            }
          }
        },

        /**
         * @description
         * Function to set component.
         * @memberof bim.component
         * @function set
         * @param {Object} params
         * <b><i>Required.</i></b>
         * @param {*} params.type
         * <b><i>Required.</i></b>
         * @param {*} params.id
         * <b><i>Required.</i></b>
         * @param {*} params.css
         * <b><i>Required.</i></b>
         * @param {*} params.js
         * <b><i>Required.</i></b>
         * @param {*} params.assets
         * <b><i>Required.</i></b>
         * @param {*} params.level
         * <b><i>Required.</i></b>
         * @param {*} params.body
         * <b><i>Required.</i></b>
         * @returns {}
         * @example
         * const bimComponent = {
         *  type: "tabbar",
         *  id: ${1:id},
         *  css: ${2:css},
         *  js: ${3:js},
         *  assets: ${4:assets},
         *  level: ${5:level},
         *  body: ${6:body},
         * };
         * bim.component.set(bimComponent);
         */
        set: function (params) {
          if (!params || params == "" || typeof params !== "object") return;

          if (
            !params.type ||
            !params.id ||
            !params.css ||
            !params.js ||
            !params.assets ||
            !params.level ||
            !params.body
          )
            return console.error(
              "bimDB: Err-72M Some required parameters are missing"
            );

          console.log(
            "HTB_NATIVE_LOG",
            "bim.component.set()",
            `${params.type} is set ..`
          );
        },

        /**
         * @description
         * Function to build component.
         * Currently, only "Android" is supported.
         * Currently, only "tabbar" is supported.
         * @memberof bim.component
         * @function build
         * @param {Object} params
         * @param {string} params.type
         * Currently, only "tabbar" is supported.
         * @deprecated
         * It is same with {@link bim.component.show}
         * @example
         * bim.component.build({type: "tabbar"});
         */
        build: function (params) {
          console.log(
            "HTB_NATIVE_LOG",
            "bim.component.build()",
            `${params.type} is built ..`
          );
        },
      },

      /**
       * @description
       * Function to read the given file path and call the given function back with
       * the read file as data property.
       * @memberof bim
       * @function readFile
       * @param {Object} params
       * @param {string} params.file
       * Path of the file
       * @param {string} params.onErrorMessage
       * error message which would be sent to callback
       * as parameter message when error occurs
       * @param {function} callback
       * @returns {*}
       * result got from the callback function
       */
      readFile: function (params, callback) {
        if (!params || !params.file || params.file == "")
          return callback({
            error: true,
            message: "File path cannot be empty",
            errno: "83v363v4555",
          });

        var request = new XMLHttpRequest();
        request.open("GET", params.file, true);
        request.onload = function () {
          if (
            (request.status >= 200 && request.status < 400) ||
            this.status == 0
          ) {
            return callback({ error: false, data: request.responseText });
          } else {
            return callback({
              error: true,
              message: params.onErrorMessage,
              errno: "934b45645v45",
            });
          }
        };
        request.onerror = function (err) {
          return callback({
            error: true,
            message: params.onErrorMessage,
            description: err,
            errno: "84b65ba74",
          });
        };
        request.send();
      },

      /**
       * @description
       * Collection of Objects to control elements of the document.
       * Currently, only has
       * <code>bim.crawling.inject.css.apply</code>
       * @namespace bim.crawling
       * @type {Object}
       * @property {Object} inject
       * Collection of Objects to inject elements to the document.
       * See {@link bim.crawling.inject} for more details
       */
      crawling: {
        /**
         * @description
         * Collection of Objects to inject elements to the document.
         * @memberof bim.crawling
         * @name inject
         * @type {Object}
         * @property {Object} css
         * Set of functions that create or manage style elements to inject into the document.
         * @property {function} css.apply
         * Function to create style element and append to head of document.
         */
        inject: {
          /**
           * @description
           * Set of functions that create or manage
           * style elements to inject into the document.
           * @memberof bim.crawling.inject
           * @name css
           * @type {Object}
           */
          css: {
            /**
             * @description
             * Function to create style element and append to head of document.
             *
             * Used in
             * <code>bim.app.init</code>
             * @memberof bim.crawling.inject.css
             * @function apply
             * @param {string} css
             * Required.
             */
            apply: function (css) {
              if (!css || css == "") return;

              var styleSheet = document.createElement("style");
              styleSheet.type = "text/css";
              styleSheet.innerText = css;
              document.head.appendChild(styleSheet);
            },
          },
        },
      },

      /**
       * @description
       * General static functions which can be called with <code>bim.functions</code>
       * @namespace bim.functions
       * @property {function} isHTML
       * Function to check whether the given string parameter is a tag of HTML or not.
       * See {@link bim.functions.isHTML} for more details.
       *
       * @property {function} parse
       * Function to parse to JSON.
       * See {@link bim.functions.parse} for more details.
       *
       * @property {function} text
       * Function to randomly generate alphabetical string with given parameter number.
       * See {@link bim.functions.text} for more details.
       *
       * @property {function} unique
       * Function to remove duplicated values from an Array.
       * See {@link bim.functions.unique} for more details.
       */
      functions: {
        /**
         * @description
         * Function to check whether the given string parameter is
         * a valid tag of HTML or not.
         * @memberof bim.functions
         * @function isHTML
         * @param {string} html
         * <b><i>Required.</i></b>
         * String to verify that it is a valid  HTML tag.
         * @returns {boolean}
         * <code>true</code> | <code>false</code>
         * @example
         * if (bim.functions.isHTML(${1:html})) ..;
         * @example
         * let bimHtml = "<p>Hello Bim~</p>";
         *
         * if (bim.functions.isHTML(bimHtml)) {
         *  bim.app.element("#bimElement").append(bimHtml);
         * }
         */
        isHTML: function (html) {
          if (!html) return false;

          var isHTML = RegExp.prototype.test.bind(/(<([^>]+)>)/i);

          if (isHTML(html)) {
            return true;
          }

          return false;
        },

        /**
         * @description
         * Function that firstly parses the given parameter to JSON,
         * secondly calls the callback function and returns the result.
         * @memberof bim.functions
         * @function parse
         * @param {*} j  Object expected to be parsed into JSON
         * @param {function} callback  function
         * @returns {*} callback({boolean}, {parsedObject})
         * @example bim.functions.parse(${1:parameter}, ${2:callback})
         */
        parse: function (j, callback) {
          var messageObject;

          try {
            messageObject = JSON.parse(j);
          } catch (e) {
            if (typeof callback == "function") return callback(true, e);
          }

          if (typeof callback == "function")
            return callback(false, messageObject);
        },

        /**
         * @description
         * Randomly generates alphabetical string with given parameter number
         * @memberof bim.functions
         * @function text
         * @param {number} [n = 16] length of the text
         * @returns {string} randomlly generated text string
         * @example bim.functions.text(${1:number})
         */
        text: function (n) {
          if (typeof n !== "number") {
            n = 16;
          }
          var text = "";
          var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";

          for (var i = 0; i < n; i++) {
            text += possible.charAt(
              Math.floor(Math.random() * possible.length)
            );
          }

          return text;
        },

        /**
         * @description
         * Function that returns new Array duplicated values removed.
         * @memberof bim.functions
         * @function unique
         * @param {Array} arr Array
         * @returns {Array} New Array duplicated valued removed
         */
        unique: function (arr) {
          var uniqueArray = [];
          for (var i = 0; i < arr.length; i += 1) {
            if (uniqueArray.indexOf(arr[i]) === -1) {
              uniqueArray.push(arr[i]);
            }
          }
          return uniqueArray;
        },
      },
    };
  })();

  const require = (function () {
    var __parent_module;
    var root = "project/app";
    var bimsPath = "bims";
    return {
      package: (function () {
        return {
          json: function (params) {
            var a = new XMLHttpRequest();
            a.open("GET", params.package, 0);
            a.send();
            if (X.status && X.status !== 200) {
              return { error: true, description: a.responseText };
            } else {
              return { error: false, data: a.responseText };
            }
          },
        };
      })(),

      modulePath: function (params) {
        return `./${root}/${bimsPath}/${params.module}`;
      },

      load: function () {
        var moduledev = arguments[0];
        var params = arguments[1];
        if (!moduledev || moduledev == "") return;
        if (!w.require.cache) w.require.cache = [];
        var url =
          moduledev.indexOf("./") > -1
            ? moduledev
            : require.modulePath({ module: moduledev }) + "/index.js";
        let __dirname =
          moduledev.indexOf("./") > -1
            ? url
            : url.substring(0, url.lastIndexOf("/"));
        var a = new XMLHttpRequest();
        var ext = moduledev.split(".").pop();
        if (ext === "json") {
          if (typeof __Main_Module__ !== "undefined") {
            moduledev =
              require.modulePath({ module: __Main_Module__ }) +
              "/" +
              moduledev.replace("./", "");
          }

          a.open("GET", moduledev, 0);
          a.send();
          if (a.status && a.status !== 200) {
            console.error({
              error: true,
              description: a.statusText,
              message: "File not found",
            });
          } else {
            try {
              var source = a.responseText;
              return JSON.parse(source);
            } catch (e) {
              return {
                error: true,
                description: e,
                message: "Verify if json file is correct",
              };
            }
          }
        } else {
          // TODO: ADD TRACKING MODULES SEQUENCE
          var exports = w.require.cache[moduledev];

          if (!exports) {
            exports = {};
            a.open("GET", url, 0);
            a.send();
            if (a.status && a.status !== 200) {
              console.error({
                error: true,
                description: a.statusText,
                message: "Verify the bim module is correctly installed",
              });
            } else {
              var isMain = false;
              var isCmmn = "";
              if (typeof __Main_Module__ == "undefined") {
                isMain = moduledev;
                isCmmn = "window.__Main_Module__ = module.main";
              }
              var source = a.responseText;
              if (source.substr(0, 10) === "(function(") {
                var moduleStart = source.indexOf("{");
                var moduleEnd = source.lastIndexOf("})");
                var CDTcomment = source.indexOf("//@ ");
                if (CDTcomment > -1 && CDTcomment < moduleStart + 6)
                  moduleStart = source.indexOf("\n", CDTcomment);
                source = source.slice(moduleStart + 1, moduleEnd - 1);
              }
              var module = {
                id: url,
                uri: url,
                exports: exports,
                main: isMain,
                parent_module: __parent_module,
              };
              source =
                "//@ sourceURL=" + window.location.origin + url + "\n" + source;
              var __module = new Function(
                "require",
                "exports",
                "module",
                "__dirname",
                source + isCmmn
              );
              __module(require.load, exports, module, __dirname);
              w.require.cache[moduledev] = exports = module.exports;
              __parent_module = moduledev;
            }
          }
          return exports;
        }
      },
    };
  })();

  w.require = require.load;
  w.bim = __lib;
})(window);