modules/LbWidget.js

import moment from 'moment';
import Identicon from 'identicon.js';
import jsSHA from 'jssha';
import cssVars from 'css-vars-ponyfill';

import mergeObjects from '../utils/mergeObjects';
import mapObject from '../utils/mapObject';
import formatNumberLeadingZeros from '../utils/formatNumberLeadingZeros';
import stringContains from '../utils/stringContains';
import objectIterator from '../utils/objectIterator';
import query from '../utils/query';
import hasClass from '../utils/hasClass';
import addClass from '../utils/addClass';
import removeClass from '../utils/removeClass';
import closest from '../utils/closest';
import isMobileTablet from '../utils/isMobileTablet';
import camelToKebabCase from '../utils/camelToKebabCase';

import cLabs from './cLabs';
import './Ajax';

import { Notifications } from './Notifications';
import { MiniScoreBoard } from './MiniScoreBoard';
import { MainWidget } from './MainWidget';
import { CanvasAnimation } from './CanvasAnimation';

const translation = require(`../../i18n/translation_${process.env.LANG}.json`);

/**
 * Main leaderboard widget, controls all actions and initiation logic.
 * Main responsibility is to control the interactions between different widgets/plugins and user even actions
 * @param options {Object} setting parameters used to overwrite the default settings
 * @constructor
 */
