/* global tinymce, SimpleMDE, modJs, SignaturePad, modulesInstalled */
/* eslint-disable prefer-destructuring */
/*
   Copyright (c) 2018 [Glacies UG, Berlin, Germany] (http://glacies.de)
 Developer: Thilina Hasantha (http://lk.linkedin.com/in/thilinah | https://github.com/thilinah)
 */
import FormValidation from './FormValidation';
/**
 * The base class for providing core functions to all module classes.
 * @class Base.js
 */

class ModuleBase {
  constructor() {
    this.deleteParams = {};
    this.createRemoteTable = false;
    this.instanceId = 'None';
    this.ga = [];
    this.showEdit = true;
    this.showDelete = true;
    this.showSave = true;
    this.showCancel = true;
    this.showFormOnPopup = false;
    this.filtersAlreadySet = false;
    this.currentFilterString = '';
    this.sorting = 0;
    this.settings = {};
    this.translations = {};
    this.customFields = [];
    this.csrfRequired = false;

    this.fieldTemplates = null;
    this.templates = null;
    this.customTemplates = null;
    this.emailTemplates = null;
    this.fieldMasterData = null;
    this.fieldMasterDataKeys = null;
    this.fieldMasterDataCallback = null;
    this.sourceMapping = null;
    this.currentId = null;
    this.currentElement = null;
    this.user = null;
    this.currentProfile = null;
    this.permissions = {};
    this.baseUrl = null;
  }

  // eslint-disable-next-line no-unused-vars
  init(appName, currentView, dataUrl, permissions) {

  }

  /**
     * Some browsers do not support sending JSON in get parameters. Set this to true to avoid sending JSON
     * @method setNoJSONRequests
     * @param val {Boolean}
     */
  setNoJSONRequests(val) {
    this.noJSONRequests = val;
  }


  setPermissions(permissions) {
    this.permissions = permissions;
  }

  sortingStarted(val) {
    this.sorting = val;
  }

  /**
     * Check if the current user has a permission
     * @method checkPermission
     * @param permission {String}
     * @example
     * this.checkPermission("Upload/Delete Profile Image")
     */
  checkPermission(permission) {
    if (this.permissions[permission] === undefined || this.permissions[permission] == null || this.permissions[permission] === 'Yes') {
      return 'Yes';
    }
    return this.permissions[permission];
  }

  setBaseUrl(url) {
    this.baseUrl = url;
  }

  setUser(user) {
    this.user = user;
  }

  getUser() {
    return this.user;
  }

  setInstanceId(id) {
    this.instanceId = id;
  }

  setCSRFRequired(val) {
    this.csrfRequired = val;
  }

  scrollToTop() {
    $('html, body').animate({ scrollTop: 0 }, 'fast');
  }

  scrollToBottom() {
    $('html, body').animate({ scrollTop: $(document).height() }, 'slow');
  }

  scrollToElement(element) {
    if ($(window).height() <= element.offset().top) {
      $('html, body').animate({ scrollTop: element.offset().top }, 'slow');
    }
  }

  scrollToElementBottom(element) {
    if ($(window).height() <= element.offset().top + element.height()) {
      $('html, body').animate({ scrollTop: element.offset().top + element.height() }, 'slow');
    }
  }


  setTranslations(txt) {
    this.translations = txt.messages[''];
  }

  setTranslationsSubModules(translations) {
    this.translations = translations;
  }

  gt(key) {
    if (this.translations[key] === undefined || this.translations[key] === null) {
      return key;
    }
    return this.translations[key][0];
  }

  addToLangTerms(key) {
    let termsArr;
    const terms = localStorage.getItem('terms');
    if (terms === undefined) {
      termsArr = {};
    } else {
      try {
        termsArr = JSON.parse(terms);
      } catch (e) {
        termsArr = {};
      }
    }

    if (this.translations[key] === undefined) {
      termsArr[key] = key;
      localStorage.setItem('terms', JSON.stringify(termsArr));
    }
  }

  /**
     * If this method returned false the action buttons in data table for modules will not be displayed.
     * Override this method in module lib.js to hide action buttons
     * @method showActionButtons
     * @param permission {String}
     * @example
     * EmployeeLeaveEntitlementAdapter.method('showActionButtons() {
     *  return false;
     * }
     */
  showActionButtons() {
    return true;
  }

  trackEvent(action, label, value) {
    try {
      if (label === undefined || label == null) {
        this.ga.push(['_trackEvent', this.instanceId, action]);
      } else if (value === undefined || value == null) {
        this.ga.push(['_trackEvent', this.instanceId, action, label]);
      } else {
        this.ga.push(['_trackEvent', this.instanceId, action, label, value]);
      }
    } catch (e) {
      // Do nothing
    }
  }


  setCurrentProfile(currentProfile) {
    this.currentProfile = currentProfile;
  }

  /**
     * Get the current profile
     * @method getCurrentProfile
     * @returns Profile of the current user if the profile is not switched if not switched profile
     */

  getCurrentProfile() {
    return this.currentProfile;
  }

  /**
     * Retrive data required to create select boxes for add new /edit forms for a given module. This is called when loading the module
     * @method initFieldMasterData
     * @param callback {Function} call this once loading completed
     * @param callback {Function} call this once all field loading completed. This indicate that the form can be displayed saftly
     * @example
     *   ReportAdapter.method('renderForm(object) {
     *    var that = this;
     *    this.processFormFieldsWithObject(object);
     *    var cb = function(){
     *      that.super.renderForm(object);
     *    };
     *    this.initFieldMasterData(cb);
     *  }
     */
  initFieldMasterData(callback, loadAllCallback, loadAllCallbackData) {
    let values;
    if (this.showAddNew === undefined || this.showAddNew == null) {
      this.showAddNew = true;
    }
    this.fieldMasterData = {};
    this.fieldMasterDataKeys = {};
    this.fieldMasterDataCallback = loadAllCallback;
    this.fieldMasterDataCallbackData = loadAllCallbackData;
    this.sourceMapping = {};
    const fields = this.getFormFields();
    const filterFields = this.getFilters();

    if (filterFields != null) {
      for (let j = 0; j < filterFields.length; j++) {
        values = this.getMetaFieldValues(filterFields[j][0], fields);
        if (values == null || (values.type !== 'select' && values.type !== 'select2' && values.type !== 'select2multi')) {
          fields.push(filterFields[j]);
        }
      }
    }


    const remoteSourceFields = [];
    const remoteSourceFieldKeys = [];
    let field = null;
    let fieldSub = null;
    for (let i = 0; i < fields.length; i++) {
      field = fields[i];
      if (field[1]['remote-source'] !== undefined && field[1]['remote-source'] !== null) {
        const key = `${field[1]['remote-source'][0]}_${field[1]['remote-source'][1]}_${field[1]['remote-source'][2]}`;
        // if(remoteSourceFieldKeys.indexOf(key) < 0){
        remoteSourceFields.push(field);
        remoteSourceFieldKeys.push(key);
        // }
      } else if (field[1].form !== undefined && field[1].form !== null) {
        for (let j = 0; j < field[1].form.length; j++) {
          fieldSub = field[1].form[j];
          if (fieldSub[1]['remote-source'] !== undefined && fieldSub[1]['remote-source'] !== null) {
            const key = `${fieldSub[1]['remote-source'][0]}_${fieldSub[1]['remote-source'][1]}_${fieldSub[1]['remote-source'][2]}`;
            if (remoteSourceFieldKeys.indexOf(key) < 0) {
              remoteSourceFields.push(fieldSub);
              remoteSourceFieldKeys.push(key);
            }
          }
        }
      }
    }

    for (let i = 0; i < remoteSourceFields.length; i++) {
      const fieldRemote = remoteSourceFields[i];
      if (fieldRemote[1]['remote-source'] !== undefined && fieldRemote[1]['remote-source'] != null) {
        const key = `${fieldRemote[1]['remote-source'][0]}_${fieldRemote[1]['remote-source'][1]}_${fieldRemote[1]['remote-source'][2]}`;
        this.fieldMasterDataKeys[key] = false;
        this.sourceMapping[fieldRemote[0]] = fieldRemote[1]['remote-source'];

        const callBackData = {};
        callBackData.callBack = 'initFieldMasterDataResponse';
        callBackData.callBackData = [key];
        if (callback !== null && callback !== undefined) {
          callBackData.callBackSuccess = callback;
        }
        this.getFieldValues(fieldRemote[1]['remote-source'], callBackData);
      }
    }
  }

  /**
     * Pass true to this method after creating module JS object to open new/edit entry form for the module on a popup.
     * @method setShowFormOnPopup
     * @param val {Boolean}
     * @example
     *   modJs.subModJsList['tabCandidateApplication'] = new CandidateApplicationAdapter('Application','CandidateApplication',{"candidate":data.id}
     *  modJs.subModJsList['tabCandidateApplication'].setShowFormOnPopup(true);
     */

  setShowFormOnPopup(val) {
    this.showFormOnPopup = val;
  }

  /**
     * Set this to true to if you need the datatable to load data page by page instead of loading all data at once.
     * @method setRemoteTable
     * @param val {Boolean}
     * @example
     *   modJs.subModJsList['tabCandidateApplication'] = new CandidateApplicationAdapter('Application','CandidateApplication',{"candidate":data.id}
     *  modJs.subModJsList['tabCandidateApplication'].setRemoteTable(true);
     */

  setRemoteTable(val) {
    this.createRemoteTable = val;
  }

  setSettings(val) {
    this.settings = val;
  }

  getRemoteTable() {
    return this.createRemoteTable;
  }

