function createTabControl({ p_div, p_hierarchy, p_layout}) {

	// Get an element's exact position
	function getPosition(el) {
		var xPos = 0;
		var yPos = 0;

		while (el) {
			if (el.tagName == "BODY") {
				var xScroll = el.scrollLeft || document.documentElement.scrollLeft;
				var yScroll = el.scrollTop || document.documentElement.scrollTop;

				xPos += (el.offsetLeft - xScroll + el.clientLeft);
				yPos += (el.offsetTop - yScroll + el.clientTop);
			}
			else {
				xPos += (el.offsetLeft - el.scrollLeft + el.clientLeft);
				yPos += (el.offsetTop - el.scrollTop + el.clientTop);
			}

			el = el.offsetParent;
		}

		return {
			x: xPos,
			y: yPos
		};
	}

  // Initializing HTML elements
	var v_div = document.getElementById(p_div);
  v_div.innerHTML = '';

  var v_nav = document.createElement('nav');
  var v_div_tab_list = document.createElement('div');
  v_div_tab_list.className = 'nav nav-tabs';
  v_nav.appendChild(v_div_tab_list);
  var v_div_tab_content_list = document.createElement('div');
  v_div_tab_content_list.className = 'tab-content omnidb__tab-content';

  var v_tab_menu = document.createElement('div');
  v_tab_menu.className = 'omnidb__tab-menu';

  var css_tab_menu_variations = [
    'omnidb__tab-menu--',
    'omnidb__theme-bg--menu-'
  ];
  // Adding classes based on variations and hierarchy.
  v_div.classList.add(css_tab_menu_variations[0] + 'container');
  if (p_hierarchy !== undefined) {
    v_div.classList.add(css_tab_menu_variations[0] + 'container--' + p_hierarchy);
    v_div.classList.add(css_tab_menu_variations[0] + 'container--menu-shown');
    for (let i = 0; i < css_tab_menu_variations.length; i++) {
      v_tab_menu.classList.add(css_tab_menu_variations[i] + p_hierarchy);
    }
    v_div_tab_content_list.classList.add('omnidb__tab-content--' + p_hierarchy);
  }

  v_tab_menu.appendChild(v_nav);
  v_div.appendChild(v_tab_menu);
  v_div.appendChild(v_div_tab_content_list);

  if (p_layout === 'card') {
    v_div.classList.add('card');
    v_tab_menu.classList.add('card-header');
    v_tab_menu.classList.add('pb-0');
    v_div_tab_content_list.classList.add('card-body');
  }

	var v_tabControl = {
    // Params
		id: p_div,
		selectedTab: null,
		selectedDiv: null,
		selectedA: null,
    tabColor: null,
    tabCounter : 0,
		tabListContentDiv: v_div_tab_content_list,
		tabList: [],
		tabListDiv: v_div_tab_list,
    tabMenu: v_tab_menu,
    tabCssVariation: css_tab_menu_variations[0],
		tag: new Object(),
    isToggleable: (p_hierarchy === 'primary'),
    // Actions
    disableTabIndex : function(p_index) {
      this.tabList[p_index].elementA.classList.add('disabled');
		},
    enableTabIndex : function(p_index) {
      this.tabList[p_index].elementA.classList.remove('disabled');
		},
    disableSelectableTabIndex : function(p_index) {
      this.tabList[p_index].selectable = false;
		},
    enableSelectableTabIndex : function(p_index) {
      this.tabList[p_index].selectable = true;
		},
		selectTab : function(p_tab) {

			if (this.selectedTab!=p_tab) {
				if (p_tab.selectable) {

					if (this.selectedTab!=null)
						this.selectedTab.selected = false;

					p_tab.selected = true;

					this.selectedTab = p_tab;

					if (this.selectedDiv!=null) {
						this.selectedDiv.classList.remove('active');
						this.selectedA.classList.remove('active');
					}

					p_tab.elementA.classList.add('active');
					p_tab.elementDiv.classList.add('active');

					this.selectedA = p_tab.elementA;
					this.selectedDiv = p_tab.elementDiv;

					if(p_tab.selectFunction != null) {
						p_tab.selectFunction();
					}
				}
			}
		},
    selectTabIndex : function(p_index) {

			if (this.tabList[p_index].selectable) {

				if (this.selectedTab!=null)
					this.selectedTab.selected = false;

				this.tabList[p_index].selected = true;

				this.selectedTab = this.tabList[p_index];

        //$(this.tabList[p_index].elementA).tab('show');

				if (this.selectedDiv!=null) {
          this.selectedDiv.classList.remove('active');
          this.selectedA.classList.remove('active');
				}

				this.tabList[p_index].elementA.classList.add('active');
				this.tabList[p_index].elementDiv.classList.add('active');

				this.selectedA = this.tabList[p_index].elementA;
				this.selectedDiv = this.tabList[p_index].elementDiv;

				if(this.tabList[p_index].selectFunction != null) {
					this.tabList[p_index].selectFunction();
				}
			}

		},
    disableTab : function(p_tab) {
      p_tab.elementA.classList.add('disabled');
		},
    enableTab : function(p_tab) {
      p_tab.elementA.classList.remove('disabled');
		},
    disableSelectableTab : function(p_tab) {
      p_tab.selectable = false;
		},
    enableSelectableTab : function(p_tab) {
      p_tab.selectable = true;
		},
		disableClose : function(p_tab) {
			if (p_tab.elementClose!=null) {
				p_tab.elementClose.style.display = 'none';
			}
		},
		enableClose : function(p_tab) {
			if (p_tab.elementClose!=null) {
				p_tab.elementClose.style.display = '';
			}
		},
		removeTabIndex : function(p_index) {
			var v_tab = this.tabList[p_index];

      if (v_tab.closeFunction!=null) {
        v_tab.closeFunction(null,v_tab);
      }
      else if (v_tab) {
        this.removeTab(v_tab);
      }
		},
    removeLastTab : function() {
      var v_this = this
      var v_tab_index = v_this.tabList.length-1;

      this.removeTabIndex(v_tab_index);
		},
		removeTab : function(p_tab) {

			var v_tab = p_tab;

			v_tab.elementDiv.parentNode.removeChild(v_tab.elementDiv);
			v_tab.elementA.parentNode.removeChild(v_tab.elementA);

			var v_index = this.tabList.indexOf(p_tab);

      var v_current_index = this.tabList.indexOf(this.selectedTab);

      if (v_index == v_current_index) {
  			if (v_index > 0)
  				this.selectTabIndex(v_index-1);
  			else if (this.tabList[v_index+1]!=null)
  				this.selectTabIndex(v_index+1);
      }

			this.tabList.splice(this.tabList.indexOf(p_tab), 1);

      // When there are not outer tabs left to select, need to search and select or create a welcome tab.
      if (  (this === v_connTabControl) &&// Checking if the removed tab belongs to the outer menu.
            (v_connTabControl.tabList.indexOf(v_connTabControl.selectedTab) === -1)// Checking if there are no other tabs to select.
      ) {
        // Looking for a welcome tab
        var v_welcome_tab_index = false;
        for (let i = 0; i < v_connTabControl.tabList.length; i++) {
          if (v_connTabControl.tabList[i].tag) {
            if (v_connTabControl.tabList[i].tag.mode === 'welcome') {
              v_welcome_tab_index = i;
            }
          }
        }

        if (v_welcome_tab_index) {
          this.selectTabIndex(v_welcome_tab_index);
        }
        // Not forcing user to have a welcome tab.
        // else {
        //   v_connTabControl.tag.createWelcomeTab();
        // }
      }

		},
		renameTab : function(p_tab,p_name) {

      var v_tab_title_span = $(p_tab.elementA).find('.omnidb__tab-menu__link-name');
      if (v_tab_title_span) {
        v_tab_title_span.html(p_name);
      }

			p_tab.text = p_name;

		},
    dragEndFunction : function(e,p_tab) {
      let el = e.target;
      let el_pos = el.getBoundingClientRect();
      let el_index = $(el).index();

      let drop_pos_x = e.x;
      let drop_pos_y = e.y;

      let old_index = el_index;
      let new_index;

      let siblings = $(el).siblings();
      let total = siblings.length;
      for (let i = 0; i < total; i++) {
        let sibling = siblings[i];
        let sibling_pos = sibling.getBoundingClientRect();
        let sibling_pos_x = sibling_pos.x;
        let sibling_pos_x_center = sibling_pos_x + (sibling_pos.width / 2);
        let sibling_pos_x_end = sibling_pos_x + sibling_pos.width;
        let sibling_pos_y = sibling_pos.y;
        let sibling_pos_y_end = sibling_pos.y + sibling_pos.height;
        if (sibling_pos_y < drop_pos_y && drop_pos_y < sibling_pos_y_end && sibling_pos_x < drop_pos_x && drop_pos_x < sibling_pos_x_end) {
          var removedEl = p_tab.tabList.splice(old_index, 1)[0];
          if (drop_pos_x < sibling_pos_x_center) {
            new_index = i;
            p_tab.tabList.splice(new_index, 0, removedEl);
            sibling.before(el);
          }
          else {
            new_index = i + 1;
            p_tab.tabList.splice(new_index, 0, removedEl);
            sibling.after(el);
          }
        }
      }
    },
    hideTabMenu : function() {
      document.getElementById(p_div).classList.remove(this.tabCssVariation + 'container--menu-shown');
      this.tabMenu.classList.remove(this.tabCssVariation + 'shown');
    },
    showTabMenu : function() {
      document.getElementById(p_div).classList.add(this.tabCssVariation + 'container--menu-shown');
      this.tabMenu.classList.add(this.tabCssVariation + 'shown');
    },
    toggleTabMenu : function(e) {
      var v_this = this;
      $('#'+p_div).toggleClass(this.tabCssVariation + 'container--menu-shown');
      $(v_this.tabMenu).toggleClass(v_this.tabCssVariation + 'shown');
      // if (this.tabMenu === e.target)
      //   this.showTabMenu();
      // else
      //   this.hideTabMenu();
    },

		/**
      * ## createTab
      * @desc Creates a generic tab object with optional parameters and callbacks.
      * Ex: p_mode === 'customer_dashboard' expects data based on columns from customer tables, and will return all data necessary to kickoff a customer dashboard.
      *
      * @param  {function} p_clickFunction Callback for onclick.
      * @param  {boolean} p_close Defines if the elementA has a closing icon.
      * @param  {function} p_dblClickFunction  Callback for ondoubleclick.
      * @param  {boolean} p_disabled  Defines if the elementA is disabled.
      * @param  {string} p_icon HTML string is accepted as an optional icon.
      * @param  {boolean} p_isDraggable Defines if the elementA is draggable inside the tab-menu.
      * @param  {string} p_name HTML string is accepted as an optional name for the elementA.
      * @param  {function} p_rightClickFunction Callback for oncontextmenu.
      * @param  {function} p_selectFunction  Callback for after the tab-content is rendered.
      * @param  {boolean} p_selectable  Defines if the the tab-content is controlled by default bootstrap tab system selection. Used together with p_clickFunction to override the selecting tab behaviour, like the snippets panel.
      * @param  {string} p_tooltip_name  HTML string is accepted as an optional tooltip. This is bootstrap's default tooltip.
      * @param  {string} p_omnidb_tooltip_name  HTML string is accepted as an optional tooltip. This is OmniDB custom tooltip, used in the outer menu to avoid overflow bugs from bootstrap.
      * @return {oject} Creates the tab object in this tabControl.
		 */
		createTab : function({
      p_clickFunction = null,
      p_close = true,
      p_closeFunction = null,
      p_dblClickFunction = null,
      p_disabled = false,
      p_icon = false,
      p_isDraggable = true,
      p_name = '',
      p_rightClickFunction = false,
      p_selectFunction = null,
      p_selectable = true,
      p_tooltip_name = false,
      p_omnidb_tooltip_name = false
    }) {
			var v_control = this;
			var v_index = this.tabCounter;

			this.tabCounter++;

			var v_tab = {
				id : p_div + '_tab' + v_index + '_' + Date.now(),
				seq: v_index,
				text: p_name,
				selected : false,
				elementA : null,
				elementDiv : null,
				elementClose: null,
				tag: null,
				clickFunction: p_clickFunction,
				dblClickFunction : p_dblClickFunction,
				closeFunction: p_closeFunction,
				selectFunction: p_selectFunction,
				selectable: p_selectable,
        disabled: p_disabled,
				removeTab: function() { v_control.removeTab(this); },
				renameTab: function(p_name) { v_control.renameTab(this,p_name); },
				disableClose: function() { v_control.disableClose(this) },
				enableClose: function() { v_control.enableClose(this) },
        dragEndFunction: function(e,p_tab,p_index) { v_control.dragEndFunction(e,p_tab) },
				isDraggable: p_isDraggable
			};

      // Sets tabMenu toggle action based on page interaction
      // if (this.isToggleable && v_index === 1) {
      //   document.body.addEventListener("click", v_control.toggleTabMenu.bind(v_control));
      // }

			var v_a = document.createElement('a');
      v_a.setAttribute('id','a_' + v_tab.id);
      v_a.setAttribute('data-toggle','tab');
      v_a.setAttribute('role','tab');
      v_a.setAttribute('aria-selected','false');
      v_a.setAttribute('aria-selected','false');
      v_a.setAttribute('href','#' + 'div_' + v_tab.id);
      v_a.setAttribute('aria-controls','div_' + v_tab.id);

      if (v_tab.isDraggable) {
        v_a.setAttribute('draggable','true');
        v_a.ondragend = function(e) {
          e.stopPropagation();
          e.preventDefault();
          v_tab.dragEndFunction(e,this);
  			}.bind(this);
      }

      if (p_disabled) {
        v_a.className = 'omnidb__tab-menu__link nav-item nav-link disabled';
      }
      else {
        v_a.className = 'omnidb__tab-menu__link nav-item nav-link';
      }

      var v_close = document.createElement('i');
      v_close.className = 'fas fa-times tab-icon icon-close omnidb__tab-menu__link-close';

			v_tab.elementClose = v_close;

			v_close.onclick = function(e) {
        e.stopPropagation();
        e.preventDefault();
        if (v_tab.closeFunction!=null) {
          v_tab.closeFunction(e,v_tab);
        }
			};

      if (p_rightClickFunction) {
        v_a.oncontextmenu = function(e) {
          e.stopPropagation();
          e.preventDefault();
          p_rightClickFunction(e);
        }
      }

      var v_icon = (p_icon !== false) ? '<span class="omnidb__menu__btn omnidb__tab-menu__link-icon">' + p_icon + '</span>' : '';
      var v_name = (p_name !== undefined && p_name !== null && p_name !== '') ? p_name : '';

      if (p_tooltip_name) {
        getAttributesTooltip(v_a, p_tooltip_name, null, 'right');
      }
      else if (p_omnidb_tooltip_name) {
        getAttributesOmniDBTooltip(v_a, p_omnidb_tooltip_name, null, 'right');
      }
			v_a.innerHTML = '<span class="omnidb__tab-menu__link-content">' +
                        v_icon +
                        '<span class="omnidb__tab-menu__link-name">' + v_name + '<span>' +
                      '<span>';
      if (p_close) {
  			v_a.appendChild(v_close);
      }

			v_a.ondblclick = function(e) {

				if (v_tab.dblClickFunction!=null)
					v_tab.dblClickFunction(v_tab);
			};

			var v_div = document.createElement('div');
      v_div.className = 'tab-pane';
      v_div.setAttribute('id','div_' + v_tab.id);
      v_div.setAttribute('role','tabpanel');
      v_div.setAttribute('aria-labelledby','a_' + v_tab.id);


			v_tab.elementA = v_a;
			v_tab.elementDiv = v_div;

			v_a.onclick = function(e) {

        e.stopPropagation();
        e.preventDefault();
        if (v_tab.selectable) {
          v_control.selectTab(v_tab);
        }
				if (v_tab.clickFunction!=null) {
          v_tab.clickFunction(e);
        }
        // Hiding the tooltip on click if the has tooltips.
        if (p_tooltip_name) {
          $(v_a).tooltip('hide');
        }
			};

			this.tabListDiv.appendChild(v_a);

			this.tabListContentDiv.appendChild(v_div);

			this.tabList.push(v_tab);

			return v_tab;
		},
	};

	return v_tabControl;

}
## createTabControl
@desc Creates the `tabControl` with methods to manipulate the tab-system.

@param  {string} { p_div   String for the ID of the target DOM element where the tabControl will create/remove tabs.
@param  {type} p_hierarchy Optional string defining the tab-system classes with OmniDB pre-defined styles ['primary', 'secondary',...].
The classes system refers to BEM practices implemented inside OmniDB: 'omnidb__tab-menu' (default base class), 'omnidb__tab-menu--primary', 'omnidb__tab-menu--secondary'.
- 'primary': OmniDB Outer Tabs. Results in the tab classlists containing 'omnidb__tab-menu omnidb__tab-menu--primary', 'omnidb__tab-content omnidb__tab-content--primary'
- 'secondary': OmniDB Inner Tabs. Results in the tab classlists containing 'omnidb__tab-menu omnidb__tab-menu--secondary', 'omnidb__tab-content omnidb__tab-content--secondary'
@param  {type} p_layout}   String defining some additional ready-to-use styles for the tab system.
- 'card': Adds a bootstrap based card style to the tab system, with the tab-menu inside the card-header and the tab-content inside the card-body.
@return {type} Returns the `tabControl` object.