export const LbWidget = function (options) {
  /**
   * LbWidget settings
   * @memberOf LbWidget
   * @constant
   * @type { Object }
   */
  this.settings = {
    debug: true,
    bindContainer: document.body,
    autoStart: true,
    sseMessaging: null,
    notifications: null,
    miniScoreBoard: null,
    canvasAnimation: null,
    enableNotifications: true,
    mainWidget: null,
    globalAjax: new cLabs.Ajax(),
    checkAjax: new cLabs.Ajax(),
    language: process.env.LANG,
    currency: '',
    spaceName: '',
    memberId: '',
    groups: '',
    gameId: '',
    enforceGameLookup: false, // tournament lookup will include/exclude game only requests
    apiKey: '',
    member: null,
    layout: {
      enableMiniScoreBoardDragging: true, // enable/disable dragging with mouse/touch
      miniScoreBoardPosition: { // default position of mini scoreboard left/right/bottom/top (Example: top: '20px')
        left: null,
        right: null,
        top: null,
        bottom: null
      },
      allowOrientationChange: true, // allows the switch between horizontal/vertical orientation
      miniScoreBoardOrientation: 'horizontal' // vertical/horizontal => default is horizontal
    },
    competition: {
      activeCompetitionId: null,
      activeContestId: null,
      activeCompetition: null,
      activeContest: null,
      refreshInterval: null,
      refreshIntervalMillis: 10000,
      allowNegativeCountdown: false, // false: will mark competition as finishing, true: will continue to countdown into negative
      includeMetadata: false,
      extractImageHeader: true // will extract the first found image inside the body tag and move it on top
    },
    achievements: {
      limit: 100,
      totalCount: 0,
      list: [],
      availableRewards: [],
      rewards: [],
      expiredRewards: [],
      extractImageHeader: true // will extract the first found image inside the body tag and move it on top
    },
    rewards: {
      availableRewards: [],
      rewards: [],
      expiredRewards: [],
      rewardFormatter: function (reward) {
        var defaultRewardValue = reward.value;

        if (typeof reward.unitOfMeasure !== 'undefined' && typeof reward.unitOfMeasure.symbol !== 'undefined' && reward.unitOfMeasure.symbol !== null) {
          defaultRewardValue = reward.unitOfMeasure.symbol + reward.value;
        }

        return defaultRewardValue;
      }
    },
    messages: {
      messages: []
    },
    tournaments: {
      activeCompetitionId: null,
      readyCompetitions: [], // statusCode 3
      activeCompetitions: [], // statusCode 5
      finishedCompetitions: [] // statusCode 7
    },
    leaderboard: {
      fullLeaderboardSize: 100,
      refreshIntervalMillis: 3000,
      refreshInterval: null,
      refreshLbDataInterval: null,
      leaderboardData: [],
      loadLeaderboardHistory: {},
      layoutSettings: {
        // tournamentList: true,
        imageBanner: true,
        // title: true,
        titleLinkToDetailsPage: false // if set to false will make the description available under title
      },
      miniScoreBoard: {
        enableRankings: true, // enabled rankings before after rankings of members [-2 YOU +2]
        rankingsCount: 2
      },
      pointsFormatter: function (points) {
        return points;
      }
    },
    navigation: { // primary navigation items, if all are disabled init will fail, if only 1 is enabled items will be hidden
      tournaments: {
        enable: true,
        navigationClass: 'cl-main-widget-navigation-lb',
        navigationClassIcon: 'cl-main-widget-navigation-lb-icon',
        containerClass: 'cl-main-widget-lb',
        order: 1
      },
      achievements: {
        enable: true,
        navigationClass: 'cl-main-widget-navigation-ach',
        navigationClassIcon: 'cl-main-widget-navigation-ach-icon',
        containerClass: 'cl-main-widget-section-ach',
        order: 2
      },
      rewards: {
        enable: true,
        navigationClass: 'cl-main-widget-navigation-rewards',
        navigationClassIcon: 'cl-main-widget-navigation-rewards-icon',
        containerClass: 'cl-main-widget-section-reward',
        order: 3
      },
      inbox: {
        enable: true,
        navigationClass: 'cl-main-widget-navigation-inbox',
        navigationClassIcon: 'cl-main-widget-navigation-inbox-icon',
        containerClass: 'cl-main-widget-section-inbox',
        order: 4
      }
    },
    uri: {
      gatewayDomain: cLabs.api.url,

      members: '/api/v1/:space/members/reference/:id',
      assets: '/assets/attachments/:attachmentId',

      memberSSE: '/api/v1/:space/sse/reference/:id',
      memberSSEHeartbeat: '/api/v1/:space/sse/reference/:id/heartbeat',

      competitions: '/api/v1/:space/competitions',
      competitionById: '/api/v1/:space/competitions/:id',
      contestLeaderboard: '/api/v1/:space/contests/:id/leaderboard',

      achievement: '/api/v1/:space/achievements/:id',
      achievements: '/api/v1/:space/achievements/members/reference/:id',
      // achievements: "/api/v1/:space/achievements",
      achievementsProgression: '/api/v1/:space/members/reference/:id/achievements',
      achievementsIssued: '/api/v1/:space/members/reference/:id/achievements/issued',

      messages: '/api/v1/:space/members/reference/:id/messages',
      messageById: '/api/v1/:space/members/reference/:id/messages/:messageId',

      memberReward: '/api/v1/:space/members/reference/:id/award/:awardId',
      memberRewardClaim: '/api/v1/:space/members/reference/:id/award/:awardId/award',

      memberCompetitions: '/api/v1/:space/members/reference/:id/competitions',
      memberCompetitionById: '/api/v1/:space/members/reference/:id/competition/:competitionId',
      memberCompetitionOptIn: '/api/v1/:space/members/reference/:id/competition/:competitionId/optin',
      memberCompetitionOptInCheck: '/api/v1/:space/members/reference/:id/competition/:competitionId/optin-check',

      translationPath: '' // ../i18n/translation_:language.json
    },
    loadTranslations: true,
    showCopyright: true,
    translation: translation,
    resources: [], // Example: ["http://example.com/style.css", "http://example.com/my-fonts.css"]
    styles: null, // Example: {widgetBgColor: '#1f294a', widgetIcon: 'url(../../../examples/images/logo-icon-3.png)'}
    partialFunctions: {
      startupCallback: function (instance) {},
      rewardFormatter: function (reward) {
        var defaultRewardValue = reward.value;

        if (typeof reward.unitOfMeasure !== 'undefined' && typeof reward.unitOfMeasure.symbol !== 'undefined' && reward.unitOfMeasure.symbol !== null) {
          defaultRewardValue = reward.unitOfMeasure.symbol + reward.value;
        }

        return defaultRewardValue;
      },
      competitionDataAvailableResponseParser: function (competitionData, callback) { callback(competitionData); },
      competitionDataFinishedResponseParser: function (competitionData, callback) { callback(competitionData); },
      activeCompetitionDataResponseParser: function (competitionData, callback) { callback(competitionData); },
      activeContestDataResponseParser: function (contestData, callback) { callback(contestData); },
      leaderboardDataResponseParser: function (leaderboardData, callback) { callback(leaderboardData); },
      achievementDataForAllResponseParser: function (achievementData, callback) { callback(achievementData); },
      achievementDataForMemberGroupResponseParser: function (achievementData, callback) { callback(achievementData); },
      achievementDataResponseParser: function (achievementData, callback) { callback(achievementData); },
      rewardDataResponseParser: function (rewardData, callback) { callback(rewardData); },
      messageDataResponseParser: function (messageData, callback) { callback(messageData); },
      claimRewardDataResponseParser: function (claimRewardData, callback) { callback(claimRewardData); },
      issuedAchievementsDataResponseParser: function (issuedAchievementsData, callback) { callback(issuedAchievementsData); },
      memberAchievementsProgressionDataResponseParser: function (memberAchievementsProgressionData, callback) { callback(memberAchievementsProgressionData); },
      claimedRewardsDataResponseParser: function (claimedRewardsData, callback) { callback(claimedRewardsData); },
      notClaimedRewardsDataResponseParser: function (notClaimedRewardsData, callback) { callback(notClaimedRewardsData); },
      expiredRewardsDataResponseParser: function (expiredRewardsData, callback) { callback(expiredRewardsData); },
      availableMessagesDataResponseParser: function (availableMessagesData, callback) { callback(availableMessagesData); }
    }
  };

  if (typeof options !== 'undefined') {
    this.settings = mergeObjects(this.settings, options);
  }

  // alias references to modules
  this.CanvasAnimation = CanvasAnimation;
  this.Notifications = Notifications;
  this.MiniScoreBoard = MiniScoreBoard;
  this.MainWidget = MainWidget;

  this.log = function (message) {
    if (this.settings.debug) {
      console.error(message);
    }
  };

  /**
   * Format duration of Date Time from moment() object
   * @memberOf LbWidget
   * @param duration {moment}
   * @returns {string}
   */
  this.formatDateTime = function (duration) {
    var _this = this;
    var largeResult = [];
    var result = [];
    if (duration.days()) largeResult.push(duration.days() + '<span class="time-ind">' + _this.settings.translation.time.days + '</span>');
    if (duration.hours() || duration.days() > 0) {
      result.push(formatNumberLeadingZeros(duration.hours(), 2) + '<span class="time-ind">' + _this.settings.translation.time.hours + '</span>');
    } else result.push('00<span class="time-ind">' + _this.settings.translation.time.hours + '</span>');
    if (duration.minutes() || duration.hours() > 0 || duration.days() > 0) {
      result.push(formatNumberLeadingZeros(duration.minutes(), 2) + ((duration.days() > 0) ? '<span class="time-ind">' + _this.settings.translation.time.minutes + '</span>' : '<span class="time-ind">' + _this.settings.translation.time.minutesShortHand + '</span>'));
    } else (result.push('00' + ((duration.days() > 0) ? '<span class="time-ind">' + _this.settings.translation.time.minutes + '</span>' : '<span class="time-ind">' + _this.settings.translation.time.minutesShortHand + '</span>')));
    // if (duration.seconds() && duration.days() === 0){ result.push( formatNumberLeadingZeros(duration.seconds(), 2) + '<span class="time-ind">s</span>' ) }else if(duration.days() === 0){result.push( '00<span class="time-ind">s</span>' )};
    result.push(formatNumberLeadingZeros(duration.seconds(), 2) + '<span class="time-ind">' + _this.settings.translation.time.seconds + '</span>');
    return (largeResult.length > 0) ? (largeResult.join(' ') + ' ' + result.join(':')) : result.join(':');
  };

  this.populateIdenticonBase64Image = function (str) {
    if (str.length > 0) {
      /* eslint new-cap: "off" */
      var shaObj = new jsSHA('SHA-512', 'TEXT');
      shaObj.update(str);
      var hash = shaObj.getHash('HEX', 1);

      /**
       * for IE 11 comment out the lines above and use this code with the jsSHA library inside utils
       * import jsSHA from '../utils/jsSHA';
      var shaObj = new jsSHA(str, 'TEXT');
      var hash = shaObj.getHash('SHA-512', 'HEX', 1);
      */

      var data = new Identicon(hash, {
        background: [255, 255, 255, 255], // rgba white
        margin: 0.1, // 20% margin
        size: 22, // 420px square
        format: 'svg' // use SVG instead of PNG
      }).toString();

      var icon = 'data:image/svg+xml;base64,' + data;

      return icon;
    } else {
      return '';
    }
  };

  /**
   * get a list of available competition filtered by provided global criteria
   * @param callback {Function}
   */
  const competitionCheckAjax = new cLabs.Ajax();

  this.checkForAvailableCompetitions = function (callback, ajaxInstance) {
    var _this = this;
    var url = (_this.settings.memberId.length === 0) ? (
      _this.settings.uri.competitions.replace(':space', _this.settings.spaceName)
    ) : (
      _this.settings.uri.memberCompetitions.replace(':space', _this.settings.spaceName).replace(':id', _this.settings.memberId)
    );
    var filters = [
      'statusCode>==3',
      'statusCode<==5',
      '_sortByFields=options.scheduledDates.end:desc',
      ('_lang=' + _this.settings.language)
    ];
    var ajaxInstanceToUse = (typeof ajaxInstance !== 'undefined' && ajaxInstance !== null) ? ajaxInstance : competitionCheckAjax;

    if (typeof _this.settings.currency === 'string' && _this.settings.currency.length > 0) {
      filters.push('_uomKey=' + _this.settings.currency);
    }

    if (_this.settings.gameId.length > 0 && _this.settings.enforceGameLookup) {
      filters.push('options.products.productRefId=' + _this.settings.gameId);
    }

    if (_this.settings.groups.length > 0 && _this.settings.memberId.length === 0) {
      filters.push('options.limitEntrantsTo=' + _this.settings.groups);
    }

    ajaxInstanceToUse.abort().getData({
      type: 'GET',
      url: _this.settings.uri.gatewayDomain + url + '?' + filters.join('&'),
      headers: {
        'X-API-KEY': _this.settings.apiKey
      },
      success: function (response, dataObj, xhr) {
        if (xhr.status === 200) {
          var json = JSON.parse(response);

          _this.settings.partialFunctions.competitionDataAvailableResponseParser(json.data, function (compData) {
            _this.settings.tournaments.readyCompetitions = [];
            _this.settings.tournaments.activeCompetitions = [];

            mapObject(compData, function (comp) {
              if (comp.statusCode === 3) {
                _this.settings.tournaments.readyCompetitions.push(comp);
              } else if (comp.statusCode === 5) {
                _this.settings.tournaments.activeCompetitions.push(comp);
              }
            });

            _this.checkForFinishedCompetitions(callback, ajaxInstance);
          });
        } else {
          _this.log('failed to checkForActiveCompetitions ' + response);
        }
      }
    });
  };

  /**
   * get a list of finished competition filtered by provided global criteria
   * @param callback {Function}
   */
  const competitionFinishedCheckAjax = new cLabs.Ajax();

  this.checkForFinishedCompetitions = function (callback, ajaxInstance) {
    var _this = this;
    var url = (_this.settings.memberId.length === 0) ? (
      _this.settings.uri.competitions.replace(':space', _this.settings.spaceName)
    ) : (
      _this.settings.uri.memberCompetitions.replace(':space', _this.settings.spaceName).replace(':id', _this.settings.memberId)
    );
    var filters = [
      'statusCode=7',
      '_limit=10',
      '_sortByFields=options.scheduledDates.end:desc',
      ('_lang=' + _this.settings.language)
    ];
    var ajaxInstanceToUse = (typeof ajaxInstance !== 'undefined' && ajaxInstance !== null) ? ajaxInstance : competitionFinishedCheckAjax;

    if (typeof _this.settings.currency === 'string' && _this.settings.currency.length > 0) {
      filters.push('_uomKey=' + _this.settings.currency);
    }

    if (_this.settings.gameId.length > 0 && _this.settings.enforceGameLookup) {
      filters.push('options.products.productRefId=' + _this.settings.gameId);
    }

    if (_this.settings.groups.length > 0 && _this.settings.memberId.length === 0) {
      filters.push('options.limitEntrantsTo=' + _this.settings.groups);
    }

    ajaxInstanceToUse.abort().getData({
      type: 'GET',
      url: _this.settings.uri.gatewayDomain + url + '?' + filters.join('&'),
      headers: {
        'X-API-KEY': _this.settings.apiKey
      },
      success: function (response, dataObj, xhr) {
        if (xhr.status === 200) {
          var json = JSON.parse(response);

          _this.settings.partialFunctions.competitionDataFinishedResponseParser(json.data, function (compData) {
            _this.settings.tournaments.finishedCompetitions = [];

            mapObject(compData, function (comp) {
              if (comp.statusCode === 7) {
                _this.settings.tournaments.finishedCompetitions.push(comp);
              }
            });

            if (typeof callback === 'function') {
              callback();
            }
          });
        } else {
          _this.log('failed to checkForActiveCompetitions ' + response);
        }
      }
    });
  };

  this.prepareActiveCompetition = function (callback) {
    var _this = this;
    var activeCompetition = null;
    var activeCompetitionId = null;

    if (_this.settings.tournaments.activeCompetitionId !== null) {
      mapObject(_this.settings.tournaments.activeCompetitions, function (comp) {
        if (comp.id === _this.settings.tournaments.activeCompetitionId) {
          activeCompetition = comp;
        }
      });
      mapObject(_this.settings.tournaments.readyCompetitions, function (comp) {
        if (comp.id === _this.settings.tournaments.activeCompetitionId) {
          activeCompetition = comp;
        }
      });
      mapObject(_this.settings.tournaments.finishedCompetitions, function (comp) {
        if (comp.id === _this.settings.tournaments.activeCompetitionId) {
          activeCompetition = comp;
        }
      });

      if (activeCompetition !== null) {
        activeCompetitionId = _this.settings.tournaments.activeCompetitionId;
      } else {
        _this.settings.tournaments.activeCompetitionId = null;
      }
    }

    if (activeCompetition === null && _this.settings.tournaments.activeCompetitions.length > 0) {
      activeCompetition = _this.settings.tournaments.activeCompetitions[0];
      activeCompetitionId = activeCompetition.id;
    } else if (activeCompetition === null && _this.settings.tournaments.readyCompetitions.length > 0) {
      activeCompetition = _this.settings.tournaments.readyCompetitions[0];
      activeCompetitionId = activeCompetition.id;
    }

    if (activeCompetitionId === null) { // no active or ready competitions found
      _this.deactivateCompetitionsAndLeaderboards();
    } else {
      if (_this.settings.competition.activeCompetitionId !== activeCompetitionId && activeCompetitionId !== null) {
        _this.settings.competition.activeCompetition = activeCompetition;
        _this.settings.competition.activeCompetitionId = activeCompetitionId;
      }

      if (activeCompetitionId !== null) {
        _this.loadActiveCompetition(function (json) {
          _this.setActiveCompetition(json, callback);
        });
      } else if (typeof callback === 'function') {
        callback();
      }
    }
  };

  this.loadActiveCompetition = function (callback) {
    var _this = this;
    var url = (_this.settings.memberId.length === 0) ? (
      _this.settings.uri.competitionById.replace(':space', _this.settings.spaceName).replace(':id', _this.settings.competition.activeCompetitionId)
    ) : (
      _this.settings.uri.memberCompetitionById.replace(':space', _this.settings.spaceName).replace(':id', _this.settings.memberId).replace(':competitionId', _this.settings.competition.activeCompetitionId)
    );
    var filters = [
      ('_include=strategy' + (_this.settings.competition.includeMetadata ? ',metadata' : '')),
      ('_lang=' + _this.settings.language)
    ];

    if (typeof _this.settings.currency === 'string' && _this.settings.currency.length > 0) {
      filters.push('_uomKey=' + _this.settings.currency);
    }

    _this.settings.globalAjax.abort().getData({
      type: 'GET',
      url: _this.settings.uri.gatewayDomain + url + '?' + filters.join('&'),
      headers: {
        'X-API-KEY': _this.settings.apiKey
      },
      success: function (response, dataObj, xhr) {
        if (xhr.status === 200) {
          var json = JSON.parse(response);

          _this.settings.partialFunctions.activeCompetitionDataResponseParser(json, function (compData) {
            if (typeof callback === 'function') {
              callback(compData);
            }
          });
        } else {
          _this.log('failed to loadActiveCompetition ' + response);
        }
      }
    });
  };

  this.setActiveCompetition = function (json, callback) {
    var _this = this;

    _this.settings.competition.activeCompetition = json.data;
    _this.settings.competition.activeContest = null;
    _this.settings.competition.activeContestId = null;

    if (typeof json.data.contests !== 'undefined' && json.data.contests.length > 0) {
      _this.settings.partialFunctions.activeContestDataResponseParser(json.data.contests, function (contests) {
        mapObject(contests, function (contest) {
          if (contest.statusCode < 7 && _this.settings.competition.activeContest === null) {
            _this.settings.competition.activeContest = contest;
            _this.settings.competition.activeContestId = contest.id;

            if (typeof _this.settings.competition.activeContest.rewards === 'undefined') {
              _this.settings.competition.activeContest.rewards = [];
            }

            var rewards = [];
            mapObject(_this.settings.competition.activeContest.rewards, function (reward) {
              if (typeof reward.rewardRank === 'string') {
                var rankParts = reward.rewardRank.split(',');
                var rewardRank = [];

                mapObject(rankParts, function (part) {
                  if (stringContains(part, '-')) {
                    var rankRange = part.split('-');
                    var rageStart = parseInt(rankRange[0]);
                    var rangeEnd = parseInt(rankRange[1]);
                    for (var i = rageStart; i <= rangeEnd; i++) {
                      rewardRank.push(i);
                    }
                  } else {
                    rewardRank.push(parseInt(part));
                  }
                });

                reward.rewardRank = rewardRank;
              }

              rewards.push(reward);
            });

            _this.settings.competition.activeContest.rewards = rewards;
          }
        });
      });
    }

    if (typeof callback === 'function') {
      callback();
    }
  };

  this.getLeaderboardData = function (count, callback) {
    if (this.settings.competition.activeContestId !== null) {
      var _this = this;
      var url = _this.settings.uri.contestLeaderboard.replace(':space', _this.settings.spaceName).replace(':id', _this.settings.competition.activeContestId);
      var filters = [
        '_limit=' + count
      ];

      if (_this.settings.leaderboard.miniScoreBoard.enableRankings) {
        filters.push('rankings=' + _this.settings.leaderboard.miniScoreBoard.rankingsCount);
      }

      if (typeof _this.settings.memberId === 'string' && _this.settings.memberId.length > 0) {
        filters.push('memberId=' + _this.settings.memberId);
      }

      _this.settings.globalAjax.abort().getData({
        type: 'GET',
        url: _this.settings.uri.gatewayDomain + url + '?' + filters.join('&'),
        headers: {
          'X-API-KEY': _this.settings.apiKey
        },
        success: function (response, dataObj, xhr) {
          if (xhr.status === 200) {
            var json = JSON.parse(response);

            // if(
            //   typeof _this.settings.loadLeaderboardHistory[_this.settings.competition.activeContestId] === "undefined"
            //   ||
            //   (
            //     typeof _this.settings.loadLeaderboardHistory[_this.settings.competition.activeContestId] !== "undefined"
            //     &&
            //     _this.settings.loadLeaderboardHistory[_this.settings.competition.activeContestId] !== data
            //   )
            // ) {
            //   _this.settings.loadLeaderboardHistory[_this.settings.competition.activeContestId] = {
            //     changed: true,
            //     data: JSON.stringify(json.data)
            //   };
            // }

            _this.settings.partialFunctions.leaderboardDataResponseParser(json.data, function (lbData) {
              _this.settings.leaderboard.leaderboardData = lbData;

              callback(lbData);
            });
          } else {
            _this.log('failed to getLeaderboardData ' + response);
          }
        }
      });
    } else {
      callback();
    }
  };

  this.updateLeaderboardNavigationCounts = function () {
    var _this = this;

    if (_this.settings.mainWidget.settings.navigation !== null) {
      var menuItemCount = query(_this.settings.mainWidget.settings.navigation, '.' + _this.settings.navigation.tournaments.navigationClass + ' .cl-main-navigation-item-count');
      menuItemCount.innerHTML = _this.settings.tournaments.activeCompetitions.length;
    }
  };

  this.updateAchievementNavigationCounts = function () {
    var _this = this;

    if (_this.settings.mainWidget.settings.navigation !== null) {
      var menuItemCount = query(_this.settings.mainWidget.settings.navigation, '.' + _this.settings.navigation.achievements.navigationClass + ' .cl-main-navigation-item-count');
      menuItemCount.innerHTML = _this.settings.achievements.totalCount;
    }
  };

  this.updateRewardsNavigationCounts = function () {
    var _this = this;

    if (_this.settings.mainWidget.settings.navigation !== null) {
      var menuItemCount = query(_this.settings.mainWidget.settings.navigation, '.' + _this.settings.navigation.rewards.navigationClass + ' .cl-main-navigation-item-count');
      menuItemCount.innerHTML = _this.settings.rewards.availableRewards.length;
    }
  };

  this.updateMessagesNavigationCounts = function () {
    var _this = this;

    if (_this.settings.mainWidget.settings.navigation !== null) {
      var menuItemCount = query(_this.settings.mainWidget.settings.navigation, '.' + _this.settings.navigation.inbox.navigationClass + ' .cl-main-navigation-item-count');
      menuItemCount.innerHTML = _this.settings.messages.messages.length;
    }
  };

  var checkAchievementsAjax = new cLabs.Ajax();
  this.checkForAvailableAchievements = function (callback) {
    var _this = this;
    var url = _this.settings.uri.achievements.replace(':space', _this.settings.spaceName).replace(':id', _this.settings.memberId);
    // var date = new Date();
    // var createdDateFilter = date.toISOString();
    var filters = [
      '_limit=' + _this.settings.achievements.limit,
      '_include=rewards',
      // 'scheduledEnd>==' + createdDateFilter,
      ('_lang=' + _this.settings.language)
    ];
    var withGroups = false;

    if (typeof _this.settings.currency === 'string' && _this.settings.currency.length > 0) {
      filters.push('_uomKey=' + _this.settings.currency);
    }

    if (typeof _this.settings.member.groups !== 'undefined' && _this.settings.member.groups.length > 0) {
      withGroups = true;
      filters.push('memberGroups=' + _this.settings.member.groups.join(','));
    }

    // '&scheduledEnd>==' + createdDateFilter +
    checkAchievementsAjax.abort().getData({
      type: 'GET',
      url: _this.settings.uri.gatewayDomain + url + '?_lang=' + _this.settings.language + '&_uomKey=' + _this.settings.currency,
      headers: {
        'X-API-KEY': _this.settings.apiKey
      },
      success: function (response, dataObj, xhr) {
        if (xhr.status === 200) {
          var jsonData = JSON.parse(response);

          _this.settings.partialFunctions.achievementDataForAllResponseParser(jsonData, function (jsonForAll) {
            _this.settings.achievements.totalCount = parseInt(jsonForAll.meta.totalRecordsFound);
            _this.settings.achievements.list = [];

            mapObject(jsonForAll.data, function (ach) {
              _this.settings.achievements.list.push(ach);
            });

            if (withGroups) {
              checkAchievementsAjax.abort().getData({
                type: 'GET',
                url: _this.settings.uri.gatewayDomain + url + '?' + filters.join('&'),
                headers: {
                  'X-API-KEY': _this.settings.apiKey
                },
                success: function (response, dataObj, xhr) {
                  if (xhr.status === 200) {
                    var json = JSON.parse(response);

                    _this.settings.partialFunctions.achievementDataForMemberGroupResponseParser(json, function (achievmentMemberGroupData) {
                      mapObject(achievmentMemberGroupData.data, function (ach) {
                        var found = false;
                        mapObject(_this.settings.achievements.list, function (achCheck) {
                          if (achCheck.id === ach.id) {
                            found = true;
                          }
                        });

                        if (!found) {
                          _this.settings.achievements.list.push(ach);
                        }
                      });

                      _this.settings.achievements.totalCount = _this.settings.achievements.list.length;

                      if (typeof callback === 'function') callback(_this.settings.achievements.list);
                    });
                  } else {
                    _this.log('failed to checkForAvailableAchievements ' + response);
                  }
                }
              });
            } else {
              if (typeof callback === 'function') callback(jsonForAll.data);
            }
          });
        } else {
          _this.log('failed to checkForAvailableAchievements ' + response);
        }
      }
    });
  };

  var getAchievementsAjax = new cLabs.Ajax();
  this.getAchievement = function (achievementId, callback) {
    var _this = this;

    getAchievementsAjax.abort().getData({
      url: _this.settings.uri.gatewayDomain + _this.settings.uri.achievement.replace(':space', _this.settings.spaceName).replace(':id', achievementId) + '?_lang=' + _this.settings.language + '&_uomKey=' + _this.settings.currency,
      headers: {
        'X-API-KEY': _this.settings.apiKey
      },
      type: 'GET',
      success: function (response, dataObj, xhr) {
        var json = null;
        if (xhr.status === 200) {
          try {
            json = JSON.parse(response);
          } catch (e) {
          }
        }

        if (typeof callback === 'function') {
          _this.settings.partialFunctions.achievementDataResponseParser(json, function (achievementData) {
            callback(achievementData);
          });
        }
      },
      error: function () {
        if (typeof callback === 'function') {
          callback(null);
        }
      }
    });
  };

  var getRewardAjax = new cLabs.Ajax();
  this.getReward = function (rewardId, callback) {
    var _this = this;

    getRewardAjax.abort().getData({
      url: _this.settings.uri.gatewayDomain + _this.settings.uri.memberReward.replace(':space', _this.settings.spaceName).replace(':id', _this.settings.memberId).replace(':awardId', rewardId),
      headers: {
        'X-API-KEY': _this.settings.apiKey
      },
      type: 'GET',
      success: function (response, dataObj, xhr) {
        var json = null;
        if (xhr.status === 200) {
          try {
            json = JSON.parse(response);
          } catch (e) {
          }
        }

        if (typeof callback === 'function') {
          _this.settings.partialFunctions.rewardDataResponseParser(json, function (rewardData) {
            callback(rewardData);
          });
        }
      },
      error: function () {
        if (typeof callback === 'function') {
          callback(null);
        }
      }
    });
  };

  var getMessageAjax = new cLabs.Ajax();
  this.getMessage = function (messageId, callback) {
    var _this = this;

    getMessageAjax.abort().getData({
      url: _this.settings.uri.gatewayDomain + _this.settings.uri.messageById.replace(':space', _this.settings.spaceName).replace(':id', _this.settings.memberId).replace(':messageId', messageId),
      headers: {
        'X-API-KEY': _this.settings.apiKey
      },
      type: 'GET',
      success: function (response, dataObj, xhr) {
        var json = null;
        if (xhr.status === 200) {
          try {
            json = JSON.parse(response);
          } catch (e) {
          }
        }

        if (typeof callback === 'function') {
          _this.settings.partialFunctions.messageDataResponseParser(json, function (messageData) {
            callback(messageData);
          });
        }
      },
      error: function () {
        if (typeof callback === 'function') {
          callback(null);
        }
      }
    });
  };

  var claimRewardAjax = new cLabs.Ajax();
  this.claimReward = function (rewardId, callback) {
    var _this = this;

    claimRewardAjax.abort().getData({
      url: _this.settings.uri.gatewayDomain + _this.settings.uri.memberRewardClaim.replace(':space', _this.settings.spaceName).replace(':id', _this.settings.memberId).replace(':awardId', rewardId),
      headers: {
        'X-API-KEY': _this.settings.apiKey
      },
      type: 'POST',
      success: function (response, dataObj, xhr) {
        var json = null;
        if (xhr.status === 200) {
          try {
            json = JSON.parse(response);
          } catch (e) {
          }
        }

        if (typeof callback === 'function') {
          _this.settings.partialFunctions.claimRewardDataResponseParser(json, function (claimRewardData) {
            callback(claimRewardData);
          });
        }
      },
      error: function () {
        if (typeof callback === 'function') {
          callback(null);
        }
      }
    });
  };

  var checkForMemberAchievementsAjax = new cLabs.Ajax();
  this.checkForMemberAchievementsIssued = function (callback) {
    var _this = this;
    var url = _this.settings.uri.achievementsIssued.replace(':space', _this.settings.spaceName).replace(':id', _this.settings.memberId);

    checkForMemberAchievementsAjax.abort().getData({
      type: 'GET',
      url: _this.settings.uri.gatewayDomain + url,
      headers: {
        'X-API-KEY': _this.settings.apiKey
      },
      success: function (response, dataObj, xhr) {
        if (xhr.status === 200) {
          var json = JSON.parse(response);

          _this.settings.partialFunctions.issuedAchievementsDataResponseParser(json, function (issuedAchievementsData) {
            var idList = [];

            if (typeof issuedAchievementsData.aggregations !== 'undefined' && issuedAchievementsData.aggregations.length > 0) {
              mapObject(issuedAchievementsData.aggregations[0].items, function (item) {
                idList.push(item.value);
              });
            }

            if (typeof callback === 'function') callback(idList);
          });
        } else {
          _this.log('failed to checkForMemberAchievementsIssued ' + response);
        }
      }
    });
  };

  var checkForMemberAchievementsProgressionAjax = new cLabs.Ajax();
  this.checkForMemberAchievementsProgression = function (idList, callback) {
    var _this = this;
    var url = _this.settings.uri.achievementsProgression.replace(':space', _this.settings.spaceName).replace(':id', _this.settings.memberId);

    checkForMemberAchievementsProgressionAjax.abort().getData({
      type: 'GET',
      url: _this.settings.uri.gatewayDomain + url + (idList.length > 0 ? ('?id=' + idList.join(',')) : ''),
      headers: {
        'X-API-KEY': _this.settings.apiKey
      },
      success: function (response, dataObj, xhr) {
        if (xhr.status === 200) {
          var json = JSON.parse(response);

          if (typeof callback === 'function') {
            _this.settings.partialFunctions.memberAchievementsProgressionDataResponseParser(json.data, function (memberAchievementsProgressionData) {
              callback(memberAchievementsProgressionData);
            });
          }
        } else {
          _this.log('failed to checkForMemberAchievementsProgression ' + response);
        }
      }
    });
  };

  var checkForAvailableRewardsAjax = new cLabs.Ajax();
  this.checkForAvailableRewards = function (callback) {
    var _this = this;
    var url = _this.settings.uri.messages.replace(':space', _this.settings.spaceName).replace(':id', _this.settings.memberId);

    // claimed rewards
    checkForAvailableRewardsAjax.abort().getData({
      type: 'GET',
      url: _this.settings.uri.gatewayDomain + url + '?_sortByFields=created:desc&messageType=Reward&prize.claimed=true&_hasValuesFor=prize&_limit=100',
      headers: {
        'X-API-KEY': _this.settings.apiKey
      },
      success: function (response, dataObj, xhr) {
        if (xhr.status === 200) {
          var jsonClaimedPrizes = JSON.parse(response);

          _this.settings.rewards.rewards = [];
          _this.settings.rewards.availableRewards = [];
          _this.settings.rewards.expiredRewards = [];

          _this.settings.partialFunctions.claimedRewardsDataResponseParser(jsonClaimedPrizes.data, function (claimedRewardsData) {
            mapObject(claimedRewardsData, function (message) {
              var expired = (typeof message.expiry === 'undefined') ? false : (moment(message.expiry).diff(moment()) < 0);

              if (!expired) {
                _this.settings.rewards.rewards.push(message);
              }
            });

            // not-claimed rewards
            checkForAvailableRewardsAjax.abort().getData({
              type: 'GET',
              url: _this.settings.uri.gatewayDomain + url + '?_sortByFields=created:desc&messageType=Reward&prize.claimed=false&_hasValuesFor=prize&_limit=100',
              headers: {
                'X-API-KEY': _this.settings.apiKey
              },
              success: function (response, dataObj, xhr) {
                if (xhr.status === 200) {
                  var jsonNotClaimed = JSON.parse(response);

                  _this.settings.partialFunctions.notClaimedRewardsDataResponseParser(jsonNotClaimed.data, function (notClaimedRewardsData) {
                    mapObject(notClaimedRewardsData, function (message) {
                      var expired = (typeof message.expiry === 'undefined') ? false : (moment(message.expiry).diff(moment()) < 0);

                      if (!expired) {
                        _this.settings.rewards.availableRewards.push(message);
                      }
                    });

                    // expired rewards
                    var date = new Date();
                    var utcDate = date.getUTCFullYear() + '-' + formatNumberLeadingZeros((date.getUTCMonth() + 1), 2) + '-' + formatNumberLeadingZeros(date.getUTCDate(), 2) + 'T' + formatNumberLeadingZeros(date.getUTCHours(), 2) + ':' + formatNumberLeadingZeros(date.getUTCMinutes(), 2) + ':00';
                    checkForAvailableRewardsAjax.abort().getData({
                      type: 'GET',
                      url: _this.settings.uri.gatewayDomain + url + '?_sortByFields=created:desc&_limit=100&messageType=Reward&_hasValuesFor=expiry&expiry<==' + utcDate,
                      headers: {
                        'X-API-KEY': _this.settings.apiKey
                      },
                      success: function (response, dataObj, xhr) {
                        if (xhr.status === 200) {
                          var jsonExpiredRewards = JSON.parse(response);

                          _this.settings.partialFunctions.expiredRewardsDataResponseParser(jsonExpiredRewards.data, function (expiredRewardsData) {
                            mapObject(expiredRewardsData, function (message) {
                              _this.settings.rewards.expiredRewards.push(message);
                            });

                            if (typeof callback === 'function') callback(_this.settings.rewards.rewards, _this.settings.rewards.availableRewards, _this.settings.rewards.expiredRewards);
                          });
                        } else {
                          _this.log('failed to checkForAvailableRewards expired ' + response);
                        }
                      }
                    });
                  });
                } else {
                  _this.log('failed to checkForAvailableRewards not-claimed ' + response);
                }
              }
            });
          });
        } else {
          _this.log('failed to checkForAvailableRewards claimed ' + response);
        }
      }
    });
  };

  var checkForAvailableMessagesAjax = new cLabs.Ajax();
  this.checkForAvailableMessages = function (callback) {
    var _this = this;
    var url = _this.settings.uri.messages.replace(':space', _this.settings.spaceName).replace(':id', _this.settings.memberId);
    var date = new Date();

    date.setDate(date.getMonth() - 1);

    var createdDateFilter = date.getFullYear() + '-' + formatNumberLeadingZeros((date.getMonth() + 1), 2) + '-' + formatNumberLeadingZeros(date.getDate(), 2);

    checkForAvailableMessagesAjax.abort().getData({
      type: 'GET',
      url: _this.settings.uri.gatewayDomain + url + '?_sortByFields=created:desc&_hasNoValuesFor=prize&_limit=100&created>==' + createdDateFilter,
      headers: {
        'X-API-KEY': _this.settings.apiKey
      },
      success: function (response, dataObj, xhr) {
        if (xhr.status === 200) {
          var jsonAvailableMessages = JSON.parse(response);

          _this.settings.partialFunctions.availableMessagesDataResponseParser(jsonAvailableMessages.data, function (availableMessagesData) {
            _this.settings.messages.messages = [];

            mapObject(availableMessagesData, function (message) {
              _this.settings.messages.messages.push(message);
            });

            if (typeof callback === 'function') callback(_this.settings.messages.messages);
          });
        } else {
          _this.log('failed to checkForAvailableMessages ' + response);
        }
      }
    });
  };

  var optInMemberAjax = new cLabs.Ajax();
  this.optInMemberToActiveCompetition = function (callback) {
    var _this = this;
    var url = _this.settings.uri.memberCompetitionOptIn.replace(':space', _this.settings.spaceName).replace(':id', _this.settings.memberId).replace(':competitionId', _this.settings.competition.activeCompetitionId);

    optInMemberAjax.abort().getData({
      type: 'GET',
      url: _this.settings.uri.gatewayDomain + url,
      headers: {
        'X-API-KEY': _this.settings.apiKey
      },
      success: function (response, dataObj, xhr) {
        if (xhr.status === 200) {
          callback();
        } else {
          _this.log('failed to optInMemberToActiveCompetition ' + response);
        }
      }
    });
  };

  var revalidationCount = 0;
  this.revalidateIfSuccessfullOptIn = function (callback) {
    var _this = this;

    _this.loadActiveCompetition(function (competitionJson) {
      if (typeof competitionJson.data.optin === 'boolean' && !competitionJson.data.optin) {
        revalidationCount++;

        if (revalidationCount < 5) {
          setTimeout(function () {
            _this.revalidateIfSuccessfullOptIn(callback);
          }, 100);
        } else {
          revalidationCount = 0;
        }
      } else if (typeof competitionJson.data.optin === 'boolean' && competitionJson.data.optin) {
        callback(competitionJson);
      }
    });
  };

  this.leaderboardDataRefresh = function () {
    var _this = this;

    if (_this.settings.leaderboard.refreshLbDataInterval) {
      clearTimeout(_this.settings.leaderboard.refreshLbDataInterval);
    }

    if (
      (_this.settings.competition.activeCompetition !== null && typeof _this.settings.competition.activeCompetition.optinRequired === 'boolean' && !_this.settings.competition.activeCompetition.optinRequired) ||
      (typeof _this.settings.competition.activeCompetition.optin === 'boolean' && _this.settings.competition.activeCompetition.optin)
    ) {
      var count = (_this.settings.miniScoreBoard.settings.active) ? 0 : _this.settings.leaderboard.fullLeaderboardSize;
      _this.getLeaderboardData(count, function (data) {
        if (_this.settings.miniScoreBoard.settings.active) _this.settings.miniScoreBoard.loadScoreBoard();
        if (_this.settings.mainWidget.settings.active) _this.settings.mainWidget.loadLeaderboard();
      });
    }

    _this.settings.leaderboard.refreshLbDataInterval = setTimeout(function () {
      _this.leaderboardDataRefresh();
    }, _this.settings.leaderboard.refreshIntervalMillis);
  };

  this.activeDataRefresh = function (callback) {
    var _this = this;

    if (_this.settings.competition.refreshInterval) {
      clearTimeout(_this.settings.competition.refreshInterval);
    }

    _this.checkForAvailableCompetitions(function () {
      _this.updateLeaderboardNavigationCounts();

      _this.prepareActiveCompetition(function () {
        var count = (_this.settings.miniScoreBoard.settings.active) ? 0 : _this.settings.leaderboard.fullLeaderboardSize;

        // clear to not clash with LB refresh that could happen at same time
        if (_this.settings.leaderboard.refreshInterval) {
          clearTimeout(_this.settings.leaderboard.refreshInterval);
        }

        if (_this.settings.miniScoreBoard.settings.active || _this.settings.mainWidget.settings.active) {
          if (
            (_this.settings.competition.activeCompetition !== null && typeof _this.settings.competition.activeCompetition.optinRequired === 'boolean' && !_this.settings.competition.activeCompetition.optinRequired) ||
            (_this.settings.competition.activeCompetition !== null && typeof _this.settings.competition.activeCompetition.optin === 'boolean' && _this.settings.competition.activeCompetition.optin)
          ) {
            _this.getLeaderboardData(count, function (data) {
              if (_this.settings.miniScoreBoard.settings.active) _this.settings.miniScoreBoard.loadScoreBoard();
              if (_this.settings.mainWidget.settings.active) _this.settings.mainWidget.loadLeaderboard();

              // re-start leaderboard refresh
              _this.leaderboardDataRefresh();

              if (typeof callback === 'function') {
                callback();
              }
            });
          } else {
            if (_this.settings.miniScoreBoard.settings.active) _this.settings.miniScoreBoard.loadScoreBoard();
            if (_this.settings.mainWidget.settings.active) {
              _this.getLeaderboardData(count, function (data) {
                _this.settings.mainWidget.loadLeaderboard();
              });
            }

            // restart leaderboard refresh
            _this.leaderboardDataRefresh();

            if (typeof callback === 'function') {
              callback();
            }
          }
        } else {
          if (_this.settings.miniScoreBoard.settings.active) _this.settings.miniScoreBoard.loadScoreBoard();

          if (typeof callback === 'function') {
            callback();
          }
        }
      });
    });

    _this.settings.competition.refreshInterval = setTimeout(function () {
      _this.activeDataRefresh();
    }, _this.settings.competition.refreshIntervalMillis);
  };

  this.deactivateCompetitionsAndLeaderboards = function (callback) {
    var _this = this;

    if (_this.settings.leaderboard.refreshInterval) {
      clearTimeout(_this.settings.leaderboard.refreshInterval);
    }

    _this.settings.miniScoreBoard.clearAll();
    _this.settings.mainWidget.clearAll();

    if (typeof callback === 'function') {
      callback();
    }
  };

  this.stopActivity = function (callback) {
    var _this = this;

    if (_this.settings.leaderboard.refreshInterval) {
      clearTimeout(_this.settings.leaderboard.refreshInterval);
      clearInterval(_this.settings.leaderboard.refreshInterval);
    }

    if (_this.settings.competition.refreshInterval) {
      clearTimeout(_this.settings.competition.refreshInterval);
      clearInterval(_this.settings.competition.refreshInterval);
    }

    if (_this.settings.leaderboard.refreshLbDataInterval) {
      clearTimeout(_this.settings.leaderboard.refreshLbDataInterval);
      clearInterval(_this.settings.leaderboard.refreshLbDataInterval);
    }

    if (_this.settings.miniScoreBoard.settings.updateInterval) {
      clearTimeout(_this.settings.miniScoreBoard.settings.updateInterval);
      clearInterval(_this.settings.leaderboard.refreshInterval);
    }

    if (typeof callback === 'function') {
      callback();
    }
  };

  this.restartActivity = function (callback) {
    var _this = this;

    _this.activeDataRefresh();
    _this.settings.miniScoreBoard.updateScoreBoard();

    if (typeof callback === 'function') {
      callback();
    }
  };

  this.loadMember = function (callback) {
    var _this = this;

    _this.settings.globalAjax.abort().getData({
      type: 'GET',
      url: _this.settings.uri.gatewayDomain + _this.settings.uri.members.replace(':space', _this.settings.spaceName).replace(':id', _this.settings.memberId),
      headers: {
        'X-API-KEY': _this.settings.apiKey
      },
      success: function (response, dataObj, xhr) {
        if (xhr.status === 200) {
          var json = JSON.parse(response);

          _this.settings.member = json.data;

          callback(json.data);
        } else {
          _this.log('failed to loadMember ' + response);
        }
      }
    });
  };

  this.loadWidgetTranslations = function (callback) {
    var _this = this;

    if (typeof _this.settings.uri.translationPath === 'string' && _this.settings.uri.translationPath.length > 0 && _this.settings.loadTranslations) {
      var url = (stringContains(_this.settings.uri.translationPath, 'http')) ? _this.settings.uri.translationPath.replace(':language', _this.settings.language) : _this.settings.uri.gatewayDomain + _this.settings.uri.translationPath.replace(':language', _this.settings.language);

      _this.settings.globalAjax.abort().getData({
        type: 'GET',
        url: url,
        headers: {
          'X-API-KEY': _this.settings.apiKey
        },
        success: function (response, dataObj, xhr) {
          if (xhr.status === 200) {
            var json = JSON.parse(response);

            _this.settings.translation = mergeObjects(_this.settings.translation, json);

            callback();
          } else {
            _this.log('no translation foound ' + response);

            callback();
          }
        }
      });
    } else {
      callback();
    }
  };

  this.startup = function () {
    var _this = this;

    _this.settings.miniScoreBoard.initLayout(function () {
      _this.settings.miniScoreBoard.settings.active = true;
      _this.activeDataRefresh(function () {
        _this.settings.partialFunctions.startupCallback(_this);
      });

      if (_this.settings.enableNotifications) {
        _this.settings.notifications.init();
        _this.settings.canvasAnimation.init();
      }

      _this.cleanup();
    });
  };

  var _cleanupInstance;
  this.cleanup = function () {
    var _this = this;

    if (_cleanupInstance) {
      clearTimeout(_cleanupInstance);
    }

    _cleanupInstance = setTimeout(function () {
      _this.settings.mainWidget.preLoaderRerun();

      _this.cleanup();
    }, 3000);
  };

  this.loadStylesheet = function (callback) {
    var _this = this;
    var createdResources = false;
    var availableLinks = [];

    objectIterator(query('link'), function (link) {
      if (link !== null) {
        availableLinks.push(new URL(link.href, document.baseURI).href);
      }
    });

    mapObject(_this.settings.resources, function (resource, key, count) {
      var exists = false;

      mapObject(availableLinks, function (link) {
        if (link === new URL(resource, document.baseURI).href) {
          exists = true;
        }
      });

      if (!exists) {
        var link = document.createElement('link');
        link.setAttribute('rel', 'stylesheet');
        link.setAttribute('type', 'text/css');
        link.setAttribute('href', resource);

        if (count === 0) {
          link.onload = function () {
            if (typeof callback === 'function') {
              callback();
            }
          };

          link.onerror = function (e) {
            if (typeof callback === 'function') {
              callback();
            }
          };
        }

        document.body.appendChild(link);

        createdResources = true;
      }
    });

    if (!createdResources && typeof callback === 'function') {
      callback();
    }
  };

  this.clickedMiniScoreBoard = function () {
    var _this = this;

    if (!_this.settings.miniScoreBoard.settings.dragging) {
      _this.deactivateCompetitionsAndLeaderboards(function () {
        _this.settings.leaderboard.leaderboardData = [];
        _this.settings.mainWidget.initLayout(function () {
          // load tournaments data
          if (_this.settings.navigation.tournaments.enable) {
            _this.activeDataRefresh();
          }

          // load achievement data
          if (_this.settings.navigation.achievements.enable) {
            _this.checkForAvailableAchievements(function (achievementData) {
              _this.updateAchievementNavigationCounts();
            });
          }

          // load initial available reward data
          if (_this.settings.navigation.rewards.enable) {
            _this.checkForAvailableRewards(function () {
              _this.updateRewardsNavigationCounts();
            });
          }

          // load initial available messages data
          if (_this.settings.navigation.inbox.enable) {
            _this.checkForAvailableMessages(function () {
              _this.updateMessagesNavigationCounts();
            });
          }
        });
        setTimeout(function () {
          _this.settings.miniScoreBoard.settings.container.style.display = 'none';
        }, 200);
      });
    }
  };

  /**
   * Open main widget and open specific tab and loads relevant action
   * @memberOf LbWidget
   * @param tab String
   * @param actionCallback Function
   */
  this.openWithTabAndAction = function (tab, actionCallback) {
    var _this = this;

    if (_this.settings.mainWidget.settings.active) {
      var loadTab = query(_this.settings.mainWidget.settings.container, tab);
      _this.settings.mainWidget.navigationSwitch(loadTab, function () {
        _this.activeDataRefresh();

        if (typeof actionCallback === 'function') {
          actionCallback();
        }
      });

      setTimeout(function () {
        _this.settings.miniScoreBoard.settings.container.style.display = 'none';
      }, 200);
    } else {
      _this.deactivateCompetitionsAndLeaderboards(function () {
        _this.settings.mainWidget.initLayout(function () {
          _this.settings.mainWidget.navigationSwitch(query(_this.settings.mainWidget.settings.container, tab), function () {
            _this.activeDataRefresh();

            if (typeof actionCallback === 'function') {
              actionCallback();
            }
          });
        });
        setTimeout(function () {
          _this.settings.miniScoreBoard.settings.container.style.display = 'none';
        }, 200);
      });
    }
  };

  var loadCompetitionListAjax = new cLabs.Ajax();
  this.eventHandlers = function (el) {
    var _this = this;

    // mini scoreboard opt-in action
    if (hasClass(el, 'cl-widget-ms-optin-action') && !hasClass(el, 'checking')) {
      addClass(el, 'checking');

      _this.optInMemberToActiveCompetition(function () {
        _this.revalidateIfSuccessfullOptIn(function (competitionJson) {
          _this.settings.competition.activeCompetition = competitionJson.data;

          // _this.getLeaderboardData(1, function( data ){
          //  _this.settings.miniScoreBoard.loadScoreBoard( data );
          // });

          // extra action to load competition details on mini scoreboard opt-in - Product request
          _this.deactivateCompetitionsAndLeaderboards(function () {
            _this.settings.leaderboard.leaderboardData = [];
            _this.settings.mainWidget.initLayout(function () {
              _this.activeDataRefresh();

              _this.settings.mainWidget.loadCompetitionDetails(function () {

              });
            });
            setTimeout(function () {
              _this.settings.miniScoreBoard.settings.container.style.display = 'none';
            }, 200);
          });

          removeClass(el, 'checking');
        });
      });

      // Leaderboard details opt-in action
    } else if (hasClass(el, 'cl-main-widget-lb-details-optin-action') && !hasClass(el, 'checking')) {
      addClass(el, 'checking');

      _this.optInMemberToActiveCompetition(function () {
        _this.revalidateIfSuccessfullOptIn(function (competitionJson) {
          _this.settings.competition.activeCompetition = competitionJson.data;
          _this.settings.mainWidget.competitionDetailsOptInButtonState();

          removeClass(el, 'checking');
        });
      });

      // Leaderboard details opt-in action
    } else if (hasClass(el, 'cl-main-widget-lb-optin-action') && !hasClass(el, 'checking')) {
      addClass(el, 'checking');

      _this.optInMemberToActiveCompetition(function () {
        _this.revalidateIfSuccessfullOptIn(function (competitionJson) {
          _this.settings.competition.activeCompetition = competitionJson.data;

          _this.settings.mainWidget.loadCompetitionDetails(function () {
          });

          removeClass(el, 'checking');
          el.parentNode.style.display = 'none';
        });
      });

      // close mini scoreboard info area
    } else if (hasClass(el, 'cl-widget-ms-information-close') && !hasClass(el, 'checking')) {
      _this.settings.miniScoreBoard.clearAll();

      // close notification window
    } else if (hasClass(el, 'cl-widget-notif-information-close') && !hasClass(el, 'checking')) {
      _this.settings.notifications.hideNotification();

      // close leaderboard window
    } else if (hasClass(el, 'cl-main-widget-lb-header-close') || hasClass(el, 'cl-main-widget-ach-header-close') || hasClass(el, 'cl-main-widget-reward-header-close') || hasClass(el, 'cl-main-widget-inbox-header-close') || hasClass(el, 'cl-widget-main-widget-overlay-wrapper')) {
      _this.settings.mainWidget.hide(function () {
        _this.settings.miniScoreBoard.settings.active = true;
        _this.settings.miniScoreBoard.settings.container.style.display = 'block';

        _this.activeDataRefresh();
      });

      // load embedded competition details
    } else if (!_this.settings.leaderboard.layoutSettings.titleLinkToDetailsPage && (hasClass(el, 'cl-main-widget-lb-details-content-label') || closest(el, '.cl-main-widget-lb-details-content-label') !== null)) {
      _this.settings.mainWidget.showEmbeddedCompetitionDetailsContent(function () {});

      // hide embedded competition details
    } else if (!_this.settings.leaderboard.layoutSettings.titleLinkToDetailsPage && hasClass(el, 'cl-main-widget-lb-details-description-close')) {
      _this.settings.mainWidget.hideEmbeddedCompetitionDetailsContent(function () {});

      // load competition details
    } else if (hasClass(el, 'cl-main-widget-lb-details-content-label') || closest(el, '.cl-main-widget-lb-details-content-label') !== null) {
      if (_this.settings.competition.activeContest !== null) {
        _this.settings.mainWidget.loadCompetitionDetails(function () {
        });
      }

      // load achievement details
    } else if (hasClass(el, 'cl-ach-list-more')) {
      _this.getAchievement(el.dataset.id, function (data) {
        _this.settings.mainWidget.loadAchievementDetails(data, function () {
        });
      });

      // leaderboard details back button
    } else if (hasClass(el, 'cl-main-widget-lb-details-back-btn')) {
      _this.settings.mainWidget.hideCompetitionDetails();

      // achievements details back button
    } else if (hasClass(el, 'cl-main-widget-ach-details-back-btn')) {
      _this.settings.mainWidget.hideAchievementDetails(function () {
      });

      // rewards details back button
    } else if (hasClass(el, 'cl-main-widget-reward-details-back-btn')) {
      _this.settings.mainWidget.hideRewardDetails(function () {
      });

      // messages details back button
    } else if (hasClass(el, 'cl-main-widget-inbox-details-back-btn')) {
      _this.settings.mainWidget.hideMessageDetails(function () {
      });

      // load rewards details
    } else if (hasClass(el, 'cl-rew-list-item') || closest(el, '.cl-rew-list-item') !== null) {
      var rewardId = (hasClass(el, 'cl-rew-list-item')) ? el.dataset.rewardId : closest(el, '.cl-rew-list-item').dataset.rewardId;
      _this.getReward(rewardId, function (data) {
        _this.settings.mainWidget.loadRewardDetails(data, function () {
        });
      });

      // load inbox details
    } else if (hasClass(el, 'cl-inbox-list-item') || closest(el, '.cl-inbox-list-item') !== null) {
      var messageId = (hasClass(el, 'cl-inbox-list-item')) ? el.dataset.rewardId : closest(el, '.cl-inbox-list-item').dataset.id;
      _this.getMessage(messageId, function (data) {
        _this.settings.mainWidget.loadMessageDetails(data, function () {
        });
      });

      // claim reward
    } else if (hasClass(el, 'cl-main-widget-reward-claim-btn')) {
      _this.claimReward(el.dataset.id, function (data) {
        if (data.data.claimed) {
          _this.settings.mainWidget.loadRewards();

          addClass(el, 'cl-claimed');
          el.innerHTML = _this.settings.translation.rewards.claimed;
        } else {
          removeClass(el, 'cl-claimed');
          el.innerHTML = _this.settings.translation.rewards.claim;
        }
      });

      // load achievement details window from notification window
    } else if (hasClass(el, 'cl-widget-notif-information-details-wrapper') || closest(el, '.cl-widget-notif-information-details-wrapper') !== null) {
      _this.openWithTabAndAction('.cl-main-widget-navigation-ach-icon', function () {
        var id = (hasClass(el, 'cl-widget-notif-information-details-wrapper')) ? el.dataset.id : closest(el, '.cl-widget-notif-information-details-wrapper').dataset.id;
        _this.settings.notifications.hideNotification();
        _this.settings.mainWidget.hideAchievementDetails(function () {
          _this.getAchievement(id, function (data) {
            _this.settings.mainWidget.loadAchievementDetails(data);
          });
        });
      });

      // primary widget navigation
    } else if (hasClass(el, 'cl-main-navigation-item')) {
      _this.settings.mainWidget.navigationSwitch(el);

      // competition list
    } else if (hasClass(el, 'cl-main-widget-lb-header-list-icon')) {
      if (_this.settings.leaderboard.refreshInterval) {
        clearTimeout(_this.settings.leaderboard.refreshInterval);
      }
      _this.settings.mainWidget.loadCompetitionList(function () {
        _this.activeDataRefresh();
      }, loadCompetitionListAjax);

      // load competition
    } else if (hasClass(el, 'cl-tour-list-item') || closest(el, '.cl-tour-list-item') !== null) {
      var tournamentId = (hasClass(el, 'cl-tour-list-item')) ? el.dataset.id : closest(el, '.cl-tour-list-item').dataset.id;
      var preLoader = _this.settings.mainWidget.preloader();

      preLoader.show(function () {
        _this.settings.mainWidget.settings.active = true;
        _this.settings.tournaments.activeCompetitionId = tournamentId;
        _this.activeDataRefresh(function () {
          _this.settings.mainWidget.hideCompetitionList(function () {
            if (!_this.settings.leaderboard.layoutSettings.titleLinkToDetailsPage) {
              _this.settings.mainWidget.showEmbeddedCompetitionDetailsContent(function () {});
            } else if (_this.settings.competition.activeContest !== null) {
              _this.settings.mainWidget.loadCompetitionDetails(function () {});
            }

            preLoader.hide();
          });
        });
      });

      // hide competition list view
    } else if (hasClass(el, 'cl-main-widget-tournaments-back-btn')) {
      _this.settings.mainWidget.hideCompetitionList();

      // mini scoreboard action to open primary widget
    } else if ((hasClass(el, 'cl-widget-ms-icon-wrapper') || closest(el, '.cl-widget-ms-icon-wrapper') !== null) || (hasClass(el, 'cl-widget-ms-information-wrapper') || closest(el, '.cl-widget-ms-information-wrapper') !== null)) {
      _this.clickedMiniScoreBoard();

      // accordion navigation
    } else if (hasClass(el, 'cl-accordion-label')) {
      _this.settings.mainWidget.accordionNavigation(el);
    }
  };

  this.eventListeners = function () {
    var _this = this;

    document.body.addEventListener('keyup', function (event) {
      switch (event.keyCode) {
        case 27: // on escape
          if (_this.settings.mainWidget.settings.active) {
            _this.settings.mainWidget.hide(function () {
              _this.settings.miniScoreBoard.settings.active = true;
              _this.settings.miniScoreBoard.settings.container.style.display = 'block';

              _this.activeDataRefresh();
            });
          }
          break;
      }
    });

    if (_this.isMobile()) {
      document.body.addEventListener('touchend', function (event) {
        var el = event.target;

        if (!_this.settings.miniScoreBoard.settings.dragging) {
          _this.eventHandlers(el);
        }
      });
    } else {
      document.body.addEventListener('click', function (event) {
        var el = event.target;

        _this.eventHandlers(el);
      });
    }
  };

  this.closeEverything = function () {
    var _this = this;

    _this.deactivateCompetitionsAndLeaderboards(function () {
      _this.settings.leaderboard.leaderboardData = [];
      setTimeout(function () {
        _this.settings.miniScoreBoard.settings.container.style.display = 'none';
      }, 200);
    });

    _this.settings.mainWidget.hide();
    _this.settings.mainWidget.settings.preLoader.preLoaderActive = false;
  };

  var restartReloadInterval;
  this.restart = function () {
    var _this = this;

    _this.settings.mainWidget.hide(() => {
      _this.deactivateCompetitionsAndLeaderboards(() => {
        _this.stopActivity(() => {
          _this.loadMember((member) => {
            _this.loadWidgetTranslations(() => {
              if (restartReloadInterval) {
                clearTimeout(restartReloadInterval);
              }
              _this.settings.mainWidget.destroyLayout();

              restartReloadInterval = setTimeout(function () {
                _this.settings.miniScoreBoard.settings.active = true;
                _this.settings.miniScoreBoard.settings.container.style.display = 'block';
                _this.startup();
              }, 300);
            });
          });
        });
      });
    });
  };

  this.isMobile = function () {
    return isMobileTablet();
  };

  this.applyAppearance = function () {
    if (this.settings.styles !== null) {
      const styles = Object.keys(this.settings.styles).reduce((accumulator, currentValue) => {
        return {
          ...accumulator,
          [`--lb3-${camelToKebabCase(currentValue)}`]: this.settings.styles[currentValue]
        };
      }, {});

      cssVars({
        include: 'link[rel=stylesheet],style',
        watch: true,
        onlyLegacy: false,
        variables: { ...styles }
      });
    }
  };

  /**
   * Init LbWidget method
   * @method
   * @memberOf LbWidget
   * @return {undefined}
   */
  this.init = function () {
    this.loadStylesheet(() => {
      this.applyAppearance();

      this.loadMember((member) => {
        this.loadWidgetTranslations(() => {
          if (this.settings.miniScoreBoard === null) {
            this.settings.canvasAnimation = new CanvasAnimation();
            this.settings.notifications = new Notifications({
              canvasInstance: this.settings.canvasAnimation
            });
            this.settings.miniScoreBoard = new MiniScoreBoard({
              active: true
            });
            this.settings.mainWidget = new MainWidget();

            this.settings.notifications.settings.lbWidget = this;
            this.settings.miniScoreBoard.settings.lbWidget = this;
            this.settings.mainWidget.settings.lbWidget = this;
            this.settings.canvasAnimation.settings.lbWidget = this;

            this.startup();
            this.eventListeners();
          } else {
            this.settings.mainWidget.hide(() => {
              this.deactivateCompetitionsAndLeaderboards(() => {
                this.settings.miniScoreBoard.settings.active = true;
                this.settings.miniScoreBoard.settings.container.style.display = 'block';
                this.startup();
              });
            });
          }
        });
      });
    });
  };

  if (this.settings.autoStart) {
    this.init();
  }
};