  isAllLoaded(fieldMasterDataKeys) {
    for (const key in fieldMasterDataKeys) {
      if (fieldMasterDataKeys[key] === false) {
        return false;
      }
    }
    return true;
  }

  // eslint-disable-next-line no-unused-vars
  initFieldMasterDataResponse(key, data, callback, loadAllCallbackData) {
    this.fieldMasterData[key] = data;
    this.fieldMasterDataKeys[key] = true;

    if (callback !== undefined && callback !== null) {
      callback();
    }

    if (this.fieldMasterDataCallback !== null
            && this.fieldMasterDataCallback !== undefined
            && this.isAllLoaded(this.fieldMasterDataKeys)
            && (this.fieldMasterDataCallbackData !== null && this.fieldMasterDataCallbackData !== undefined)
    ) {
      this.fieldMasterDataCallback(this.fieldMasterDataCallbackData);
    } else if (this.fieldMasterDataCallback !== null
            && this.fieldMasterDataCallback !== undefined
            && this.isAllLoaded(this.fieldMasterDataKeys)
    ) {
      this.fieldMasterDataCallback();
    }
  }

  getMetaFieldValues(key, fields) {
    for (let i = 0; i < fields.length; i++) {
      if (key === fields[i][0]) {
        return fields[i][1];
      }
    }
    return null;
  }

  getThemeColors() {
    const colors = ['red', 'yellow', 'aqua', 'blue',
      'light-blue', 'green', 'navy', 'teal', 'olive', 'orange',
      'fuchsia', 'purple'];

    return colors;
  }

  getColorByRandomString(string) {
    const colors = this.getThemeColors();
    const k = string.charCodeAt(0);
    return colors[k % colors.length];
  }

  getColorByFileType(type) {
    type = type.toLowerCase();

    const colorMap = {};
    colorMap.pdf = 'red';
    colorMap.csv = 'yellow';
    colorMap.xls = 'green';
    colorMap.xlsx = 'green';
    colorMap.doc = 'light-blue';
    colorMap.docx = 'light-blue';
    colorMap.docx = 'blue';
    colorMap.ppt = 'orange';
    colorMap.pptx = 'orange';
    colorMap.jpg = 'teal';
    colorMap.jpeg = 'teal';
    colorMap.gif = 'green';
    colorMap.png = 'yellow';
    colorMap.bmp = 'fuchsia';


    if (colorMap[type] !== undefined || colorMap[type] != null) {
      return colorMap[type];
    }
    return this.getColorByRandomString(type);
  }

  getIconByFileType(type) {
    type = type.toLowerCase();

    const iconMap = {};
    iconMap.pdf = 'fa fa-file-pdf-o';
    iconMap.csv = 'fa fa fa-file-code-o';
    iconMap.xls = 'fa fa-file-excel-o';
    iconMap.xlsx = 'fa fa-file-excel-o';
    iconMap.doc = 'fa fa-file-word-o';
    iconMap.docx = 'fa fa-file-word-o';
    iconMap.ppt = 'fa fa-file-powerpoint-o';
    iconMap.pptx = 'fa fa-file-powerpoint-o';
    iconMap.jpg = 'fa fa-file-image-o';
    iconMap.jpeg = 'fa fa-file-image-o';
    iconMap.gif = 'fa fa-file-image-o';
    iconMap.png = 'fa fa-file-image-o';
    iconMap.bmp = 'fa fa-file-image-o';
    iconMap.txt = 'fa fa-file-text-o';
    iconMap.rtf = 'fa fa-file-text-o';


    if (iconMap[type] !== undefined || iconMap[type] != null) {
      return iconMap[type];
    }
    return 'fa fa-file-o';
  }

  getSourceMapping() {
    return this.sourceMapping;
  }

  setTesting(testing) {
    this.testing = testing;
  }

  consoleLog(message) {
    if (this.testing) {
      console.log(message);
    }
  }

  setClientMessages(msgList) {
    this.msgList = msgList;
  }

  setTemplates(templates) {
    this.templates = templates;
  }


  getWSProperty(array, key) {
    if (array.hasOwnProperty(key)) {
      return array[key];
    }
    return null;
  }


  getClientMessage(key) {
    return this.getWSProperty(this.msgList, key);
  }


  getTemplate(key) {
    return this.getWSProperty(this.templates, key);
  }

  setGoogleAnalytics(gaq) {
    this.gaq = gaq;
  }


  showView(view) {
    if (this.currentView != null) {
      this.previousView = this.currentView;
      $(`#${this.currentView}`).hide();
    }
    $(`#${view}`).show();
    this.currentView = view;
    this.moveToTop();
  }

  showPreviousView() {
    this.showView(this.previousView);
  }


  moveToTop() {

  }


  callFunction(callback, cbParams, thisParam) {
    if ($.isFunction(callback)) {
      try {
        if (thisParam === undefined || thisParam === null) {
          callback.apply(document, cbParams);
        } else {
          callback.apply(thisParam, cbParams);
        }
      } catch (e) {
        console.log(e.message);
      }
    } else {
      const f = this[callback];
      if ($.isFunction(f)) {
        try {
          f.apply(this, cbParams);
        } catch (e) {
          console.log(e.message);
        }
      }
    }
  }

  getTableTopButtonHtml() {
    let html = '';
    if (this.getShowAddNew()) {
      html = `<button onclick="modJs.renderForm();return false;" class="btn btn-small btn-primary">${this.gt(this.getAddNewLabel())} <i class="fa fa-plus"></i></button>`;
    }

    if (this.getFilters() != null) {
      if (html !== '') {
        html += '&nbsp;&nbsp;';
      }
      html += `<button onclick="modJs.showFilters();return false;" class="btn btn-small btn-primary">${this.gt('Filter')} <i class="fa fa-filter"></i></button>`;
      html += '&nbsp;&nbsp;';
      if (this.filtersAlreadySet) {
        html += '<button id="__id___resetFilters" onclick="modJs.resetFilters();return false;" class="btn btn-small btn-default">__filterString__ <i class="fa fa-times"></i></button>';
      } else {
        html += '<button id="__id___resetFilters" onclick="modJs.resetFilters();return false;" class="btn btn-small btn-default" style="display:none;">__filterString__ <i class="fa fa-times"></i></button>';
      }
    }

    html = html.replace(/__id__/g, this.getTableName());

    if (this.currentFilterString !== '' && this.currentFilterString != null) {
      html = html.replace(/__filterString__/g, this.currentFilterString);
    } else {
      html = html.replace(/__filterString__/g, 'Reset Filters');
    }

    if (html !== '') {
      html = `<div class="row"><div class="col-xs-12">${html}</div></div>`;
    }

    return html;
  }


  getActionButtonHeader() {
    return { sTitle: '', sClass: 'center' };
  }

  getTableHTMLTemplate() {
    return '<div class="box-body table-responsive"><table cellpadding="0" cellspacing="0" border="0" class="table table-bordered table-striped" id="grid"></table></div>';
  }

  isSortable() {
    return true;
  }

  /**
     * Create the data table on provided element id
     * @method createTable
     * @param val {Boolean}
     */

  createTable(elementId) {
    const that = this;

    if (this.getRemoteTable()) {
      this.createTableServer(elementId);
      return;
    }


    const headers = this.getHeaders();

    // add translations
    for (const index in headers) {
      headers[index].sTitle = this.gt(headers[index].sTitle);
    }

    const data = this.getTableData();

    if (this.showActionButtons()) {
      headers.push(this.getActionButtonHeader());
    }


    if (this.showActionButtons()) {
      for (let i = 0; i < data.length; i++) {
        data[i].push(this.getActionButtonsHtml(data[i][0], data[i]));
      }
    }

    let html = '';
    html = this.getTableTopButtonHtml() + this.getTableHTMLTemplate();
    /*
         if(this.getShowAddNew()){
         html = this.getTableTopButtonHtml()+'<div class="box-body table-responsive"><table cellpadding="0" cellspacing="0" border="0" class="table table-bordered table-striped" id="grid"></table></div>';
         }else{
         html = '<div class="box-body table-responsive"><table cellpadding="0" cellspacing="0" border="0" class="table table-bordered table-striped" id="grid"></table></div>';
         }
         */
    // Find current page
    const activePage = $(`#${elementId} .dataTables_paginate .active a`).html();
    let start = 0;
    if (activePage !== undefined && activePage != null) {
      start = parseInt(activePage, 10) * 15 - 15;
    }

    $(`#${elementId}`).html(html);

    const dataTableParams = {
      oLanguage: {
        sLengthMenu: '_MENU_ records per page',
      },
      aaData: data,
      aoColumns: headers,
      bSort: that.isSortable(),
      iDisplayLength: 15,
      iDisplayStart: start,
    };


    const customTableParams = this.getCustomTableParams();

    $.extend(dataTableParams, customTableParams);

    $(`#${elementId} #grid`).dataTable(dataTableParams);

    $('.dataTables_paginate ul').addClass('pagination');
    $('.dataTables_length').hide();
    $('.dataTables_filter input').addClass('form-control');
    $('.dataTables_filter input').attr('placeholder', 'Search');
    $('.dataTables_filter label').contents().filter(function () {
      return (this.nodeType === 3);
    }).remove();
    $('.tableActionButton').tooltip();
  }

  /**
     * Create a data table on provided element id which loads data page by page
     * @method createTableServer
     * @param val {Boolean}
     */

  createTableServer(elementId) {
    const that = this;
    const headers = this.getHeaders();

    headers.push({ sTitle: '', sClass: 'center' });

    // add translations
    for (const index in headers) {
      headers[index].sTitle = this.gt(headers[index].sTitle);
    }

    let html = '';
    html = this.getTableTopButtonHtml() + this.getTableHTMLTemplate();

    // Find current page
    const activePage = $(`#${elementId} .dataTables_paginate .active a`).html();
    let start = 0;
    if (activePage !== undefined && activePage != null) {
      start = parseInt(activePage, 10) * 15 - 15;
    }


    $(`#${elementId}`).html(html);

    const dataTableParams = {
      oLanguage: {
        sLengthMenu: '_MENU_ records per page',
      },
      bProcessing: true,
      bServerSide: true,
      sAjaxSource: that.getDataUrl(that.getDataMapping()),
      aoColumns: headers,
      bSort: that.isSortable(),
      parent: that,
      iDisplayLength: 15,
      iDisplayStart: start,
    };

    if (this.showActionButtons()) {
      dataTableParams.aoColumnDefs = [
        {
          fnRender: that.getActionButtons,
          aTargets: [that.getDataMapping().length],
        },
      ];
    }

    const customTableParams = this.getCustomTableParams();

    $.extend(dataTableParams, customTableParams);

    $(`#${elementId} #grid`).dataTable(dataTableParams);

    $('.dataTables_paginate ul').addClass('pagination');
    $('.dataTables_length').hide();
    $('.dataTables_filter input').addClass('form-control');
    $('.dataTables_filter input').attr('placeholder', 'Search');
    $('.dataTables_filter label').contents().filter(function () {
      return (this.nodeType === 3);
    }).remove();

    $('.tableActionButton').tooltip();
  }

  /**
     * This should be overridden in module lib.js classes to return module headers which are used to create the data table.
     * @method getHeaders
     * @example
     SettingAdapter.method('getHeaders() {
      return [
      { "sTitle": "ID" ,"bVisible":false},
      { "sTitle": "Name" },
      { "sTitle": "Value"},
      { "sTitle": "Details"}
    ];
    }
     */
  getHeaders() {

  }


  /**
     * This should be overridden in module lib.js classes to return module field values which are used to create the data table.
     * @method getDataMapping
     * @example
     SettingAdapter.method('getDataMapping() {
  return [
          "id",
          "name",
          "value",
          "description"
  ];
  }
     */

  getDataMapping() {

  }

  /**
     * This should be overridden in module lib.js classes to return module from fields which are used to create the add/edit form and also used for initializing select box values in form.
     * @method getFormFields
     * @example
     SettingAdapter.method('getFormFields() {
  return [
          [ "id", {"label":"ID","type":"hidden"}],
          [ "value", {"label":"Value","type":"text","validation":"none"}]
  ];
  }
     */
  getFormFields() {

  }

  getTableData() {

  }

  /**
     * This can be overridden in module lib.js classes inorder to show a filter form
     * @method getFilters
     * @example
     EmployeeAdapter.method('getFilters() {
    return [
            [ "job_title", {"label":"Job Title","type":"select2","allow-null":true,"null-label":"All Job Titles","remote-source":["JobTitle","id","name"]}],
            [ "department", {"label":"Department","type":"select2","allow-null":true,"null-label":"All Departments","remote-source":["CompanyStructure","id","title"]}],
            [ "supervisor", {"label":"Supervisor","type":"select2","allow-null":true,"null-label":"Anyone","remote-source":["Employee","id","first_name+last_name"]}]
    ];
  }
     */
  getFilters() {
    return null;
  }

  /**
     * Show the edit form for an item
     * @method edit
     * @param id {int} id of the item to edit
     */
  edit(id) {
    this.currentId = id;
    this.getElement(id, []);
  }

  copyRow(id) {
    this.getElement(id, [], true);
  }

  renderModel(id, header, body) {
    $(`#${id}ModelBody`).html('');

    if (body === undefined || body == null) {
      body = '';
    }

    $(`#${id}ModelLabel`).html(header);
    $(`#${id}ModelBody`).html(body);
  }


  renderYesNoModel(header, body, yesBtnName, noBtnName, callback, callbackParams) {
    const that = this;
    const modelId = '#yesnoModel';

    if (body === undefined || body == null) {
      body = '';
    }

    $(`${modelId}Label`).html(header);
    $(`${modelId}Body`).html(body);
    if (yesBtnName != null) {
      $(`${modelId}YesBtn`).html(yesBtnName);
    }
    if (noBtnName != null) {
      $(`${modelId}NoBtn`).html(noBtnName);
    }

    $(`${modelId}YesBtn`).off().on('click', () => {
      if (callback !== undefined && callback != null) {
        callback.apply(that, callbackParams);
        that.cancelYesno();
      }
    });

    $(modelId).modal({
      backdrop: 'static',
    });
  }

  renderModelFromDom(id, header, element) {
    $(`#${id}ModelBody`).html('');

    if (element === undefined || element == null) {
      element = $('<div></div>');
    }

    $(`#${id}ModelLabel`).html(header);
    $(`#${id}ModelBody`).html('');
    $(`#${id}ModelBody`).append(element);
  }

  /**
     * Delete an item
     * @method deleteRow
     * @param id {int} id of the item to edit
     */

  deleteRow(id) {
    this.deleteParams.id = id;
    this.renderModel('delete', 'Confirm Deletion', 'Are you sure you want to delete this item ?');
    $('#deleteModel').modal('show');
  }

  /**
     * Show a popup with message
     * @method showMessage
     * @param title {String} title of the message box
     * @param message {String} message
     * @param closeCallback {Function} this will be called once the dialog is closed (optional)
     * @param closeCallback {Function} data to pass to close callback (optional)
     * @param closeCallbackData
     * @param isPlain {Boolean} if true buttons are not shown (optional / default = true)
     * @example
     *   this.showMessage("Error Occured while Applying Leave", callBackData);
     */
  showMessage(title, message, closeCallback = null, closeCallbackData = null, isPlain = false) {
    const that = this;
    let modelId = '';
    if (isPlain) {
      modelId = '#plainMessageModel';
    } else {
      modelId = '#messageModel';
    }

    $(modelId).off();

    if (isPlain) {
      this.renderModel('plainMessage', title, message);
    } else {
      this.renderModel('message', title, message);
    }

    if (closeCallback !== null && closeCallback !== undefined) {
      $(modelId).modal({ show: true });
      $(modelId).on('hidden.bs.modal', () => {
        closeCallback.apply(that, closeCallbackData);
        $('.modal-backdrop').remove();
      });
    } else {
      $(modelId).modal({
        backdrop: 'static',
      });
    }
  }

  showDomElement(title, element, closeCallback, closeCallbackData, isPlain) {
    const that = this;
    let modelId = '';
    if (isPlain) {
      modelId = '#dataMessageModel';
    } else {
      modelId = '#messageModel';
    }

    $(modelId).unbind('hide');

    if (isPlain) {
      this.renderModelFromDom('dataMessage', title, element);
    } else {
      this.renderModelFromDom('message', title, element);
    }


    if (closeCallback !== null && closeCallback !== undefined) {
      $(modelId).modal({ show: true });
      $(modelId).on('hidden.bs.modal', () => {
        closeCallback.apply(that, closeCallbackData);
        $('.modal-backdrop').remove();
      });
    } else {
      $(modelId).modal({
        backdrop: 'static',
      });
    }
  }

  confirmDelete() {
    if (this.deleteParams.id !== undefined || this.deleteParams.id != null) {
      this.deleteObj(this.deleteParams.id, []);
    }
    $('#deleteModel').modal('hide');
  }

  cancelDelete() {
    $('#deleteModel').modal('hide');
    this.deleteParams.id = null;
  }

  closeMessage() {
    $('#messageModel').modal('hide');
  }

  cancelYesno() {
    $('#yesnoModel').modal('hide');
  }

  closePlainMessage() {
    $('#plainMessageModel').modal('hide');
    $('#dataMessageModel').modal('hide');
  }

  closeDataMessage() {
    $('#dataMessageModel').modal('hide');
  }


  /**
     * Create or edit an element
     * @method save
     * @param getFunctionCallBackData {Array} once a success is returned call get() function for this module with these parameters
     * @param successCallback {Function} this will get called after success response
     */

  save(callGetFunction, successCallback) {
    const validator = new FormValidation(`${this.getTableName()}_submit`, true, { ShowPopup: false, LabelErrorClass: 'error' });
    if (validator.checkValues()) {
      let params = validator.getFormParameters();
      params = this.forceInjectValuesBeforeSave(params);
      const msg = this.doCustomValidation(params);
      if (msg == null) {
        if (this.csrfRequired) {
          params.csrf = $(`#${this.getTableName()}Form`).data('csrf');
        }
        const id = $(`#${this.getTableName()}_submit #id`).val();
        if (id != null && id !== undefined && id !== '') {
          params.id = id;
        }
        params = this.makeEmptyDateFieldsNull(params);
        this.add(params, [], callGetFunction, successCallback);
      } else {
        $(`#${this.getTableName()}Form .label`).html(msg);
        $(`#${this.getTableName()}Form .label`).show();
        this.scrollToTop();
      }
    }
  }


  makeEmptyDateFieldsNull(params) {
    const fields = this.getFormFields();
    fields.forEach((field) => {
      if ((field[1].type === 'date' || field[1].type === 'datetime')
                && (params[field[0]] === '' || params[field[0]] === '0000-00-00' || params[field[0]] === '0000-00-00 00:00:00')) {
        if (field[1].validation === 'none') {
          params[field[0]] = 'NULL';
        } else {
          delete params[field[0]];
        }
      }
    });
    return params;
  }

  validatePassword(password) {
    if (password.length < 8) {
      return this.gt('Password too short');
    }

    if (password.length > 20) {
      return this.gt('Password too long');
    }

    const numberTester = /.*[0-9]+.*$/;
    if (!password.match(numberTester)) {
      return this.gt('Password must include at least one number');
    }

    const lowerTester = /.*[a-z]+.*$/;
    if (!password.match(lowerTester)) {
      return this.gt('Password must include at least one lowercase letter');
    }

    const upperTester = /.*[A-Z]+.*$/;
    if (!password.match(upperTester)) {
      return this.gt('Password must include at least one uppercase letter');
    }

    const symbolTester = /.*[\W]+.*$/;
    if (!password.match(symbolTester)) {
      return this.gt('Password must include at least one symbol');
    }

    return null;
  }

  /**
     * Override this method to inject attitional parameters or modify existing parameters retrived from
     * add/edit form before sending to the server
     * @method forceInjectValuesBeforeSave
     * @param params {Array} keys and values in form
     * @returns {Array} modified parameters
     */
  forceInjectValuesBeforeSave(params) {
    return params;
  }

  /**
     * Override this method to do custom validations at client side
     * @method doCustomValidation
     * @param params {Array} keys and values in form
     * @returns {Null or String} return null if validation success, returns error message if unsuccessful
     * @example
     EmployeeLeaveAdapter.method('doCustomValidation(params) {
    try{
      if(params['date_start'] != params['date_end']){
        var ds = new Date(params['date_start']);
        var de = new Date(params['date_end']);
        if(de < ds){
          return "Start date should be earlier than end date of the leave period";
        }
      }
    }catch(e){

    }
  return null;
}
     */
  // eslint-disable-next-line no-unused-vars
  doCustomValidation(params) {
    return null;
  }

  filterQuery() {
    const validator = new FormValidation(`${this.getTableName()}_filter`, true, { ShowPopup: false, LabelErrorClass: 'error' });
    if (validator.checkValues()) {
      const params = validator.getFormParameters();
      if (this.doCustomFilterValidation(params)) {
        // remove null params
        for (const prop in params) {
          if (params.hasOwnProperty(prop)) {
            if (params[prop] === 'NULL') {
              delete (params[prop]);
            }
          }
        }

        this.setFilter(params);
        this.filtersAlreadySet = true;
        $(`#${this.getTableName()}_resetFilters`).show();
        this.currentFilterString = this.getFilterString(params);

        this.get([]);
        this.closePlainMessage();
      }
    }
  }


  getFilterString(filters) {
    let str = '';
    let rmf; let source; let values; let select2MVal; let value; let
      valueOrig;

    const filterFields = this.getFilters();


    if (values == null) {
      values = [];
    }

    for (const prop in filters) {
      if (filters.hasOwnProperty(prop)) {
        values = this.getMetaFieldValues(prop, filterFields);
        value = '';
        valueOrig = null;

        if ((values.type === 'select' || values.type === 'select2')) {
          if (values['remote-source'] !== undefined && values['remote-source'] != null) {
            rmf = values['remote-source'];
            if (filters[prop] === 'NULL') {
              if (values['null-label'] !== undefined && values['null-label'] != null) {
                value = values['null-label'];
              } else {
                value = 'Not Selected';
              }
            } else {
              value = this.fieldMasterData[`${rmf[0]}_${rmf[1]}_${rmf[2]}`][filters[prop]];
              valueOrig = value;
            }
          } else {
            source = values.source[0];
            if (filters[prop] === 'NULL') {
              if (values['null-label'] !== undefined && values['null-label'] != null) {
                value = values['null-label'];
              } else {
                value = 'Not Selected';
              }
            } else {
              for (let i = 0; i < source.length; i++) {
                if (filters[prop] === values.source[i][0]) {
                  value = values.source[i][1];
                  valueOrig = value;
                  break;
                }
              }
            }
          }
        } else if (values.type === 'select2multi') {
          select2MVal = [];
          try {
            select2MVal = JSON.parse(filters[prop]);
          } catch (e) {
            // Do nothing
          }

          value = select2MVal.join(',');
          if (value !== '') {
            valueOrig = value;
          }
        } else {
          value = filters[prop];
          if (value !== '') {
            valueOrig = value;
          }
        }

        if (valueOrig != null) {
          if (str !== '') {
            str += ' | ';
          }

          str += `${values.label} = ${value}`;
        }
      }
    }

    return str;
  }

  /**
     * Override this method to do custom validations at client side for values selected in filters
     * @method doCustomFilterValidation
     * @param params {Array} keys and values in form
     * @returns {Null or String} return null if validation success, returns error message if unsuccessful
     */
  doCustomFilterValidation(params) {
    return true;
  }


  /**
     * Reset selected filters
     * @method resetFilters
     */

  resetFilters() {
    this.filter = this.origFilter;
    this.filtersAlreadySet = false;
    $(`#${this.getTableName()}_resetFilters`).hide();
    this.currentFilterString = '';
    this.get([]);
  }


  showFilters(object) {
    let formHtml = this.templates.filterTemplate;
    let html = '';
    const fields = this.getFilters();

    for (let i = 0; i < fields.length; i++) {
      const metaField = this.getMetaFieldForRendering(fields[i][0]);
      if (metaField === '' || metaField === undefined) {
        html += this.renderFormField(fields[i]);
      } else {
        const metaVal = object[metaField];
        if (metaVal !== '' && metaVal != null && metaVal !== undefined && metaVal.trim() !== '') {
          html += this.renderFormField(JSON.parse(metaVal));
        } else {
          html += this.renderFormField(fields[i]);
        }
      }
    }
    formHtml = formHtml.replace(/_id_/g, `${this.getTableName()}_filter`);
    formHtml = formHtml.replace(/_fields_/g, html);

    const randomFormId = this.generateRandom(14);
    const $tempDomObj = $('<div class="reviewBlock popupForm" data-content="Form"></div>');
    $tempDomObj.attr('id', randomFormId);

    $tempDomObj.html(formHtml);


    $tempDomObj.find('.datefield').datepicker({ viewMode: 2 });
    $tempDomObj.find('.timefield').datetimepicker({
      language: 'en',
      pickDate: false,
    });
    $tempDomObj.find('.datetimefield').datetimepicker({
      language: 'en',
    });

    $tempDomObj.find('.colorpick').colorpicker();
    tinymce.init({
      selector: `#${$tempDomObj.attr('id')} .tinymce`,
      height: '400',
    });

    $tempDomObj.find('.simplemde').each(function () {
      const simplemde = new SimpleMDE({ element: $(this)[0] });
      $(this).data('simplemde', simplemde);
      // simplemde.value($(this).val());
    });

    // $tempDomObj.find('.select2Field').select2();
    $tempDomObj.find('.select2Field').each(function () {
      $(this).select2().select2('val', $(this).find('option:eq(0)').val());
    });

    $tempDomObj.find('.select2Multi').each(function () {
      $(this).select2().on('change', function (e) {
        const parentRow = $(this).parents('.row');
        const height = parentRow.find('.select2-choices').height();
        parentRow.height(parseInt(height, 10));
      });
    });

    /*
         $tempDomObj.find('.signatureField').each(function() {
         $(this).data('signaturePad',new SignaturePad($(this)));
         });
         */

    // var tHtml = $tempDomObj.wrap('<div>').parent().html();
    this.showDomElement('Edit', $tempDomObj, null, null, true);
    $('.filterBtn').off();
    $('.filterBtn').on('click', (e) => {
      e.preventDefault();
      e.stopPropagation();
      try {
        modJs.filterQuery();
      } catch (err) {
        // Do Nothing
      }
      return false;
    });

    if (this.filter !== undefined && this.filter != null && this.filter !== '') {
      this.fillForm(this.filter, `#${this.getTableName()}_filter`, this.getFilters());
    }
  }


  /**
     * Override this method in your module class to make changes to data fo the form before showing the form
     * @method preRenderForm
     * @param object {Array} keys value list for populating form
     */

  preRenderForm(object) {

  }

  /**
     * Create the form
     * @method renderForm
     * @param object {Array} keys value list for populating form
     */

  renderForm(object) {
    const signatureIds = [];
    if (object == null || object === undefined) {
      this.currentId = null;
    }

    this.preRenderForm(object);

    let formHtml = this.templates.formTemplate;
    let html = '';
    const fields = this.getFormFields();

    for (let i = 0; i < fields.length; i++) {
      const metaField = this.getMetaFieldForRendering(fields[i][0]);
      if (metaField === '' || metaField === undefined) {
        html += this.renderFormField(fields[i]);
      } else {
        const metaVal = object[metaField];
        if (metaVal !== '' && metaVal != null && metaVal !== undefined && metaVal.trim() !== '') {
          html += this.renderFormField(JSON.parse(metaVal));
        } else {
          html += this.renderFormField(fields[i]);
        }
      }
    }
    formHtml = formHtml.replace(/_id_/g, `${this.getTableName()}_submit`);
    formHtml = formHtml.replace(/_fields_/g, html);


    let $tempDomObj;
    const randomFormId = this.generateRandom(14);
    if (!this.showFormOnPopup) {
      $tempDomObj = $(`#${this.getTableName()}Form`);
    } else {
      $tempDomObj = $('<div class="reviewBlock popupForm" data-content="Form"></div>');
      $tempDomObj.attr('id', randomFormId);
    }

    $tempDomObj.html(formHtml);


    $tempDomObj.find('.datefield').datepicker({ viewMode: 2 });
    $tempDomObj.find('.timefield').datetimepicker({
      language: 'en',
      pickDate: false,
    });
    $tempDomObj.find('.datetimefield').datetimepicker({
      language: 'en',
    });

    $tempDomObj.find('.colorpick').colorpicker();

    tinymce.init({
      selector: `#${$tempDomObj.attr('id')} .tinymce`,
      height: '400',
    });

    $tempDomObj.find('.simplemde').each(function () {
      const simplemde = new SimpleMDE({ element: $(this)[0] });
      $(this).data('simplemde', simplemde);
      // simplemde.value($(this).val());
    });

    const codeMirror = this.codeMirror;
    if (codeMirror) {
      $tempDomObj.find('.code').each(function () {
        const editor = codeMirror.fromTextArea($(this)[0], {
          lineNumbers: false,
          matchBrackets: true,
          continueComments: 'Enter',
          extraKeys: { 'Ctrl-Q': 'toggleComment' },
        });
        $(this).data('codemirror', editor);
      });
    }


    // $tempDomObj.find('.select2Field').select2();
    $tempDomObj.find('.select2Field').each(function () {
      $(this).select2().select2('val', $(this).find('option:eq(0)').val());
    });

    $tempDomObj.find('.select2Multi').each(function () {
      $(this).select2().on('change', function (e) {
        const parentRow = $(this).parents('.row');
        const height = parentRow.find('.select2-choices').height();
        parentRow.height(parseInt(height, 10));
      });
    });


    $tempDomObj.find('.signatureField').each(function () {
      // $(this).data('signaturePad',new SignaturePad($(this)));
      signatureIds.push($(this).attr('id'));
    });

    for (let i = 0; i < fields.length; i++) {
      if (fields[i][1].type === 'datagroup') {
        $tempDomObj.find(`#${fields[i][0]}`).data('field', fields[i]);
      }
    }

    if (this.showSave === false) {
      $tempDomObj.find('.saveBtn').remove();
    } else {
      $tempDomObj.find('.saveBtn').off();
      $tempDomObj.find('.saveBtn').data('modJs', this);
      $tempDomObj.find('.saveBtn').on('click', function () {
        if ($(this).data('modJs').saveSuccessItemCallback != null && $(this).data('modJs').saveSuccessItemCallback !== undefined) {
          $(this).data('modJs').save($(this).data('modJs').retriveItemsAfterSave(), $(this).data('modJs').saveSuccessItemCallback);
        } else {
          $(this).data('modJs').save();
        }

        return false;
      });
    }

    if (this.showCancel === false) {
      $tempDomObj.find('.cancelBtn').remove();
    } else {
      $tempDomObj.find('.cancelBtn').off();
      $tempDomObj.find('.cancelBtn').data('modJs', this);
      $tempDomObj.find('.cancelBtn').on('click', function () {
        $(this).data('modJs').cancel();
        return false;
      });
    }

    // Input mask
    $tempDomObj.find('[mask]').each(function () {
      $(this).inputmask($(this).attr('mask'));
    });

    $tempDomObj.find('[datemask]').each(function () {
      $(this).inputmask({
        mask: 'y-1-2',
        placeholder: 'YYYY-MM-DD',
        leapday: '-02-29',
        separator: '-',
        alias: 'yyyy/mm/dd',
      });
    });

    $tempDomObj.find('[datetimemask]').each(function () {
      $(this).inputmask('datetime', {
        mask: 'y-2-1 h:s:00',
        placeholder: 'YYYY-MM-DD hh:mm:ss',
        leapday: '-02-29',
        separator: '-',
        alias: 'yyyy/mm/dd',
      });
    });

    if (!this.showFormOnPopup) {
      $(`#${this.getTableName()}Form`).show();
      $(`#${this.getTableName()}`).hide();

      for (let i = 0; i < signatureIds.length; i++) {
        $(`#${signatureIds[i]}`)
          .data('signaturePad',
            new SignaturePad(document.getElementById(signatureIds[i])));
      }

      if (object !== undefined && object != null) {
        this.fillForm(object);
      } else {
        this.setDefaultValues();
      }

      this.scrollToTop();
    } else {
      // var tHtml = $tempDomObj.wrap('<div>').parent().html();
      // this.showMessage("Edit",tHtml,null,null,true);
      this.showMessage('Edit', '', null, null, true);

      $('#plainMessageModel .modal-body').html('');
      $('#plainMessageModel .modal-body').append($tempDomObj);


      for (let i = 0; i < signatureIds.length; i++) {
        $(`#${signatureIds[i]}`)
          .data('signaturePad',
            new SignaturePad(document.getElementById(signatureIds[i])));
      }

      if (object !== undefined && object != null) {
        this.fillForm(object, `#${randomFormId}`);
      } else {
        this.setDefaultValues(`#${randomFormId}`);
      }
    }

    this.postRenderForm(object, $tempDomObj);
  }

  setDefaultValues(formId, fields) {
    if (fields == null || fields === undefined) {
      fields = this.getFormFields();
    }

    if (formId == null || formId === undefined || formId === '') {
      formId = `#${this.getTableName()}Form`;
    }


    for (let i = 0; i < fields.length; i++) {
      if (fields[i][1].type !== 'text' && fields[i][1].type !== 'textarea') {
        continue;
      }

      if (fields[i][1].default !== undefined && fields[i][1].default !== null) {
        $(`${formId} #${fields[i][0]}`).val(fields[i][1].default);
      }
    }
  }

  retriveItemsAfterSave() {
    return true;
  }

  /**
     * Override this method in your module class to make changes to data fo the form after showing it
     * @method postRenderForm
     * @param object {Array} keys value list for populating form
     * @param $tempDomObj {DOM} a DOM element for the form
     * @example
     *   UserAdapter.method('postRenderForm(object, $tempDomObj) {
    if(object == null || object == undefined){
      $tempDomObj.find("#changePasswordBtn").remove();
    }
  }
     */

  postRenderForm(object, $tempDomObj) {

  }

  /**
     * Convert data group field to HTML
     * @method dataGroupToHtml
     * @param val {String} value in the field
     * @param field {Array} field meta data
     */

  dataGroupToHtml(val, field) {
    const data = JSON.parse(val);


    let t; let sortFunction; let item; let itemHtml; let itemVal;

    const deleteButton = '<a id="#_id_#_delete" onclick="modJs.deleteDataGroupItem(\'#_id_#\');return false;" type="button" style="float:right;margin-right:3px;" tooltip="Delete"><li class="fa fa-times"></li></a>';
    const editButton = '<a id="#_id_#_edit" onclick="modJs.editDataGroupItem(\'#_id_#\');return false;" type="button" style="float:right;margin-right:5px;" tooltip="Edit"><li class="fa fa-edit"></li></a>';

    const template = field[1].html;

    if (data != null && data !== undefined && field[1]['sort-function'] !== undefined && field[1]['sort-function'] != null) {
      data.sort(field[1]['sort-function']);
    }


    const html = $(`<div id="${field[0]}_div_inner"></div>`);


    for (let i = 0; i < data.length; i++) {
      item = data[i];

      if (field[1]['pre-format-function'] !== undefined && field[1]['pre-format-function'] != null) {
        item = field[1]['pre-format-function'].apply(this, [item]);
      }

      t = template;
      t = t.replace('#_delete_#', deleteButton);
      t = t.replace('#_edit_#', editButton);
      t = t.replace(/#_id_#/g, item.id);

      for (const key in item) {
        itemVal = item[key];
        if (itemVal !== undefined && itemVal != null && typeof itemVal === 'string') {
          itemVal = itemVal.replace(/(?:\r\n|\r|\n)/g, '<br />');
        }
        t = t.replace(`#_${key}_#`, itemVal);
      }

      if (field[1].render !== undefined && field[1].render != null) {
        t = t.replace('#_renderFunction_#', field[1].render(item));
      }

      itemHtml = $(t);
      itemHtml.attr('fieldId', `${field[0]}_div`);
      html.append(itemHtml);
    }


    return html;
  }

  /**
     * Reset the DataGroup for a given field
     * @method resetDataGroup
     * @param field {Array} field meta data
     */
  resetDataGroup(field) {
    $(`#${field[0]}`).val('');
    $(`#${field[0]}_div`).html('');
  }

  showDataGroup(field, object) {
    let formHtml = this.templates.datagroupTemplate;
    let html = '';
    const fields = field[1].form;

    if (object !== undefined && object != null && object.id !== undefined) {
      this.currentDataGroupItemId = object.id;
    } else {
      this.currentDataGroupItemId = null;
    }

    for (let i = 0; i < fields.length; i++) {
      html += this.renderFormField(fields[i]);
    }
    formHtml = formHtml.replace(/_id_/g, `${this.getTableName()}_field_${field[0]}`);
    formHtml = formHtml.replace(/_fields_/g, html);

    const randomFormId = this.generateRandom(14);
    const $tempDomObj = $('<div class="reviewBlock popupForm" data-content="Form"></div>');
    $tempDomObj.attr('id', randomFormId);

    $tempDomObj.html(formHtml);


    $tempDomObj.find('.datefield').datepicker({ viewMode: 2 });
    $tempDomObj.find('.timefield').datetimepicker({
      language: 'en',
      pickDate: false,
    });
    $tempDomObj.find('.datetimefield').datetimepicker({
      language: 'en',
    });

    $tempDomObj.find('.colorpick').colorpicker();

    tinymce.init({
      selector: `#${$tempDomObj.attr('id')} .tinymce`,
      height: '400',
    });

    $tempDomObj.find('.simplemde').each(function () {
      const simplemde = new SimpleMDE({ element: $(this)[0] });
      $(this).data('simplemde', simplemde);
      // simplemde.value($(this).val());
    });

    $tempDomObj.find('.select2Field').each(function () {
      $(this).select2().select2('val', $(this).find('option:eq(0)').val());
    });


    $tempDomObj.find('.select2Multi').each(function () {
      $(this).select2().on('change', function (e) {
        const parentRow = $(this).parents('.row');
        const height = parentRow.find('.select2-choices').height();
        parentRow.height(parseInt(height, 10));
      });
    });


    this.currentDataGroupField = field;
    this.showDomElement(`Add ${field[1].label}`, $tempDomObj, null, null, true);

    if (object !== undefined && object != null) {
      this.fillForm(object, `#${this.getTableName()}_field_${field[0]}`, field[1].form);
    } else {
      this.setDefaultValues(`#${this.getTableName()}_field_${field[0]}`, field[1].form);
    }


    $('.groupAddBtn').off();
    if (object !== undefined && object != null && object.id !== undefined) {
      $('.groupAddBtn').on('click', (e) => {
        e.preventDefault();
        e.stopPropagation();
        try {
          modJs.editDataGroup();
        } catch (err) {
          console.log(`Error editing data group: ${err.message}`);
        }
        return false;
      });
    } else {
      $('.groupAddBtn').on('click', (e) => {
        e.preventDefault();
        e.stopPropagation();
        try {
          modJs.addDataGroup();
        } catch (err) {
          console.log(`Error adding data group: ${err.message}`);
        }
        return false;
      });
    }
  }

  addDataGroup() {
    const field = this.currentDataGroupField; let
      tempParams;
    $(`#${this.getTableName()}_field_${field[0]}_error`).html('');
    $(`#${this.getTableName()}_field_${field[0]}_error`).hide();
    const validator = new FormValidation(`${this.getTableName()}_field_${field[0]}`, true, { ShowPopup: false, LabelErrorClass: 'error' });
    if (validator.checkValues()) {
      let params = validator.getFormParameters();
      if (field[1]['custom-validate-function'] !== undefined && field[1]['custom-validate-function'] != null) {
        tempParams = field[1]['custom-validate-function'].apply(this, [params]);
        if (tempParams.valid) {
          params = tempParams.params;
        } else {
          $(`#${this.getTableName()}_field_${field[0]}_error`).html(tempParams.message);
          $(`#${this.getTableName()}_field_${field[0]}_error`).show();
          return false;
        }
      }

      let val = $(`#${field[0]}`).val();
      if (val === '') {
        val = '[]';
      }
      const data = JSON.parse(val);

      params.id = `${field[0]}_${this.dataGroupGetNextAutoIncrementId(data)}`;
      data.push(params);


      if (field[1]['sort-function'] !== undefined && field[1]['sort-function'] != null) {
        data.sort(field[1]['sort-function']);
      }

      val = JSON.stringify(data);

      const html = this.dataGroupToHtml(val, field);

      $(`#${field[0]}_div`).html('');
      $(`#${field[0]}_div`).append(html);

      this.makeDataGroupSortable(field, $(`#${field[0]}_div_inner`));


      $(`#${field[0]}`).val(val);
      this.orderDataGroup(field);

      this.closeDataMessage();

      this.showMessage('Item Added', 'This change will be effective only when you save the form');
    }
    return true;
  }

  nl2br(str, len) {
    let t = '';
    try {
      const arr = str.split(' ');
      let count = 0;
      for (let i = 0; i < arr.length; i++) {
        count += arr[i].length + 1;
        if (count > len) {
          t += `${arr[i]}<br/>`;
          count = 0;
        } else {
          t += `${arr[i]} `;
        }
      }
    } catch (e) {
      // Do nothing
    }
    return t;
  }

  makeDataGroupSortable(field, obj) {
    obj.data('field', field);
    obj.data('firstSort', true);
    obj.sortable({

      create() {
        $(this).height($(this).height());
      },

      'ui-floating': false,
      start(e, uiStart) {
        $('#sortable-ul-selector-id').sortable({
          sort(event, ui) {
            const $target = $(event.target);
            if (!/html|body/i.test($target.offsetParent()[0].tagName)) {
              const top = event.pageY - $target.offsetParent().offset().top - (ui.helper.outerHeight(true) / 2);
              ui.helper.css({ top: `${top}px` });
            }
          },
        });
      },
      revert: true,
      stop() {
        modJs.orderDataGroup($(this).data('field'));
      },
      axis: 'y',
      scroll: false,
      placeholder: 'sortable-placeholder',
      cursor: 'move',
    });
  }

  orderDataGroup(field) {
    const newArr = []; let
      id;
    const list = $(`#${field[0]}_div_inner [fieldid='${field[0]}_div']`);
    let val = $(`#${field[0]}`).val();
    if (val === '') {
      val = '[]';
    }
    const data = JSON.parse(val);
    list.each(function () {
      id = $(this).attr('id');
      for (const index in data) {
        if (data[index].id === id) {
          newArr.push(data[index]);
          break;
        }
      }
    });

    $(`#${field[0]}`).val(JSON.stringify(newArr));
  }


  editDataGroup() {
    const field = this.currentDataGroupField;
    const id = this.currentDataGroupItemId;
    const validator = new FormValidation(`${this.getTableName()}_field_${field[0]}`, true, { ShowPopup: false, LabelErrorClass: 'error' });
    if (validator.checkValues()) {
      let params = validator.getFormParameters();

      if (field[1]['custom-validate-function'] !== undefined && field[1]['custom-validate-function'] != null) {
        const tempParams = field[1]['custom-validate-function'].apply(this, [params]);
        if (tempParams.valid) {
          params = tempParams.params;
        } else {
          $(`#${this.getTableName()}_field_${field[0]}_error`).html(tempParams.message);
          $(`#${this.getTableName()}_field_${field[0]}_error`).show();
          return false;
        }
      }


      if (this.doCustomFilterValidation(params)) {
        let val = $(`#${field[0]}`).val();
        if (val === '') {
          val = '[]';
        }
        const data = JSON.parse(val);

        let editVal = {};
        let editValIndex = -1;
        const newVals = [];
        for (let i = 0; i < data.length; i++) {
          const item = data[i];
          if (item.id === id) {
            editVal = item;
            editValIndex = i;
          }
          newVals.push(item);
        }


        params.id = editVal.id;
        newVals[editValIndex] = params;

        if (field[1]['sort-function'] !== undefined && field[1]['sort-function'] != null) {
          newVals.sort(field[1]['sort-function']);
        }

        val = JSON.stringify(newVals);
        $(`#${field[0]}`).val(val);

        const html = this.dataGroupToHtml(val, field);

        this.orderDataGroup(field);

        $(`#${field[0]}_div`).html('');
        $(`#${field[0]}_div`).append(html);

        this.makeDataGroupSortable(field, $(`#${field[0]}_div_inner`));


        this.closeDataMessage();

        this.showMessage('Item Edited', 'This change will be effective only when you save the form');
      }
    }

    return true;
  }

  editDataGroupItem(id) {
    const fieldId = id.substring(0, id.lastIndexOf('_'));

    const val = $(`#${fieldId}`).val();
    const data = JSON.parse(val);

    let editVal = {};

    for (let i = 0; i < data.length; i++) {
      const item = data[i];
      if (item.id === id) {
        editVal = item;
      }
    }

    this.showDataGroup($(`#${fieldId}`).data('field'), editVal);
  }

  dataGroupGetNextAutoIncrementId(data) {
    let autoId = 1; let
      id;
    for (let i = 0; i < data.length; i++) {
      const item = data[i];
      if (item.id === undefined || item.id == null) {
        item.id = 1;
      }
      id = item.id.substring(item.id.lastIndexOf('_') + 1, item.id.length);
      if (id >= autoId) {
        autoId = parseInt(id, 10) + 1;
      }
    }

    return autoId;
  }


  deleteDataGroupItem(id) {
    const fieldId = id.substring(0, id.lastIndexOf('_'));

    const val = $(`#${fieldId}`).val();
    const data = JSON.parse(val);

    const newVal = [];

    for (let i = 0; i < data.length; i++) {
      const item = data[i];
      if (item.id !== id) {
        newVal.push(item);
      }
    }

    $(`#${fieldId}`).val(JSON.stringify(newVal));

    $(`#${id}`).remove();

    this.showMessage('Item Removed', 'Item removed. This change will be effective only when you save the form');
  }


  /**
     * Fill a form with required values after showing it
     * @method fillForm
     * @param object {Array} form data
     * @param formId {String} id of the form
     * @param formId {Array} field meta data
     */

  fillForm(object, formId, fields) {
    let placeHolderVal;
    if (fields == null || fields === undefined) {
      fields = this.getFormFields();
    }

    if (formId == null || formId === undefined || formId === '') {
      formId = `#${this.getTableName()}Form`;
    }


    for (let i = 0; i < fields.length; i++) {
      if (fields[i][1].type === 'date') {
        if (object[fields[i][0]] !== '0000-00-00' && object[fields[i][0]] !== '' && object[fields[i][0]] != null && object[fields[i][0]] !== undefined) {
          $(`${formId} #${fields[i][0]}_date`).datepicker('setValue', object[fields[i][0]]);
        }
      } else if (fields[i][1].type === 'colorpick') {
        if (object[fields[i][0]] != null && object[fields[i][0]] !== undefined) {
          $(`${formId} #${fields[i][0]}_colorpick`).colorpicker('setValue', object[fields[i][0]]);
          $(`${formId} #${fields[i][0]}`).val(object[fields[i][0]]);
        }
      } else if (fields[i][1].type === 'datetime' || fields[i][1].type === 'time') {
        if (object[fields[i][0]] !== '0000-00-00 00:00:00' && object[fields[i][0]] !== '' && object[fields[i][0]] != null && object[fields[i][0]] !== undefined) {
          const tempDate = object[fields[i][0]];
          const arr = tempDate.split(' ');
          const dateArr = arr[0].split('-');
          const timeArr = arr[1].split(':');
          $(`${formId} #${fields[i][0]}_datetime`).data('datetimepicker').setLocalDate(new Date(dateArr[0], parseInt(dateArr[1], 10) - 1, dateArr[2], timeArr[0], timeArr[1], timeArr[2]));
        }
      } else if (fields[i][1].type === 'label') {
        $(`${formId} #${fields[i][0]}`).html(object[fields[i][0]]);
      } else if (fields[i][1].type === 'placeholder') {
        if (fields[i][1]['remote-source'] !== undefined && fields[i][1]['remote-source'] != null) {
          const key = `${fields[i][1]['remote-source'][0]}_${fields[i][1]['remote-source'][1]}_${fields[i][1]['remote-source'][2]}`;
          placeHolderVal = this.fieldMasterData[key][object[fields[i][0]]];
        } else {
          placeHolderVal = object[fields[i][0]];
        }

        if (placeHolderVal === undefined || placeHolderVal == null) {
          placeHolderVal = '';
        } else {
          try {
            placeHolderVal = placeHolderVal.replace(/(?:\r\n|\r|\n)/g, '<br />');
          } catch (e) {
            // Do nothing
          }
        }

        if (fields[i][1].formatter !== undefined && fields[i][1].formatter && $.isFunction(fields[i][1].formatter)) {
          try {
            placeHolderVal = fields[i][1].formatter(placeHolderVal);
          } catch (e) {
            // Do nothing
          }
        }

        $(`${formId} #${fields[i][0]}`).html(placeHolderVal);
      } else if (fields[i][1].type === 'fileupload') {
        if (object[fields[i][0]] != null && object[fields[i][0]] !== undefined && object[fields[i][0]] !== '') {
          $(`${formId} #${fields[i][0]}`).html(object[fields[i][0]]);
          $(`${formId} #${fields[i][0]}`).attr('val', object[fields[i][0]]);
          $(`${formId} #${fields[i][0]}`).show();
          $(`${formId} #${fields[i][0]}_download`).show();
          $(`${formId} #${fields[i][0]}_remove`).show();
        }
        if (fields[i][1].readonly === true) {
          $(`${formId} #${fields[i][0]}_upload`).remove();
        }
      } else if (fields[i][1].type === 'select') {
        if (object[fields[i][0]] === undefined || object[fields[i][0]] == null || object[fields[i][0]] === '') {
          object[fields[i][0]] = 'NULL';
        }
        $(`${formId} #${fields[i][0]}`).val(object[fields[i][0]]);
      } else if (fields[i][1].type === 'select2') {
        if (object[fields[i][0]] === undefined || object[fields[i][0]] == null || object[fields[i][0]] === '') {
          object[fields[i][0]] = 'NULL';
        }
        $(`${formId} #${fields[i][0]}`).select2('val', object[fields[i][0]]);
      } else if (fields[i][1].type === 'select2multi') {
        // TODO - SM
        if (object[fields[i][0]] === undefined || object[fields[i][0]] == null || object[fields[i][0]] === '') {
          object[fields[i][0]] = 'NULL';
        }

        let msVal = [];
        if (object[fields[i][0]] !== undefined && object[fields[i][0]] != null && object[fields[i][0]] !== '') {
          try {
            msVal = JSON.parse(object[fields[i][0]]);
          } catch (e) {
            // Do nothing
          }
        }

        $(`${formId} #${fields[i][0]}`).select2('val', msVal);
        const select2Height = $(`${formId} #${fields[i][0]}`).find('.select2-choices').height();
        $(`${formId} #${fields[i][0]}`).find('.controls').css('min-height', `${select2Height}px`);
        $(`${formId} #${fields[i][0]}`).css('min-height', `${select2Height}px`);
      } else if (fields[i][1].type === 'datagroup') {
        try {
          const html = this.dataGroupToHtml(object[fields[i][0]], fields[i]);
          $(`${formId} #${fields[i][0]}`).val(object[fields[i][0]]);
          $(`${formId} #${fields[i][0]}_div`).html('');
          $(`${formId} #${fields[i][0]}_div`).append(html);

          this.makeDataGroupSortable(fields[i], $(`${formId} #${fields[i][0]}_div_inner`));
        } catch (e) {
          // Do nothing
        }
      } else if (fields[i][1].type === 'signature') {
        if (object[fields[i][0]] !== '' || object[fields[i][0]] !== undefined
                    || object[fields[i][0]] != null) {
          $(`${formId} #${fields[i][0]}`).data('signaturePad').fromDataURL(object[fields[i][0]]);
        }
      } else if (fields[i][1].type === 'simplemde') {
        $(`${formId} #${fields[i][0]}`).data('simplemde').value(object[fields[i][0]]);
      } else if (fields[i][1].type === 'code') {
        const cm = $(`${formId} #${fields[i][0]}`).data('codemirror');
        if (cm) {
          cm.getDoc().setValue(object[fields[i][0]]);
        }
      } else {
        $(`${formId} #${fields[i][0]}`).val(object[fields[i][0]]);
      }
    }
  }

  /**
     * Cancel edit or add new on modules
     * @method cancel
     */

  cancel() {
    $(`#${this.getTableName()}Form`).hide();
    $(`#${this.getTableName()}`).show();
  }

  renderFormField(field) {
    let userId = 0;
    if (this.fieldTemplates[field[1].type] === undefined || this.fieldTemplates[field[1].type] == null) {
      return '';
    }
    let t = this.fieldTemplates[field[1].type];
    field[1].label = this.gt(field[1].label);
    if (field[1].validation !== 'none' && field[1].validation !== 'emailOrEmpty' && field[1].validation !== 'numberOrEmpty' && field[1].type !== 'placeholder' && field[1].label.indexOf('*') < 0) {
      const tempSelectBoxes = ['select', 'select2'];
      if (!(tempSelectBoxes.indexOf(field[1].type) >= 0 && field[1]['allow-null'] === true)) {
        field[1].label = `${field[1].label}<font class="redFont">*</font>`;
      }
    }

    if (field[1].type === 'select' || field[1].type === 'select2' || field[1].type === 'select2multi') {
      t = t.replace(/_id_/g, field[0]);
      t = t.replace(/_label_/g, field[1].label);
      if (field[1].source !== undefined && field[1].source != null) {
        t = t.replace('_options_', this.renderFormSelectOptions(field[1].source, field));
      } else if (field[1]['remote-source'] !== undefined && field[1]['remote-source'] != null) {
        const key = `${field[1]['remote-source'][0]}_${field[1]['remote-source'][1]}_${field[1]['remote-source'][2]}`;
        t = t.replace('_options_', this.renderFormSelectOptionsRemote(this.fieldMasterData[key], field));
      }
    } else if (field[1].type === 'colorpick') {
      t = t.replace(/_id_/g, field[0]);
      t = t.replace(/_label_/g, field[1].label);
    } else if (field[1].type === 'date') {
      t = t.replace(/_id_/g, field[0]);
      t = t.replace(/_label_/g, field[1].label);
    } else if (field[1].type === 'datetime') {
      t = t.replace(/_id_/g, field[0]);
      t = t.replace(/_label_/g, field[1].label);
    } else if (field[1].type === 'time') {
      t = t.replace(/_id_/g, field[0]);
      t = t.replace(/_label_/g, field[1].label);
    } else if (field[1].type === 'fileupload') {
      t = t.replace(/_id_/g, field[0]);
      t = t.replace(/_label_/g, field[1].label);
      const ce = this.getCurrentProfile();
      if (ce != null && ce !== undefined) {
        userId = ce.id;
      } else {
        userId = this.getUser().id * -1;
      }
      t = t.replace(/_userId_/g, userId);
      t = t.replace(/_group_/g, this.tab);

      if (field[1].filetypes !== undefined && field[1].filetypes != null) {
        t = t.replace(/_filetypes_/g, field[1].filetypes);
      } else {
        t = t.replace(/_filetypes_/g, 'all');
      }

      t = t.replace(/_rand_/g, this.generateRandom(14));
    } else if (field[1].type === 'datagroup') {
      t = t.replace(/_id_/g, field[0]);
      t = t.replace(/_label_/g, field[1].label);
    } else if (field[1].type === 'signature') {
      t = t.replace(/_id_/g, field[0]);
      t = t.replace(/_label_/g, field[1].label);
    } else if (field[1].type === 'tinymce' || field[1].type === 'simplemde') {
      t = t.replace(/_id_/g, field[0]);
      t = t.replace(/_label_/g, field[1].label);
    } else {
      t = t.replace(/_id_/g, field[0]);
      t = t.replace(/_label_/g, field[1].label);
    }


    if (field[1].validation !== undefined && field[1].validation != null && field[1].validation !== '') {
      t = t.replace(/_validation_/g, `validation="${field[1].validation}"`);
    } else {
      t = t.replace(/_validation_/g, '');
    }

    if (field[1].help !== undefined && field[1].help !== null) {
      t = t.replace(/_helpline_/g, field[1].help);
      t = t.replace(/_hidden_class_help_/g, '');
    } else {
      t = t.replace(/_helpline_/g, '');
      t = t.replace(/_hidden_class_help_/g, 'hide');
    }

    if (field[1].placeholder !== undefined && field[1].placeholder !== null) {
      t = t.replace(/_placeholder_/g, `placeholder="${field[1].placeholder}"`);
    } else {
      t = t.replace(/_placeholder_/g, '');
    }

    if (field[1].mask !== undefined && field[1].mask !== null) {
      t = t.replace(/_mask_/g, `mask="${field[1].mask}"`);
    } else {
      t = t.replace(/_mask_/g, '');
    }

    return t;
  }

  renderFormSelectOptions(options, field) {
    let html = '';

    if (field != null && field !== undefined) {
      if (field[1]['allow-null'] === true) {
        if (field[1]['null-label'] !== undefined && field[1]['null-label'] != null) {
          html += `<option value="NULL">${this.gt(field[1]['null-label'])}</option>`;
        } else {
          html += '<option value="NULL">Select</option>';
        }
      }
    }


    // Sort options

    const tuples = [];

    for (const key in options) {
      tuples.push(options[key]);
    }
    if (field[1].sort === true) {
      tuples.sort((a, b) => {
        a = a[1];
        b = b[1];

        // eslint-disable-next-line no-nested-ternary
        return a < b ? -1 : (a > b ? 1 : 0);
      });
    }


    for (let i = 0; i < tuples.length; i++) {
      const prop = tuples[i][0];
      const value = tuples[i][1];
      let t = '<option value="_id_">_val_</option>';
      t = t.replace('_id_', prop);
      t = t.replace('_val_', this.gt(value));
      html += t;
    }
    return html;
  }

  renderFormSelectOptionsRemote(options, field) {
    let html = '';
    if (field[1]['allow-null'] === true) {
      if (field[1]['null-label'] !== undefined && field[1]['null-label'] != null) {
        html += `<option value="NULL">${this.gt(field[1]['null-label'])}</option>`;
      } else {
        html += '<option value="NULL">Select</option>';
      }
    }

    // Sort options

    const tuples = [];

    for (const key in options) {
      tuples.push([key, options[key]]);
    }
    if (field[1].sort === 'true') {
      tuples.sort((a, b) => {
        a = a[1];
        b = b[1];

        // eslint-disable-next-line no-nested-ternary
        return a < b ? -1 : (a > b ? 1 : 0);
      });
    }

    for (let i = 0; i < tuples.length; i++) {
      const prop = tuples[i][0];
      const value = tuples[i][1];

      let t = '<option value="_id_">_val_</option>';
      t = t.replace('_id_', prop);
      t = t.replace('_val_', this.gt(value));
      html += t;
    }


    return html;
  }

  setCustomTemplates(templates) {
    this.customTemplates = templates;
  }

  setEmailTemplates(templates) {
    this.emailTemplates = templates;
  }

  getCustomTemplate(file) {
    return this.customTemplates[file];
  }

  setFieldTemplates(templates) {
    this.fieldTemplates = templates;
  }


  getMetaFieldForRendering(fieldName) {
    return '';
  }

  clearDeleteParams() {
    this.deleteParams = {};
  }

  getShowAddNew() {
    return this.showAddNew;
  }

  /**
     * Override this method to change add new button label
     * @method getAddNewLabel
     */

  getAddNewLabel() {
    return 'Add New';
  }

  /**
     * Used to set whether to show the add new button for a module
     * @method setShowAddNew
     * @param showAddNew {Boolean} value
     */

  setShowAddNew(showAddNew) {
    this.showAddNew = showAddNew;
  }

  /**
     * Used to set whether to show delete button for each entry in module
     * @method setShowDelete
     * @param val {Boolean} value
     */
  setShowDelete(val) {
    this.showDelete = val;
  }


  /**
     * Used to set whether to show edit button for each entry in module
     * @method setShowEdit
     * @param val {Boolean} value
     */

  setShowEdit(val) {
    this.showEdit = val;
  }

  /**
     * Used to set whether to show save button in form
     * @method setShowSave
     * @param val {Boolean} value
     */


  setShowSave(val) {
    this.showSave = val;
  }


  /**
     * Used to set whether to show cancel button in form
     * @method setShowCancel
     * @param val {Boolean} value
     */

  setShowCancel(val) {
    this.showCancel = val;
  }

  /**
     * Datatable option array will be extended with associative array provided here
     * @method getCustomTableParams
     * @param val {Boolean} value
     */


  getCustomTableParams() {
    return {};
  }

  getActionButtons(obj) {
    return modJs.getActionButtonsHtml(obj.aData[0], obj.aData);
  }


  /**
     * This return html for action buttons in each row. Override this method if you need to make changes to action buttons.
     * @method getActionButtonsHtml
     * @param id {int} id of the row
     * @param data {Array} data for the row
     * @returns {String} html for action buttons
     */

  getActionButtonsHtml(id, data) {
    const editButton = '<img class="tableActionButton" src="_BASE_images/edit.png" style="cursor:pointer;" rel="tooltip" title="Edit" onclick="modJs.edit(_id_);return false;"></img>';
    const deleteButton = '<img class="tableActionButton" src="_BASE_images/delete.png" style="margin-left:15px;cursor:pointer;" rel="tooltip" title="Delete" onclick="modJs.deleteRow(_id_);return false;"></img>';
    const cloneButton = '<img class="tableActionButton" src="_BASE_images/clone.png" style="margin-left:15px;cursor:pointer;" rel="tooltip" title="Copy" onclick="modJs.copyRow(_id_);return false;"></img>';
    let html = '<div style="width:80px;">_edit__delete__clone_</div>';

    if (this.showAddNew) {
      html = html.replace('_clone_', cloneButton);
    } else {
      html = html.replace('_clone_', '');
    }

    if (this.showDelete) {
      html = html.replace('_delete_', deleteButton);
    } else {
      html = html.replace('_delete_', '');
    }

    if (this.showEdit) {
      html = html.replace('_edit_', editButton);
    } else {
      html = html.replace('_edit_', '');
    }

    html = html.replace(/_id_/g, id);
    html = html.replace(/_BASE_/g, this.baseUrl);
    return html;
  }


  /**
     * Generates a random string
     * @method generateRandom
     * @param length {int} required length of the string
     * @returns {String} random string
     */

  generateRandom(length) {
    const d = new Date();
    const chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
    let result = '';
    for (let i = length; i > 0; --i) result += chars[Math.round(Math.random() * (chars.length - 1))];
    return result + d.getTime();
  }


  checkFileType(elementName, fileTypes) {
    const fileElement = document.getElementById(elementName);
    let fileExtension = '';
    if (fileElement.value.lastIndexOf('.') > 0) {
      fileExtension = fileElement.value.substring(fileElement.value.lastIndexOf('.') + 1, fileElement.value.length);
    }

    fileExtension = fileExtension.toLowerCase();

    const allowed = fileTypes.split(',');

    if (allowed.indexOf(fileExtension) < 0) {
      fileElement.value = '';
      this.showMessage('File Type Error', 'Selected file type is not supported');
      this.clearFileElement(elementName);
      return false;
    }

    return true;
  }

  clearFileElement(elementName) {
    let control = $(`#${elementName}`);
    control.replaceWith(control = control.val('').clone(true));
  }


  fixJSON(json) {
    if (this.noJSONRequests === '1') {
      json = window.btoa(json);
    }
    return json;
  }


  getClientDate(date) {
    const offset = this.getClientGMTOffset();
    const tzDate = date.addMinutes(offset * 60);
    return tzDate;
  }

  getClientGMTOffset() {
    const rightNow = new Date();
    const jan1 = new Date(rightNow.getFullYear(), 0, 1, 0, 0, 0, 0);
    const temp = jan1.toGMTString();
    const jan2 = new Date(temp.substring(0, temp.lastIndexOf(' ') - 1));
    return (jan1 - jan2) / (1000 * 60 * 60);
  }

  /**
     * Override this method in a module to provide the help link for the module. Help link of the module on frontend will get updated with this.
     * @method getHelpLink
     * @returns {String} help link
     */

  getHelpLink() {
    return null;
  }

  showLoader() {
    $('#iceloader').show();
  }

  hideLoader() {
    $('#iceloader').hide();
  }

  generateOptions(data) {
    const template = '<option value="__val__">__text__</option>';
    let options = '';
    for (const index in data) {
      options += template.replace('__val__', index).replace('__text__', data[index]);
    }

    return options;
  }

  isModuleInstalled(type, name) {
    if (modulesInstalled === undefined || modulesInstalled === null) {
      return false;
    }

    return (modulesInstalled[`${type}_${name}`] === 1);
  }


  setCustomFields(fields) {
    let field; let
      parsed;
    for (let i = 0; i < fields.length; i++) {
      field = fields[i];
      if (field.display !== 'Hidden' && field.data !== '' && field.data !== undefined) {
        try {
          parsed = JSON.parse(field.data);
          if (parsed === undefined || parsed == null) {
            continue;
          } else if (parsed.length !== 2) {
            continue;
          } else if (parsed[1].type === undefined || parsed[1].type == null) {
            continue;
          }
          this.customFields.push(parsed);
        } catch (e) {
          // Do nothing
        }
      }
    }
  }

  addCustomFields(fields) {
    for (let i = 0; i < this.customFields.length; i++) {
      fields.push(this.customFields[i]);
    }

    return fields;
  }
}

export default ModuleBase;
