<commit_before>(function () {
  var salaryContainer = '.salary-container';
  var compensationAmount = salaryContainer + ' .compensation .amount';
  var defaultValue = '--';

  var CONTRACT_TYPE_EMPLOYEE = 'employee_factor';
  var CONTRACT_TYPE_CONTRACTOR = 'contractor_factor';

  // Dropdown Core functionality

  var setDropdown = function(e) {
    var $selected = $(e.currentTarget);

    if ($selected.hasClass('filter-container')) {
      return;
    }

    var key = $selected.find('.key').text();
    var value = $selected.find('.value').text();
    var displayValue = $selected.find('.display-value').text();
    var $title = $selected.parents('.dropdown').find('.title');
    var $subtitle = $selected.parents('.dropdown').find('.subtitle');

    // Get range values if available
    var min = $selected.find('.value').data('min');
    var max = $selected.find('.value').data('max');

    $title.text(key);
    $title.data('selected', value || key);

    if (min && max) {
      $title.data('min', min);
      $title.data('max', max);
    }

    if (value) {
      $subtitle.text(value);
    } else if (displayValue) {
      $subtitle.text(displayValue);
    }
  }

  var isFullyVisible = function(el, parentEl) {
    var parentHeight = parentEl.height() - el.height(); // Make sure last item is not partially visible
    var elTop = el.position().top;
    var elBottom = elTop + (el.height());

    return (elBottom > 0 && elTop < parentHeight);
  }

  this.SalaryCalculator = (function() {
    function SalaryCalculator() {
      this.bindEvents();
    }

    SalaryCalculator.prototype.bindEvents = function() {
      var $countryDropdown = $(salaryContainer + ' .country li:not(.filter-container)');
      var $countryDropdownContainer = $(salaryContainer + ' .country');
      var $cityDropdown = $(salaryContainer + ' .city li:not(.filter-container)');
      var $cityDropdownContainer = $(salaryContainer + ' .city');
      var $levelDropdown = $(salaryContainer + ' .level li');
      var $levelDropdownContainer = $(salaryContainer + ' .level');
      var $experienceDropdown = $(salaryContainer + ' .experience li');
      var $experienceDropdownContainer = $(salaryContainer + ' .experience');
      var $countryFilter = $('.js-country-filter');
      var $cityFilter = $('.js-city-filter');

      // Set selected dropdown value
      $countryDropdown.on('click', setDropdown);
      $cityDropdown.on('click', setDropdown);
      $levelDropdown.on('click', setDropdown);
      $experienceDropdown.on('click', setDropdown);

      // Render compensation
      $countryDropdown.on('click', this.render.bind(this));
      $cityDropdown.on('click', this.render.bind(this));
      $levelDropdown.on('click', this.render.bind(this));
      $experienceDropdown.on('click', this.render.bind(this));

      // Unlock and filter city dropdown
      $countryDropdown.on('click', this.filterCityDropdown.bind(this));
      $countryDropdown.on('click', this.resetCityDropdown.bind(this));

      // Render Formula
      $levelDropdown.on('click', this.renderFormula.bind(this));
      $experienceDropdown.on('click', this.renderFormula.bind(this));

      // Highlighting for Dropdown
      $levelDropdownContainer.on('keydown', this.highlightDropdownItem.bind(this));
      $experienceDropdownContainer.on('keydown', this.highlightDropdownItem.bind(this));

      // Highlighting for Filter
      $countryDropdownContainer.on('keydown', this.highlightDropdownItem.bind(this));
      $cityDropdownContainer.on('keydown', this.highlightDropdownItem.bind(this));

      // Filtering
      $countryFilter.on('keyup', this.search.bind(null, {
        dropdown: 'country'
      }));

      $cityFilter.on('keyup', this.search.bind(this, {
        dropdown: 'city',
        filter: 'country',
      }));

      $countryDropdownContainer.on('shown.bs.dropdown', this.focusInput);
      $cityDropdownContainer.on('shown.bs.dropdown', this.focusInput);
    }

    SalaryCalculator.prototype.focusInput = function(e) {
      $(e.target).find('.filter-input').focus();
    }

    SalaryCalculator.prototype.search = function(data, e) {
      var text = e.target.value.toLowerCase();
      var filterValue = null;
      var $items = $(salaryContainer + ' .' + data.dropdown + ' li:not(.filter-container)');

      if (data.filter) {
        filterValue = this.getElementValues()[data.filter];
      }

      $items.each(function(index, element) {
        var $element = $(element);
        var value = $element.children('.key').text().toLowerCase();
        var searchableElement = true;

        if (data.filter && filterValue) {
          searchableElement = $element.data(data.filter) === filterValue;
        }

        if (value.indexOf(text) == -1 || !searchableElement) {
          $element.addClass('hidden');
        } else {
          $element.removeClass('hidden');
        }
      });
    }

    SalaryCalculator.prototype.highlightDropdownItem = function(e) {
      var $this = $(e.currentTarget);
      var listContainer = $this.find('.dropdown-menu.dropdown-scroll');
      var list = $this.find('li:not(.hidden):not(.filter-container)');
      var nextLi, prevLi, focusedLi;

      if ($this.hasClass('open')) {
        if ((e.keyCode === 38 || e.keyCode === 40) &&
            !list.filter('li.is-focused').length) {
          list.first().addClass('is-focused');
          return;
        }

        if (e.keyCode === 38 && !list.first().hasClass('is-focused')) { // Up
          prevLi = list
                    .filter('li.is-focused')
                    .prevAll('li:not(.hidden)').first();

          list.removeClass('is-focused');

          if (prevLi[0] === list.first()[0]) {
            list.first().addClass('is-focused');
          } else {
            prevLi.addClass('is-focused');
          }
        } else if (e.keyCode === 40 && !list.last().hasClass('is-focused')) { // Down
          nextLi = list
                    .filter('li.is-focused')
                    .nextAll('li:not(.hidden)').first();

          list.removeClass('is-focused');

          if (nextLi[0] === list.last()[0]) {
            list.last().addClass('is-focused');
          } else {
            nextLi.addClass('is-focused');
          }
        } else if (e.keyCode === 9) { // Tab keyCode
          $this.removeClass('open');
        } else if (e.keyCode === 13) { // Enter / Return
          // Triggering click directly while keydown is active
          // doesn't hide dropdown, this workaround does. Need better way :/
          setTimeout(function() { list.filter('li.is-focused').trigger('click'); });
        }

        focusedLi = list.filter('li.is-focused');
        if (focusedLi.length && list.first()[0] !== focusedLi[0]) {
          if (!isFullyVisible(focusedLi, listContainer)) {
            listContainer.scrollTop(list.index(focusedLi) * focusedLi.outerHeight());
          }
        }
        else {
          listContainer.scrollTop(0);
        }
      }
    }

    // Custom dropdown functionality

    SalaryCalculator.prototype.filterCityDropdown = function() {
      var selectedCountry = this.getElementValues().country;
      var $cities = $(salaryContainer + ' .city li:not(.filter-container)');

      $cities.each(function(index, element) {
        var $element = $(element);
        if ($element.data('country') === selectedCountry) {
          $element.removeClass('hidden');
        } else {
          $element.addClass('hidden');
        }
      });
    }

    SalaryCalculator.prototype.resetCityDropdown = function() {
      var $cityDropdownBtn = $(salaryContainer + ' .city .btn');
      var $cityDropdownTitle = $cityDropdownBtn.find('.title');
      var $filterInput = $('.js-city-filter');
      $filterInput.val('');
      $cityDropdownBtn.removeClass('disabled');
      $cityDropdownTitle.text('--');
      $cityDropdownTitle.data('selected', '');
      $cityDropdownBtn.find('.subtitle').text('');
      this.renderInvalidCompensation();
      this.renderFormula();
    }

    // Render functions

    SalaryCalculator.prototype.getData = function() {
      var deferred = jQuery.Deferred();

      $.get('/salary/data.json').then(function(data) {
        // Cache the data
        this.data = data;
        deferred.resolve();
      }.bind(this));

      return deferred.promise();
    }

    SalaryCalculator.prototype.render = function() {
      var input = this.getElementValues();

      function renderData() {
        if (input.country && input.city && input.level && input.experience.min && input.experience.max) {
          this.renderCompensation(input);
        } else {
          this.renderInvalidCompensation();
        }
        this.renderFormula();
        this.renderContractType();
      }

      if (this.data) {
        renderData.call(this);
      } else {
        $.when(this.getData()).then(function() {
            renderData.call(this);
          }.bind(this),
          this.renderInvalidCompensation.bind(this));
      }
    }

    SalaryCalculator.prototype.renderCompensation = function(input) {
      var numbeo = this.data.numbeo;
      var contracts = this.data.contractTypes;

      var levelIndex = parseFloat(input.level);
      var benchmark = input.salary;

      var rentIndex = this.calculateRentIndex(input.city, input.country);
      var marketAdjustment = this.calculateMarketAdjustment(input.city, input.country);
      var contract = this.calculateContractFactor(input.country);

      if (!rentIndex) {
        //When the city and country combination selected do not exist
        return this.renderInvalidCompensation();
      }

      var min = this.calculateCompensation(benchmark, rentIndex, marketAdjustment, levelIndex, contract.factor, input.experience.min);
      var max = this.calculateCompensation(benchmark, rentIndex, marketAdjustment, levelIndex, contract.factor, input.experience.max);
      $(compensationAmount).text(this.formatAmount(min) + ' - ' + this.formatAmount(max) + ' USD');
    }

    SalaryCalculator.prototype.renderContractType = function() {
      var country = this.getElementValues().country;
      var contract = this.calculateContractFactor(country);
      var $container = $('.contract-type-container');

      if (contract.type === CONTRACT_TYPE_EMPLOYEE) {
        $container.find('.grammer').text('an');
        $container.find('.company-type').text('Inc.');
        $container.find('.contract-type').text('employee');
      } else {
        $container.find('.grammer').text('a');
        $container.find('.company-type').text('BV.');
        $container.find('.contract-type').text('contractor');
      }

      $container.removeClass('hidden');
    }

    SalaryCalculator.prototype.renderFormula = function() {
      var values = this.getElementValues();
      var rentIndex = this.calculateRentIndex(values.city, values.country);
      var marketAdjustment = this.calculateMarketAdjustment(values.city, values.country);
      var contract = this.calculateContractFactor(values.country);
      var experience = values.experience;

      $('.formula .level .value').text(values.level ? values.level : defaultValue);
      $('.formula .experience .value').text(experience.min && experience.max ? experience.min + ' to ' + experience.max : defaultValue);

      $('.formula .rentIndex .value').text(rentIndex ? rentIndex : defaultValue);
      $('.formula .marketAdjustment .value').text(rentIndex ? marketAdjustment : defaultValue);
      $('.formula .contractType .value').text(contract.hasOwnProperty('factor') ? contract.factor.toFixed(2) : defaultValue);
    }

    SalaryCalculator.prototype.renderInvalidCompensation = function() {
      $(compensationAmount).text(defaultValue);
    }

    // Pure functions

    SalaryCalculator.prototype.getElementValues = function() {
      return {
        salary: $(salaryContainer).data('salary') || '',
        country: $(salaryContainer + ' .country .title').data('selected') || '',
        city: $(salaryContainer + ' .city .title').data('selected') || '',
        level: $(salaryContainer + ' .level .title').data('selected') || '',
        experience: {
          min: $(salaryContainer + ' .experience .title').data('min') || '',
          max: $(salaryContainer + ' .experience .title').data('max') || ''
        }
      };
    }

    SalaryCalculator.prototype.calculateRentIndex = function(city, country) {
      if (this.data && this.data.numbeo) {
        var locationData = this.data.numbeo.find(function(o) {
          return o.country === country && o.city === city;
        });
        return locationData ? parseFloat((locationData.rentIndex * 0.01).toFixed(2)) : 0;
      }

      // Rent Index will never be zero, safe to return as error value
      return 0;
    }

    SalaryCalculator.prototype.calculateMarketAdjustment = function(city, country) {
      if (this.data && this.data.numbeo) {
        var locationData = this.data.marketAdjustments.find(function(o) {
          return o.country === country && o.city === city;
        });

        return locationData ? parseFloat((locationData.hotmarket * 0.01).toFixed(2)) : 0;
      }

      return 0;
    }

    SalaryCalculator.prototype.calculateContractFactor = function(country) {
      var contract = {};

      if (this.data && this.data.contractTypes) {
        var defaultContractorFactor = this.data.contractTypes.find(function (o) {
          return o.country === '*';
        }).contractor_factor;

        var contractType = this.data.contractTypes.find(function(o) {
          return o.country === country
        });

        if (contractType && contractType.hasOwnProperty(CONTRACT_TYPE_EMPLOYEE)) {
          contract.type = CONTRACT_TYPE_EMPLOYEE;
          contract.factor = contractType[CONTRACT_TYPE_EMPLOYEE];
        } else if (contractType) {
          contract.type = CONTRACT_TYPE_CONTRACTOR;
          contract.factor = contractType[CONTRACT_TYPE_CONTRACTOR];
        } else {
          contract.type = CONTRACT_TYPE_CONTRACTOR;
          contract.factor = defaultContractorFactor;
        }
      }

      return contract;
    }

    SalaryCalculator.prototype.calculateCompensation = function(benchmark, rentIndex, marketAdjustment, levelIndex, contractType, experienceFactor) {
      return Math.round(benchmark * (rentIndex + marketAdjustment + 0.25) * levelIndex * contractType * experienceFactor);
    };

    SalaryCalculator.prototype.formatAmount = function(amount) {
      return '$' + amount.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
    }

    return SalaryCalculator;
  })();

  new SalaryCalculator();
})();
<commit_msg>parsing params and adding them to the Object state<commit_after>(function () {
  var paramsFetcher = function() {
    var regex = /[?&]([^=#]+)=([^&#]*)/g;
    var params = {};
    var match = '';

    while(match = regex.exec(window.location.href)) {
      params[match[1]] = match[2];
    }

    return (Object.keys(params).length > 0) ? params : null;
  };

  var salaryContainer = '.salary-container';
  var compensationAmount = salaryContainer + ' .compensation .amount';
  var defaultValue = '--';

  var CONTRACT_TYPE_EMPLOYEE = 'employee_factor';
  var CONTRACT_TYPE_CONTRACTOR = 'contractor_factor';

  // Dropdown Core functionality

  var setDropdown = function(e) {
    var $selected = $(e.currentTarget);

    if ($selected.hasClass('filter-container')) {
      return;
    }

    var key = $selected.find('.key').text();
    var value = $selected.find('.value').text();
    var displayValue = $selected.find('.display-value').text();
    var $title = $selected.parents('.dropdown').find('.title');
    var $subtitle = $selected.parents('.dropdown').find('.subtitle');

    // Get range values if available
    var min = $selected.find('.value').data('min');
    var max = $selected.find('.value').data('max');

    $title.text(key);
    $title.data('selected', value || key);

    if (min && max) {
      $title.data('min', min);
      $title.data('max', max);
    }

    if (value) {
      $subtitle.text(value);
    } else if (displayValue) {
      $subtitle.text(displayValue);
    }
  }

  var isFullyVisible = function(el, parentEl) {
    var parentHeight = parentEl.height() - el.height(); // Make sure last item is not partially visible
    var elTop = el.position().top;
    var elBottom = elTop + (el.height());

    return (elBottom > 0 && elTop < parentHeight);
  }

  this.SalaryCalculator = (function() {
    function SalaryCalculator(params) {
      this.bindEvents();
      this.params = params || {};
      console.log(this.params)
    }

    SalaryCalculator.prototype.bindEvents = function() {
      var $countryDropdown = $(salaryContainer + ' .country li:not(.filter-container)');
      var $countryDropdownContainer = $(salaryContainer + ' .country');
      var $cityDropdown = $(salaryContainer + ' .city li:not(.filter-container)');
      var $cityDropdownContainer = $(salaryContainer + ' .city');
      var $levelDropdown = $(salaryContainer + ' .level li');
      var $levelDropdownContainer = $(salaryContainer + ' .level');
      var $experienceDropdown = $(salaryContainer + ' .experience li');
      var $experienceDropdownContainer = $(salaryContainer + ' .experience');
      var $countryFilter = $('.js-country-filter');
      var $cityFilter = $('.js-city-filter');

      // Set selected dropdown value
      $countryDropdown.on('click', setDropdown);
      $cityDropdown.on('click', setDropdown);
      $levelDropdown.on('click', setDropdown);
      $experienceDropdown.on('click', setDropdown);

      // Render compensation
      $countryDropdown.on('click', this.render.bind(this));
      $cityDropdown.on('click', this.render.bind(this));
      $levelDropdown.on('click', this.render.bind(this));
      $experienceDropdown.on('click', this.render.bind(this));

      // Unlock and filter city dropdown
      $countryDropdown.on('click', this.filterCityDropdown.bind(this));
      $countryDropdown.on('click', this.resetCityDropdown.bind(this));

      // Render Formula
      $levelDropdown.on('click', this.renderFormula.bind(this));
      $experienceDropdown.on('click', this.renderFormula.bind(this));

      // Highlighting for Dropdown
      $levelDropdownContainer.on('keydown', this.highlightDropdownItem.bind(this));
      $experienceDropdownContainer.on('keydown', this.highlightDropdownItem.bind(this));

      // Highlighting for Filter
      $countryDropdownContainer.on('keydown', this.highlightDropdownItem.bind(this));
      $cityDropdownContainer.on('keydown', this.highlightDropdownItem.bind(this));

      // Filtering
      $countryFilter.on('keyup', this.search.bind(null, {
        dropdown: 'country'
      }));

      $cityFilter.on('keyup', this.search.bind(this, {
        dropdown: 'city',
        filter: 'country',
      }));

      $countryDropdownContainer.on('shown.bs.dropdown', this.focusInput);
      $cityDropdownContainer.on('shown.bs.dropdown', this.focusInput);
    }

    SalaryCalculator.prototype.focusInput = function(e) {
      $(e.target).find('.filter-input').focus();
    }

    SalaryCalculator.prototype.search = function(data, e) {
      var text = e.target.value.toLowerCase();
      var filterValue = null;
      var $items = $(salaryContainer + ' .' + data.dropdown + ' li:not(.filter-container)');

      if (data.filter) {
        filterValue = this.getElementValues()[data.filter];
      }

      $items.each(function(index, element) {
        var $element = $(element);
        var value = $element.children('.key').text().toLowerCase();
        var searchableElement = true;

        if (data.filter && filterValue) {
          searchableElement = $element.data(data.filter) === filterValue;
        }

        if (value.indexOf(text) == -1 || !searchableElement) {
          $element.addClass('hidden');
        } else {
          $element.removeClass('hidden');
        }
      });
    }

    SalaryCalculator.prototype.highlightDropdownItem = function(e) {
      var $this = $(e.currentTarget);
      var listContainer = $this.find('.dropdown-menu.dropdown-scroll');
      var list = $this.find('li:not(.hidden):not(.filter-container)');
      var nextLi, prevLi, focusedLi;

      if ($this.hasClass('open')) {
        if ((e.keyCode === 38 || e.keyCode === 40) &&
            !list.filter('li.is-focused').length) {
          list.first().addClass('is-focused');
          return;
        }

        if (e.keyCode === 38 && !list.first().hasClass('is-focused')) { // Up
          prevLi = list
                    .filter('li.is-focused')
                    .prevAll('li:not(.hidden)').first();

          list.removeClass('is-focused');

          if (prevLi[0] === list.first()[0]) {
            list.first().addClass('is-focused');
          } else {
            prevLi.addClass('is-focused');
          }
        } else if (e.keyCode === 40 && !list.last().hasClass('is-focused')) { // Down
          nextLi = list
                    .filter('li.is-focused')
                    .nextAll('li:not(.hidden)').first();

          list.removeClass('is-focused');

          if (nextLi[0] === list.last()[0]) {
            list.last().addClass('is-focused');
          } else {
            nextLi.addClass('is-focused');
          }
        } else if (e.keyCode === 9) { // Tab keyCode
          $this.removeClass('open');
        } else if (e.keyCode === 13) { // Enter / Return
          // Triggering click directly while keydown is active
          // doesn't hide dropdown, this workaround does. Need better way :/
          setTimeout(function() { list.filter('li.is-focused').trigger('click'); });
        }

        focusedLi = list.filter('li.is-focused');
        if (focusedLi.length && list.first()[0] !== focusedLi[0]) {
          if (!isFullyVisible(focusedLi, listContainer)) {
            listContainer.scrollTop(list.index(focusedLi) * focusedLi.outerHeight());
          }
        }
        else {
          listContainer.scrollTop(0);
        }
      }
    }

    // Custom dropdown functionality

    SalaryCalculator.prototype.filterCityDropdown = function() {
      var selectedCountry = this.getElementValues().country;
      var $cities = $(salaryContainer + ' .city li:not(.filter-container)');

      $cities.each(function(index, element) {
        var $element = $(element);
        if ($element.data('country') === selectedCountry) {
          $element.removeClass('hidden');
        } else {
          $element.addClass('hidden');
        }
      });
    }

    SalaryCalculator.prototype.resetCityDropdown = function() {
      var $cityDropdownBtn = $(salaryContainer + ' .city .btn');
      var $cityDropdownTitle = $cityDropdownBtn.find('.title');
      var $filterInput = $('.js-city-filter');
      $filterInput.val('');
      $cityDropdownBtn.removeClass('disabled');
      $cityDropdownTitle.text('--');
      $cityDropdownTitle.data('selected', '');
      $cityDropdownBtn.find('.subtitle').text('');
      this.renderInvalidCompensation();
      this.renderFormula();
    }

    // Render functions

    SalaryCalculator.prototype.getData = function() {
      var deferred = jQuery.Deferred();

      $.get('/salary/data.json').then(function(data) {
        // Cache the data
        this.data = data;
        deferred.resolve();
      }.bind(this));

      return deferred.promise();
    }

    SalaryCalculator.prototype.render = function() {
      var input = this.getElementValues();

      function renderData() {
        if (input.country && input.city && input.level && input.experience.min && input.experience.max) {
          this.renderCompensation(input);
        } else {
          this.renderInvalidCompensation();
        }
        this.renderFormula();
        this.renderContractType();
      }

      if (this.data) {
        renderData.call(this);
      } else {
        $.when(this.getData()).then(function() {
            renderData.call(this);
          }.bind(this),
          this.renderInvalidCompensation.bind(this));
      }
    }

    SalaryCalculator.prototype.renderCompensation = function(input) {
      var numbeo = this.data.numbeo;
      var contracts = this.data.contractTypes;

      var levelIndex = parseFloat(input.level);
      var benchmark = input.salary;

      var rentIndex = this.calculateRentIndex(input.city, input.country);
      var marketAdjustment = this.calculateMarketAdjustment(input.city, input.country);
      var contract = this.calculateContractFactor(input.country);

      if (!rentIndex) {
        //When the city and country combination selected do not exist
        return this.renderInvalidCompensation();
      }

      var min = this.calculateCompensation(benchmark, rentIndex, marketAdjustment, levelIndex, contract.factor, input.experience.min);
      var max = this.calculateCompensation(benchmark, rentIndex, marketAdjustment, levelIndex, contract.factor, input.experience.max);
      $(compensationAmount).text(this.formatAmount(min) + ' - ' + this.formatAmount(max) + ' USD');
    }

    SalaryCalculator.prototype.renderContractType = function() {
      var country = this.getElementValues().country;
      var contract = this.calculateContractFactor(country);
      var $container = $('.contract-type-container');

      if (contract.type === CONTRACT_TYPE_EMPLOYEE) {
        $container.find('.grammer').text('an');
        $container.find('.company-type').text('Inc.');
        $container.find('.contract-type').text('employee');
      } else {
        $container.find('.grammer').text('a');
        $container.find('.company-type').text('BV.');
        $container.find('.contract-type').text('contractor');
      }

      $container.removeClass('hidden');
    }

    SalaryCalculator.prototype.renderFormula = function() {
      var values = this.getElementValues();
      var rentIndex = this.calculateRentIndex(values.city, values.country);
      var marketAdjustment = this.calculateMarketAdjustment(values.city, values.country);
      var contract = this.calculateContractFactor(values.country);
      var experience = values.experience;

      $('.formula .level .value').text(values.level ? values.level : defaultValue);
      $('.formula .experience .value').text(experience.min && experience.max ? experience.min + ' to ' + experience.max : defaultValue);

      $('.formula .rentIndex .value').text(rentIndex ? rentIndex : defaultValue);
      $('.formula .marketAdjustment .value').text(rentIndex ? marketAdjustment : defaultValue);
      $('.formula .contractType .value').text(contract.hasOwnProperty('factor') ? contract.factor.toFixed(2) : defaultValue);
    }

    SalaryCalculator.prototype.renderInvalidCompensation = function() {
      $(compensationAmount).text(defaultValue);
    }

    // Pure functions

    SalaryCalculator.prototype.getElementValues = function() {
      return {
        salary: $(salaryContainer).data('salary') || '',
        country: $(salaryContainer + ' .country .title').data('selected') || '',
        city: $(salaryContainer + ' .city .title').data('selected') || '',
        level: $(salaryContainer + ' .level .title').data('selected') || '',
        experience: {
          min: $(salaryContainer + ' .experience .title').data('min') || '',
          max: $(salaryContainer + ' .experience .title').data('max') || ''
        }
      };
    }

    SalaryCalculator.prototype.calculateRentIndex = function(city, country) {
      if (this.data && this.data.numbeo) {
        var locationData = this.data.numbeo.find(function(o) {
          return o.country === country && o.city === city;
        });
        return locationData ? parseFloat((locationData.rentIndex * 0.01).toFixed(2)) : 0;
      }

      // Rent Index will never be zero, safe to return as error value
      return 0;
    }

    SalaryCalculator.prototype.calculateMarketAdjustment = function(city, country) {
      if (this.data && this.data.numbeo) {
        var locationData = this.data.marketAdjustments.find(function(o) {
          return o.country === country && o.city === city;
        });

        return locationData ? parseFloat((locationData.hotmarket * 0.01).toFixed(2)) : 0;
      }

      return 0;
    }

    SalaryCalculator.prototype.calculateContractFactor = function(country) {
      var contract = {};

      if (this.data && this.data.contractTypes) {
        var defaultContractorFactor = this.data.contractTypes.find(function (o) {
          return o.country === '*';
        }).contractor_factor;

        var contractType = this.data.contractTypes.find(function(o) {
          return o.country === country
        });

        if (contractType && contractType.hasOwnProperty(CONTRACT_TYPE_EMPLOYEE)) {
          contract.type = CONTRACT_TYPE_EMPLOYEE;
          contract.factor = contractType[CONTRACT_TYPE_EMPLOYEE];
        } else if (contractType) {
          contract.type = CONTRACT_TYPE_CONTRACTOR;
          contract.factor = contractType[CONTRACT_TYPE_CONTRACTOR];
        } else {
          contract.type = CONTRACT_TYPE_CONTRACTOR;
          contract.factor = defaultContractorFactor;
        }
      }

      return contract;
    }

    SalaryCalculator.prototype.calculateCompensation = function(benchmark, rentIndex, marketAdjustment, levelIndex, contractType, experienceFactor) {
      return Math.round(benchmark * (rentIndex + marketAdjustment + 0.25) * levelIndex * contractType * experienceFactor);
    };

    SalaryCalculator.prototype.formatAmount = function(amount) {
      return '$' + amount.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
    }

    return SalaryCalculator;
  })();

  var params = paramsFetcher();

  if (params) {
    new SalaryCalculator(params);
  } else {
    new SalaryCalculator(false);
  }
})();
<|endoftext|>


<commit_before>(function () {
'use strict';

/*
 * AngularJS Toaster
 * Version: 0.4.10
 *
 * Copyright 2013-2014 Jiri Kavulak.
 * All Rights Reserved.
 * Use, reproduction, distribution, and modification of this code is subject to the terms and
 * conditions of the MIT license, available at http://www.opensource.org/licenses/mit-license.php
 *
 * Author: Jiri Kavulak
 * Related to project of John Papa and Hans Fjällemark
 */

angular.module('toaster', ['ngAnimate'])
.constant('toasterConfig', {
    'limit': 0,                   // limits max number of toasts
    'tap-to-dismiss': true,
    'close-button': false,
    'newest-on-top': true,
    //'fade-in': 1000,            // done in css
    //'on-fade-in': undefined,    // not implemented
    //'fade-out': 1000,           // done in css
    //'on-fade-out': undefined,   // not implemented
    //'extended-time-out': 1000,  // not implemented
    'time-out': 5000, // Set timeOut and extendedTimeout to 0 to make it sticky
    'icon-classes': {
        error: 'toast-error',
        info: 'toast-info',
        wait: 'toast-wait',
        success: 'toast-success',
        warning: 'toast-warning'
    },
    'body-output-type': '', // Options: '', 'trustedHtml', 'template', 'templateWithData'
    'body-template': 'toasterBodyTmpl.html',
    'icon-class': 'toast-info',
    'position-class': 'toast-top-right',
    'title-class': 'toast-title',
    'message-class': 'toast-message',
    'prevent-duplicates': false,
    'mouseover-timer-stop': true // stop timeout on mouseover and restart timer on mouseout
})
.service('toaster', ['$rootScope', 'toasterConfig', function ($rootScope, toasterConfig) {
    this.pop = function (type, title, body, timeout, bodyOutputType, clickHandler) {
        if (angular.isObject(type)) {
            var params = type; // NOTE: enable parameters as pop argument
            this.toast = {
                type: params.type,
                title: params.title,
                body: params.body,
                timeout: params.timeout,
                bodyOutputType: params.bodyOutputType,
                clickHandler: params.clickHandler
            };
        } else {
            this.toast = {
                type: type,
                title: title,
                body: body,
                timeout: timeout,
                bodyOutputType: bodyOutputType,
                clickHandler: clickHandler
            };
        }
        $rootScope.$emit('toaster-newToast');
    };

    this.clear = function () {
        $rootScope.$emit('toaster-clearToasts');
    };

    // Create one method per icon class, to allow to call toaster.info() and similar
    for (var type in toasterConfig['icon-classes']) {
        this[type] = (function (toasterType) {
            return function(title, body, timeout, bodyOutputType, clickHandler) {
                if (angular.isString(title))
                    this.pop(toasterType, title, body, timeout, bodyOutputType, clickHandler);
                else // 'title' is actually an object with options
                    this.pop(angular.extend(title, { type: toasterType }));
               }
             })(type);
    }
}])
.factory('toasterRegisterEvents', function() {
  var toasterFactory = {
      _NewToastEvent: false,
      _ClearAllToastsEvent: false,
      registerNewToastEvent: function() {
        this._NewToastEvent = true;
      },
      registerClearAllToastsEvent: function() {
        this._ClearAllToastsEvent = true;
      },
      deregisterNewToastEvent: function() {
        this._NewToastEvent = false;
      },
      deregisterClearAllToastsEvent: function() {
        this._ClearAllToastsEvent = false;
      },
      isRegisteredNewToastEvent: function() {
        return this._NewToastEvent;
      },
      isRegisteredClearAllToastsEvent: function() {
        return this._ClearAllToastsEvent;
      }
    }
    return {
      registerNewToastEvent: toasterFactory.registerNewToastEvent,
      registerClearAllToastsEvent: toasterFactory.registerClearAllToastsEvent,
      deregisterNewToastEvent: toasterFactory.deregisterNewToastEvent,
      deregisterClearAllToastsEvent: toasterFactory.deregisterClearAllToastsEvent,
      isRegisteredNewToastEvent: toasterFactory.isRegisteredNewToastEvent,
      isRegisteredClearAllToastsEvent: toasterFactory.isRegisteredClearAllToastsEvent
    }
})
.directive('toasterContainer', ['$parse', '$rootScope', '$interval', '$sce', 'toasterConfig', 'toaster', 'toasterRegisterEvents',
function ($parse, $rootScope, $interval, $sce, toasterConfig, toaster, toasterRegisterEvents) {
    return {
        replace: true,
        restrict: 'EA',
        scope: true, // creates an internal scope for this directive
        link: function (scope, elm, attrs) {
            var id = 0,
                mergedConfig;

            mergedConfig = angular.extend({}, toasterConfig, scope.$eval(attrs.toasterOptions));

            scope.config = {
                position: mergedConfig['position-class'],
                title: mergedConfig['title-class'],
                message: mergedConfig['message-class'],
                tap: mergedConfig['tap-to-dismiss'],
                closeButton: mergedConfig['close-button'],
                animation: mergedConfig['animation-class'],
                mouseoverTimer:  mergedConfig['mouseover-timer-stop']
            };

            scope.deregisterClearToasts = null;
            scope.deregisterNewToast = null;

            scope.$on("$destroy",function () {
                if (scope.deregisterClearToasts) scope.deregisterClearToasts();
                if (scope.deregisterNewToast) scope.deregisterNewToast();
                scope.deregisterClearToasts = null;
                scope.deregisterNewToast = null;
                toasterRegisterEvents.deregisterNewToastEvent();
                toasterRegisterEvents.deregisterClearAllToastsEvent();
            });

            scope.configureTimer = function configureTimer(toast) {
                var timeout = angular.isNumber(toast.timeout) ? toast.timeout : mergedConfig['time-out'];
                if (timeout > 0)
                    setTimeout(toast, timeout);
            };

            function addToast(toast) {
                toast.type = mergedConfig['icon-classes'][toast.type];
                if (!toast.type)
                    toast.type = mergedConfig['icon-class'];

                // Prevent adding duplicate toasts if it's set
                if (mergedConfig['prevent-duplicates'] === true &&
                    scope.toasters.length > 0 &&
                    scope.toasters[scope.toasters.length - 1].body === toast.body)
                    return;

                id++;
                angular.extend(toast, { id: id });

                // Set the toast.bodyOutputType to the default if it isn't set
                toast.bodyOutputType = toast.bodyOutputType || mergedConfig['body-output-type'];
                switch (toast.bodyOutputType) {
                    case 'trustedHtml':
                        toast.html = $sce.trustAsHtml(toast.body);
                        break;
                    case 'template':
                        toast.bodyTemplate = toast.body || mergedConfig['body-template'];
                        break;
                    case 'templateWithData':
                        var fcGet = $parse(toast.body || mergedConfig['body-template']);
                        var templateWithData = fcGet(scope);
                        toast.bodyTemplate = templateWithData.template;
                        toast.data = templateWithData.data;
                        break;
                }

                scope.configureTimer(toast);

                if (mergedConfig['newest-on-top'] === true) {
                    scope.toasters.unshift(toast);
                    if (mergedConfig['limit'] > 0 && scope.toasters.length > mergedConfig['limit']) {
                        scope.toasters.pop();
                    }
                } else {
                    scope.toasters.push(toast);
                    if (mergedConfig['limit'] > 0 && scope.toasters.length > mergedConfig['limit']) {
                        scope.toasters.shift();
                    }
                }

                toast.mouseover = false;
            }

            function setTimeout(toast, time) {
                toast.timeout = $interval(function () {
                    if (!toast.mouseover)
                        scope.removeToast(toast.id);
                }, time);
            }

            scope.toasters = [];

            if (!toasterRegisterEvents.isRegisteredNewToastEvent()) {
                toasterRegisterEvents.registerNewToastEvent();
                scope.deregisterNewToast = $rootScope.$on('toaster-newToast', function () {
                    addToast(toaster.toast);
                });
            }

            if (!toasterRegisterEvents.isRegisteredClearAllToastsEvent()) {
                toasterRegisterEvents.registerClearAllToastsEvent();
                scope.deregisterClearToasts = $rootScope.$on('toaster-clearToasts', function () {
                    scope.toasters.splice(0, scope.toasters.length);
                });
            }
        },
        controller: ['$scope', '$element', '$attrs', function ($scope, $element, $attrs) {
            $scope.stopTimer = function (toast) {
                toast.mouseover = true;
                if ($scope.config.mouseoverTimer === true) {
                    if (toast.timeout) {
                        $interval.cancel(toast.timeout);
                        toast.timeout = null;
                    }
                }
            };

            $scope.restartTimer = function (toast) {
                toast.mouseover = false;
                if ($scope.config.mouseoverTimer === true) {
                    if (!toast.timeout)
                        $scope.configureTimer(toast);
                }
                else if (toast.timeout === null) {
                    $scope.removeToast(toaster.id);
                }
            };

            $scope.removeToast = function (id) {
                var i = 0;
                for (i; i < $scope.toasters.length; i++) {
                    if ($scope.toasters[i].id === id) {
                        $scope.toasters.splice(i, 1);
                        break;
                    }
                }
            };

            $scope.click = function (toaster, isCloseButton) {
                if ($scope.config.tap === true || isCloseButton == true) {
                    var removeToast = true;
                    if (toaster.clickHandler) {
                        if (angular.isFunction(toaster.clickHandler)) {
                            removeToast = toaster.clickHandler(toaster, isCloseButton);
                        }
                        else if (angular.isFunction($scope.$parent.$eval(toaster.clickHandler))) {
                            removeToast = $scope.$parent.$eval(toaster.clickHandler)(toaster, isCloseButton);
                        }
                        else {
                            console.log("TOAST-NOTE: Your click handler is not inside a parent scope of toaster-container.");
                        }
                    }
                    if (removeToast) {
                        $scope.removeToast(toaster.id);
                    }
                }
            };
        }],
        template:
        '
' +
          '
' +
            '<button class="toast-close-button" ng-show="config.closeButton" ng-click="click(toaster, true)">×</button>' +
            '
{{toaster.title}}
' +
            '
' +
              '

' +
              '

' +
              '

' +
              '
{{toaster.body}}
' +
            '
' +
          '
' +
        '
'
    };
}]);
})(window, document);
<commit_msg>Allow to specify position on pop() call<commit_after>(function () {
'use strict';

/*
 * AngularJS Toaster
 * Version: 0.4.10
 *
 * Copyright 2013-2014 Jiri Kavulak.
 * All Rights Reserved.
 * Use, reproduction, distribution, and modification of this code is subject to the terms and
 * conditions of the MIT license, available at http://www.opensource.org/licenses/mit-license.php
 *
 * Author: Jiri Kavulak
 * Related to project of John Papa and Hans Fjällemark
 */

angular.module('toaster', ['ngAnimate'])
.constant('toasterConfig', {
    'limit': 0,                   // limits max number of toasts
    'tap-to-dismiss': true,
    'close-button': false,
    'newest-on-top': true,
    //'fade-in': 1000,            // done in css
    //'on-fade-in': undefined,    // not implemented
    //'fade-out': 1000,           // done in css
    //'on-fade-out': undefined,   // not implemented
    //'extended-time-out': 1000,  // not implemented
    'time-out': 5000, // Set timeOut and extendedTimeout to 0 to make it sticky
    'icon-classes': {
        error: 'toast-error',
        info: 'toast-info',
        wait: 'toast-wait',
        success: 'toast-success',
        warning: 'toast-warning'
    },
    'body-output-type': '', // Options: '', 'trustedHtml', 'template', 'templateWithData'
    'body-template': 'toasterBodyTmpl.html',
    'icon-class': 'toast-info',
    'position-class': 'toast-top-right', // Options (see CSS):
                                         // 'toast-top-full-width', 'toast-bottom-full-width', 'toast-center',
                                         // 'toast-top-left', 'toast-top-center', 'toast-top-rigt',
                                         // 'toast-bottom-left', 'toast-bottom-center', 'toast-bottom-rigt',
    'title-class': 'toast-title',
    'message-class': 'toast-message',
    'prevent-duplicates': false,
    'mouseover-timer-stop': true // stop timeout on mouseover and restart timer on mouseout
})
.service('toaster', ['$rootScope', 'toasterConfig', function ($rootScope, toasterConfig) {
    this.pop = function (type, title, body, timeout, bodyOutputType, clickHandler, position) {
        if (angular.isObject(type)) {
            var params = type; // Enable named parameters as pop argument
            this.toast = {
                type: params.type,
                title: params.title,
                body: params.body,
                timeout: params.timeout,
                bodyOutputType: params.bodyOutputType,
                clickHandler: params.clickHandler,
                position: params.position
            };
        } else {
            this.toast = {
                type: type,
                title: title,
                body: body,
                timeout: timeout,
                bodyOutputType: bodyOutputType,
                clickHandler: clickHandler,
                position: position
            };
        }
        $rootScope.$emit('toaster-newToast');
    };

    this.clear = function () {
        $rootScope.$emit('toaster-clearToasts');
    };

    // Create one method per icon class, to allow to call toaster.info() and similar
    for (var type in toasterConfig['icon-classes']) {
        this[type] = (function (toasterType) {
            return function(title, body, timeout, bodyOutputType, clickHandler, position) {
                if (angular.isString(title))
                    this.pop(toasterType, title, body, timeout, bodyOutputType, clickHandler, position);
                else // 'title' is actually an object with options
                    this.pop(angular.extend(title, { type: toasterType }));
            }
        })(type);
    }
}])
.factory('toasterRegisterEvents', function() {
    var toasterFactory = {
        _NewToastEvent: false,
        _ClearAllToastsEvent: false,
        registerNewToastEvent: function() {
            this._NewToastEvent = true;
        },
        registerClearAllToastsEvent: function() {
            this._ClearAllToastsEvent = true;
        },
        deregisterNewToastEvent: function() {
            this._NewToastEvent = false;
        },
        deregisterClearAllToastsEvent: function() {
            this._ClearAllToastsEvent = false;
        },
        isRegisteredNewToastEvent: function() {
            return this._NewToastEvent;
        },
        isRegisteredClearAllToastsEvent: function() {
            return this._ClearAllToastsEvent;
        }
    }
    return {
        registerNewToastEvent: toasterFactory.registerNewToastEvent,
        registerClearAllToastsEvent: toasterFactory.registerClearAllToastsEvent,
        deregisterNewToastEvent: toasterFactory.deregisterNewToastEvent,
        deregisterClearAllToastsEvent: toasterFactory.deregisterClearAllToastsEvent,
        isRegisteredNewToastEvent: toasterFactory.isRegisteredNewToastEvent,
        isRegisteredClearAllToastsEvent: toasterFactory.isRegisteredClearAllToastsEvent
    }
})
.directive('toasterContainer', ['$parse', '$rootScope', '$interval', '$sce', 'toasterConfig', 'toaster', 'toasterRegisterEvents',
function ($parse, $rootScope, $interval, $sce, toasterConfig, toaster, toasterRegisterEvents) {
    return {
        replace: true,
        restrict: 'EA',
        scope: true, // creates an internal scope for this directive
        link: function (scope, elm, attrs) {
            var id = 0,
                mergedConfig;

            // Merges configuration set in directive with default one
            mergedConfig = angular.extend({}, toasterConfig, scope.$eval(attrs.toasterOptions));

            scope.config = {
                position: mergedConfig['position-class'],
                title: mergedConfig['title-class'],
                message: mergedConfig['message-class'],
                tap: mergedConfig['tap-to-dismiss'],
                closeButton: mergedConfig['close-button'],
                animation: mergedConfig['animation-class'],
                mouseoverTimer: mergedConfig['mouseover-timer-stop']
            };

            scope.deregisterClearToasts = null;
            scope.deregisterNewToast = null;

            scope.$on("$destroy", function () {
                if (scope.deregisterClearToasts) scope.deregisterClearToasts();
                if (scope.deregisterNewToast) scope.deregisterNewToast();
                scope.deregisterClearToasts = null;
                scope.deregisterNewToast = null;
                toasterRegisterEvents.deregisterNewToastEvent();
                toasterRegisterEvents.deregisterClearAllToastsEvent();
            });

            scope.configureTimer = function configureTimer(toast) {
                var timeout = angular.isNumber(toast.timeout) ? toast.timeout : mergedConfig['time-out'];
                if (timeout > 0)
                    setTimeout(toast, timeout);
            };

            function addToast(toast) {
                toast.type = mergedConfig['icon-classes'][toast.type];
                if (!toast.type)
                    toast.type = mergedConfig['icon-class'];

                if (toast.position) {
                    scope.config.position = toast.position;
                }

                // Prevent adding duplicate toasts if it's set
                if (mergedConfig['prevent-duplicates'] === true &&
                    scope.toasters.length > 0 &&
                    scope.toasters[scope.toasters.length - 1].body === toast.body)
                    return;

                id++;
                toast.id = id;

                // Set the toast.bodyOutputType to the default if it isn't set
                toast.bodyOutputType = toast.bodyOutputType || mergedConfig['body-output-type'];
                switch (toast.bodyOutputType) {
                    case 'trustedHtml':
                        toast.html = $sce.trustAsHtml(toast.body);
                        break;
                    case 'template':
                        toast.bodyTemplate = toast.body || mergedConfig['body-template'];
                        break;
                    case 'templateWithData':
                        var fcGet = $parse(toast.body || mergedConfig['body-template']);
                        var templateWithData = fcGet(scope);
                        toast.bodyTemplate = templateWithData.template;
                        toast.data = templateWithData.data;
                        break;
                }

                scope.configureTimer(toast);

                if (mergedConfig['newest-on-top'] === true) {
                    scope.toasters.unshift(toast);
                    if (mergedConfig['limit'] > 0 && scope.toasters.length > mergedConfig['limit']) {
                        scope.toasters.pop();
                    }
                } else {
                    scope.toasters.push(toast);
                    if (mergedConfig['limit'] > 0 && scope.toasters.length > mergedConfig['limit']) {
                        scope.toasters.shift();
                    }
                }

                toast.mouseover = false;
            }

            function setTimeout(toast, time) {
                toast.timeout = $interval(function () {
                    if (!toast.mouseover)
                        scope.removeToast(toast.id);
                }, time);
            }

            scope.toasters = [];

            if (!toasterRegisterEvents.isRegisteredNewToastEvent()) {
                toasterRegisterEvents.registerNewToastEvent();
                scope.deregisterNewToast = $rootScope.$on('toaster-newToast', function () {
                    addToast(toaster.toast);
                });
            }

            if (!toasterRegisterEvents.isRegisteredClearAllToastsEvent()) {
                toasterRegisterEvents.registerClearAllToastsEvent();
                scope.deregisterClearToasts = $rootScope.$on('toaster-clearToasts', function () {
                    scope.toasters.splice(0, scope.toasters.length);
                });
            }
        },
        controller: ['$scope', '$element', '$attrs', function ($scope, $element, $attrs) {
            $scope.stopTimer = function (toast) {
                toast.mouseover = true;
                if ($scope.config.mouseoverTimer === true) {
                    if (toast.timeout) {
                        $interval.cancel(toast.timeout);
                        toast.timeout = null;
                    }
                }
            };

            $scope.restartTimer = function (toast) {
                toast.mouseover = false;
                if ($scope.config.mouseoverTimer === true) {
                    if (!toast.timeout)
                        $scope.configureTimer(toast);
                } else if (toast.timeout === null) {
                    $scope.removeToast(toaster.id);
                }
            };

            $scope.removeToast = function (id) {
                var i = 0;
                for (i; i < $scope.toasters.length; i++) {
                    if ($scope.toasters[i].id === id) {
                        $scope.toasters.splice(i, 1);
                        break;
                    }
                }
            };

            $scope.click = function (toaster, isCloseButton) {
                if ($scope.config.tap === true || isCloseButton === true) {
                    var removeToast = true;
                    if (toaster.clickHandler) {
                        if (angular.isFunction(toaster.clickHandler)) {
                            removeToast = toaster.clickHandler(toaster, isCloseButton);
                        } else if (angular.isFunction($scope.$parent.$eval(toaster.clickHandler))) {
                            removeToast = $scope.$parent.$eval(toaster.clickHandler)(toaster, isCloseButton);
                        } else {
                            console.log("TOAST-NOTE: Your click handler is not inside a parent scope of toaster-container.");
                        }
                    }
                    if (removeToast) {
                        $scope.removeToast(toaster.id);
                    }
                }
            };
        }],
        template:
        '
' +
          '
' +
            '<button class="toast-close-button" ng-show="config.closeButton" ng-click="click(toaster, true)">×</button>' +
            '
{{toaster.title}}
' +
            '
' +
              '

' +
              '

' +
              '

' +
              '
{{toaster.body}}
' +
            '
' +
          '
' +
        '
'
    };
}]);
})(window, document);
<|endoftext|>


<commit_before>$.fn['toggles'] = function(options) {
  options = options || {};

  // extend default opts with the users options
  var opts = $.extend({
    'drag': true, // can the toggle be dragged
    'click': true, // can it be clicked to toggle
    'text': {
      'on': 'ON', // text for the ON position
      'off': 'OFF' // and off
    },
    'on': true, // is the toggle ON on init
    'animate': 250, // animation time
    'transition': 'ease-in-out', // animation transition,
    'checkbox': null, // the checkbox to toggle (for use in forms)
    'clicker': null, // element that can be clicked on to toggle. removes binding from the toggle itself (use nesting)
    'width': 50, // width used if not set in css
    'height': 20, // height if not set in css
    'type': 'compact' // defaults to a compact toggle, other option is 'select' where both options are shown at once
  },options);

  var selectType = (opts['type'] == 'select');

  // ensure these are jquery elements
  opts['checkbox'] = $(opts['checkbox']); // doesnt matter for checkbox

  if (opts['clicker']) opts['clicker'] = $(opts['clicker']); // leave as null if not set

  // use native transitions if possible
  var transition = 'margin-left '+opts['animate']+'ms '+opts['transition'];
  var transitions = {
    '-webkit-transition': transition,
    '-moz-transition': transition,
    'transition': transition
  };

  // for resetting transitions to none
  var notransitions = {
    '-webkit-transition': '',
    '-moz-transition': '',
    'transition': ''
  };

  // this is the actual toggle function which does the toggling
  var doToggle = function(slide, width, height) {
    var active = slide.toggleClass('active').hasClass('active');
    var inner = slide.find('.toggle-inner').css(transitions);

    slide.find('.toggle-off').toggleClass('active');
    slide.find('.toggle-on').toggleClass('active');

    var margin = active ? 0 : -width + height;

    // move the toggle!
    inner.css('margin-left',margin);

    // toggle the checkbox, if there is one
    opts['checkbox'].attr('checked',active);

    // ensure the toggle is left in the correct state after animation
    setTimeout(function() {
      inner.css(notransitions);
      inner.css('margin-left',margin);
    },opts['animate']);

  };

  // start setting up the toggle(s)
  return this.each(function() {
    var toggle = $(this);

    var height = toggle.height();
    var width = toggle.width();

    // if the element doesnt have an explicit height/width in css, set them
    if (!height || !width) {
      toggle.height(height = opts.height);
      toggle.width(width = opts.width);
    }

    var div = '
'); // wrapper inside toggle
    var inner = $(div+'inner">'); // inside slide, this bit moves
    var on = $(div+'on">'); // the on div
    var off = $(div+'off">'); // off div
    var blob = $(div+'blob">'); // the grip toggle blob

    if (opts['on']) on.addClass('active');

    var halfheight = height/2;
    var onoffwidth = width - halfheight;

    // set up the CSS for the individual elements
    on
      .css({
        height: height,
        width: onoffwidth,
        textAlign: 'center',
        textIndent: selectType ? '' : -halfheight,
        lineHeight: height+'px'
      })
      .text(opts['text']['on']);

    off
      .css({
        height: height,
        width: onoffwidth,
        marginLeft: selectType ? '' : -halfheight,
        textAlign: 'center',
        textIndent: selectType ? '' : halfheight,
        lineHeight: height+'px'
      })
      .text(opts['text']['off']);

    blob.css({
      height: height,
      width: height,
      marginLeft: -halfheight
    });

    inner.css('width',width * 2 - height);

    if (selectType) {
      toggle.css('width', onoffwidth*2);
      blob.hide();
    }

    if (opts['on']) {
      slide.addClass('active');
      opts['checkbox'].attr('checked',true);
    }

    // construct the toggle
    toggle.html(slide.html(inner.append(on,blob,off)));

    // when toggle is fired, toggle the toggle
    slide.on('toggle', function(e,active) {

      // stop bubbling
      if (e) e.stopPropagation();

      toggle.trigger('toggle',active);
      doToggle(slide,width,height);
    });

    // if click is enabled and toggle isn't within the clicker element (stops double binding)
    if (opts['click'] && (!opts['clicker'] || !opts['clicker'].has(toggle).length)) {

      // bind the click, ensuring its not the blob being clicked on
      toggle.on('click',function(e) {
        if (e.target !=  blob[0] || !opts['drag']) {
          slide.trigger('toggle', !slide.hasClass('active'));
        }
      });
    }

    // setup the clicker element
    if (opts['clicker']) {
      opts['clicker'].on('click',function(e) {
        if (e.target !=  blob[0] || !opts['drag']) {
          slide.trigger('toggle', !slide.hasClass('active'));
        }
      });
    }

    // we're done with all the non dragging stuff
    if (!opts['drag'] || selectType) return;

    // time to begin the dragging parts/blob clicks
    var diff;
    var slideLimit = (width - height) / 4;

    // fired on mouseup and mouseleave events
    var upLeave = function(e) {
      toggle.off('mousemove');
      slide.off('mouseleave');
      blob.off('mouseup');

      var active = slide.hasClass('active');

      if (!diff && opts.click && e.type !== 'mouseleave') {

        // theres no diff so nothing has moved. only toggle if its a mouseup
        slide.trigger('toggle', !active);
        return;
      }

      if (active) {

        // if the movement enough to toggle?
        if (diff < -slideLimit) {
          slide.trigger('toggle',!active);
        } else {

          // go back
          inner.animate({
            marginLeft: 0
          },opts.animate/2);
        }
      } else {

        // inactive
        if (diff > slideLimit) {
          slide.trigger('toggle',!active);
        } else {

          // go back again
          inner.animate({
            marginLeft: -width + height
          },opts.animate/2);
        }
      }

    };

    var wh = -width + height;

    blob.on('mousedown', function(e) {

      // reset diff
      diff = 0;

      blob.off('mouseup');
      slide.off('mouseleave');
      var cursor = e.pageX;

      toggle.on('mousemove', blob, function(e) {
        diff = e.pageX - cursor;
        var marginLeft;
        if (slide.hasClass('active')) {

          marginLeft = diff;

          // keep it within the limits
          if (diff > 0) marginLeft = 0;
          if (diff < wh) marginLeft = wh;
        } else {

          marginLeft = diff + wh;

          if (diff < 0) marginLeft = wh;
          if (diff > -wh) marginLeft = 0;

        }

        inner.css('margin-left',marginLeft);
      });

      blob.on('mouseup', upLeave);
      slide.on('mouseleave', upLeave);
    });

  });

};<commit_msg>dont play with margins in select mode<commit_after>$.fn['toggles'] = function(options) {
  options = options || {};

  // extend default opts with the users options
  var opts = $.extend({
    'drag': true, // can the toggle be dragged
    'click': true, // can it be clicked to toggle
    'text': {
      'on': 'ON', // text for the ON position
      'off': 'OFF' // and off
    },
    'on': true, // is the toggle ON on init
    'animate': 250, // animation time
    'transition': 'ease-in-out', // animation transition,
    'checkbox': null, // the checkbox to toggle (for use in forms)
    'clicker': null, // element that can be clicked on to toggle. removes binding from the toggle itself (use nesting)
    'width': 50, // width used if not set in css
    'height': 20, // height if not set in css
    'type': 'compact' // defaults to a compact toggle, other option is 'select' where both options are shown at once
  },options);

  var selectType = (opts['type'] == 'select');

  // ensure these are jquery elements
  opts['checkbox'] = $(opts['checkbox']); // doesnt matter for checkbox

  if (opts['clicker']) opts['clicker'] = $(opts['clicker']); // leave as null if not set

  // use native transitions if possible
  var transition = 'margin-left '+opts['animate']+'ms '+opts['transition'];
  var transitions = {
    '-webkit-transition': transition,
    '-moz-transition': transition,
    'transition': transition
  };

  // for resetting transitions to none
  var notransitions = {
    '-webkit-transition': '',
    '-moz-transition': '',
    'transition': ''
  };

  // this is the actual toggle function which does the toggling
  var doToggle = function(slide, width, height) {
    var active = slide.toggleClass('active').hasClass('active');
    var inner = slide.find('.toggle-inner').css(transitions);

    slide.find('.toggle-off').toggleClass('active');
    slide.find('.toggle-on').toggleClass('active');

    // toggle the checkbox, if there is one
    opts['checkbox'].attr('checked',active);

    if (selectType) return;

    var margin = active ? 0 : -width + height;

    // move the toggle!
    inner.css('margin-left',margin);

    // ensure the toggle is left in the correct state after animation
    setTimeout(function() {
      inner.css(notransitions);
      inner.css('margin-left',margin);
    },opts['animate']);

  };

  // start setting up the toggle(s)
  return this.each(function() {
    var toggle = $(this);

    var height = toggle.height();
    var width = toggle.width();

    // if the element doesnt have an explicit height/width in css, set them
    if (!height || !width) {
      toggle.height(height = opts.height);
      toggle.width(width = opts.width);
    }

    var div = '
'); // wrapper inside toggle
    var inner = $(div+'inner">'); // inside slide, this bit moves
    var on = $(div+'on">'); // the on div
    var off = $(div+'off">'); // off div
    var blob = $(div+'blob">'); // the grip toggle blob

    if (opts['on']) on.addClass('active');

    var halfheight = height/2;
    var onoffwidth = width - halfheight;

    // set up the CSS for the individual elements
    on
      .css({
        height: height,
        width: onoffwidth,
        textAlign: 'center',
        textIndent: selectType ? '' : -halfheight,
        lineHeight: height+'px'
      })
      .text(opts['text']['on']);

    off
      .css({
        height: height,
        width: onoffwidth,
        marginLeft: selectType ? '' : -halfheight,
        textAlign: 'center',
        textIndent: selectType ? '' : halfheight,
        lineHeight: height+'px'
      })
      .text(opts['text']['off']);

    blob.css({
      height: height,
      width: height,
      marginLeft: -halfheight
    });

    inner.css('width',width * 2 - height);

    if (selectType) {
      toggle.css('width', onoffwidth*2);
      blob.hide();
    }

    if (opts['on']) {
      slide.addClass('active');
      opts['checkbox'].attr('checked',true);
    }

    // construct the toggle
    toggle.html(slide.html(inner.append(on,blob,off)));

    // when toggle is fired, toggle the toggle
    slide.on('toggle', function(e,active) {

      // stop bubbling
      if (e) e.stopPropagation();

      toggle.trigger('toggle',active);
      doToggle(slide,width,height);
    });

    // if click is enabled and toggle isn't within the clicker element (stops double binding)
    if (opts['click'] && (!opts['clicker'] || !opts['clicker'].has(toggle).length)) {

      // bind the click, ensuring its not the blob being clicked on
      toggle.on('click',function(e) {
        if (e.target !=  blob[0] || !opts['drag']) {
          slide.trigger('toggle', !slide.hasClass('active'));
        }
      });
    }

    // setup the clicker element
    if (opts['clicker']) {
      opts['clicker'].on('click',function(e) {
        if (e.target !=  blob[0] || !opts['drag']) {
          slide.trigger('toggle', !slide.hasClass('active'));
        }
      });
    }

    // we're done with all the non dragging stuff
    if (!opts['drag'] || selectType) return;

    // time to begin the dragging parts/blob clicks
    var diff;
    var slideLimit = (width - height) / 4;

    // fired on mouseup and mouseleave events
    var upLeave = function(e) {
      toggle.off('mousemove');
      slide.off('mouseleave');
      blob.off('mouseup');

      var active = slide.hasClass('active');

      if (!diff && opts.click && e.type !== 'mouseleave') {

        // theres no diff so nothing has moved. only toggle if its a mouseup
        slide.trigger('toggle', !active);
        return;
      }

      if (active) {

        // if the movement enough to toggle?
        if (diff < -slideLimit) {
          slide.trigger('toggle',!active);
        } else {

          // go back
          inner.animate({
            marginLeft: 0
          },opts.animate/2);
        }
      } else {

        // inactive
        if (diff > slideLimit) {
          slide.trigger('toggle',!active);
        } else {

          // go back again
          inner.animate({
            marginLeft: -width + height
          },opts.animate/2);
        }
      }

    };

    var wh = -width + height;

    blob.on('mousedown', function(e) {

      // reset diff
      diff = 0;

      blob.off('mouseup');
      slide.off('mouseleave');
      var cursor = e.pageX;

      toggle.on('mousemove', blob, function(e) {
        diff = e.pageX - cursor;
        var marginLeft;
        if (slide.hasClass('active')) {

          marginLeft = diff;

          // keep it within the limits
          if (diff > 0) marginLeft = 0;
          if (diff < wh) marginLeft = wh;
        } else {

          marginLeft = diff + wh;

          if (diff < 0) marginLeft = wh;
          if (diff > -wh) marginLeft = 0;

        }

        inner.css('margin-left',marginLeft);
      });

      blob.on('mouseup', upLeave);
      slide.on('mouseleave', upLeave);
    });

  });

};<|endoftext|>


<commit_before>(function () {
    'use strict';

    const config = function ($urlRouterProvider, $stateProvider, $locationProvider) {

        const tsUtils = require('ts-utils');

        ds.config.setConfig(WavesApp.network);
        ds.config.set('remappedAssetNames', WavesApp.remappedAssetNames);

        class AppConfig {

            constructor() {
                this._initUrlResolveMode();
                this._initLocalize();
                this._initAdapters();
                this._initStates();
            }

            _initAdapters() {

                const Transport = window.TransportNodeHid;

                ds.signAdapters.adapterList.forEach((Adapter) => Adapter.initOptions({
                    networkCode: WavesApp.network.code.charCodeAt(0),
                    openTimeout: WavesApp.sign.openTimeout,
                    listenTimeout: WavesApp.sign.listenTimeout,
                    exchangeTimeout: WavesApp.sign.exchangeTimeout,
                    debug: !WavesApp.isProduction(),
                    transport: Transport && Transport.default,
                    extension: () => typeof Waves === 'undefined' ? null : Waves
                }));
            }

            /**
             * @private
             */
            _initUrlResolveMode() {
                if (WavesApp.isWeb()) {
                    const base = document.createElement('base');
                    base.href = '/';
                    document.head.appendChild(base);
                    $locationProvider.html5Mode(true);
                }
                $urlRouterProvider.otherwise('/');
            }

            /**
             * @private
             */
            _initLocalize() {

                const BIG_NUMBER_FORMAT = {
                    decimalSeparator: '.',
                    groupSeparator: ',',
                    groupSize: 3,
                    secondaryGroupSize: 0,
                    fractionGroupSeparator: ' ',
                    fractionGroupSize: 0
                };

                i18next
                    .use(i18nextLocizeBackend)
                    .init({
                        lng: AppConfig.getUserLang(),
                        debug: !WavesApp.isProduction(),
                        ns: WavesApp.modules
                            .filter(
                                tsUtils.filterList(
                                    tsUtils.notContains('app.templates'),
                                    tsUtils.notContains('app.keeper'),
                                    tsUtils.notContains('app.wallet'),
                                    tsUtils.notContains('app.stand')
                                )
                            ),
                        fallbackLng: 'en',
                        whitelist: Object.keys(WavesApp.localize),
                        defaultNS: 'app',
                        useCookie: false,
                        useLocalStorage: false,
                        interpolation: {
                            format: function (value, format) {
                                switch (format) {
                                    case 'money':
                                        return value && value.getTokens().toFixed() || '';
                                    case 'money-currency':
                                        if (value) {
                                            return `${value.getTokens().toFixed()} ${value.asset.displayName}`;
                                        } else {
                                            return '';
                                        }
                                    case 'money-fee':
                                        return (
                                            value &&
                                            `${value.getTokens().toFixed()} ${value.asset.displayName}` ||
                                            ''
                                        );
                                    case 'BigNumber':
                                        return value && value.toFixed() || '';
                                    default:
                                        throw new Error('Wrong format type!');
                                }
                            }
                        },
                        backend: {
                            loadPath: `/locales/{{lng}}/{{ns}}.json?${WavesApp.version}`,
                            referenceLng: 'en'
                        }
                    });

                i18next.on('initialized', () => {
                    const localeData = WavesApp.getLocaleData().separators;

                    BigNumber.config({
                        ROUNDING_MODE: BigNumber.ROUND_DOWN,
                        FORMAT: tsUtils.merge(Object.create(null), BIG_NUMBER_FORMAT, {
                            groupSeparator: localeData.group,
                            decimalSeparator: localeData.decimal
                        })
                    });

                    if (WavesApp.isDesktop()) {
                        transfer('setLanguage', i18next.language);
                    }

                    i18next.on('languageChanged', () => {
                        if (WavesApp.isDesktop()) {
                            transfer('setLanguage', i18next.language);
                        }

                        const localeData = WavesApp.getLocaleData().separators;

                        BigNumber.config({
                            ROUNDING_MODE: BigNumber.ROUND_DOWN,
                            FORMAT: tsUtils.merge(Object.create(null), BIG_NUMBER_FORMAT, {
                                groupSeparator: localeData.group,
                                decimalSeparator: localeData.decimal
                            })
                        });
                    });
                });
            }

            /**
             * @private
             */
            _initStates() {

                const defaultUrl = AppConfig.getUrlFromState(WavesApp.stateTree.find('welcome'));
                $urlRouterProvider.when('', defaultUrl);

                WavesApp.stateTree.toArray()
                    .slice(1)
                    .forEach((item) => {
                        const abstract = item.get('abstract');
                        const url = AppConfig.getUrlFromState(item);
                        const redirectTo = item.get('redirectTo');
                        const reloadOnSearch = item.get('reloadOnSearch');

                        const views = item.get('views').reduce((views, viewData) => {
                            const controller = (
                                (abstract || viewData.noController) ?
                                    undefined :
                                    AppConfig.getCtrlName(item.get('controller') || tsUtils.camelCase(item.id))
                            );
                            const template = viewData.template;
                            const templateUrl = (
                                template ?
                                    undefined :
                                    (
                                        viewData.templateUrl ||
                                        AppConfig.getTemplateUrl(WavesApp.stateTree.getPath(item.id))
                                    )
                            );
                            views[viewData.name] = { controller, template, templateUrl };

                            return views;
                        }, Object.create(null));

                        $stateProvider.state(WavesApp.stateTree.getPath(item.id).join('.'), {
                            abstract,
                            url,
                            redirectTo,
                            views,
                            reloadOnSearch
                        });
                    });
            }

            static getCtrlName(name) {
                return `${name.charAt(0)
                    .toUpperCase() + name.substr(1)}Ctrl as $ctrl`;
            }

            static getTemplateUrl(path) {
                return path.filter((id) => !WavesApp.stateTree.find(id).get('abstract'))
                    .reduce((result, item, index, array) => {
                        item = tsUtils.camelCase(item);
                        if (index === array.length - 1) {
                            result += `/modules/${item}/templates/${item}.html`;
                        } else {
                            result += `/modules/${item}`;
                        }
                        return result;
                    }, '')
                    .replace(/\/\//g, '/')
                    .substr(1);
            }

            static getUserLang() {
                const available = Object.keys(WavesApp.localize);
                const langFromStorage = localStorage.getItem('lng');
                if (available.indexOf(langFromStorage) !== -1) {
                    return localStorage.getItem('lng');
                }
                const cookieLng = Cookies.get('locale');
                const userLang = navigator.language || navigator.userLanguage;

                if (available.indexOf(cookieLng) !== -1) {
                    return cookieLng;
                }

                if (!userLang) {
                    return 'en';
                } else if (available.indexOf(userLang) !== -1) {
                    return userLang;
                } else {
                    let lng = null;
                    userLang.split(/\W/).some((part) => {
                        if (available.indexOf(part) !== -1) {
                            lng = part;
                        } else if (available.indexOf(part.toLowerCase()) !== -1) {
                            lng = part.toLowerCase();
                        }
                        return !!lng;
                    });

                    return lng || 'en';
                }
            }

            /**
             * @param {BaseTree} state
             */
            static getUrlFromState(state) {
                return state.get('abstract') ? undefined : state.get('url') || `/${state.id}`;
            }

        }

        return new AppConfig();
    };

    config.$inject = ['$urlRouterProvider', '$stateProvider', '$locationProvider'];

    angular.module('app')
        .config(config);
})();
<commit_msg>CLIENT-1811: fix double storage<commit_after>(function () {
    'use strict';

    const config = function ($urlRouterProvider, $stateProvider, $locationProvider) {

        const tsUtils = require('ts-utils');

        ds.config.setConfig(WavesApp.network);
        ds.config.set('remappedAssetNames', WavesApp.remappedAssetNames);

        class AppConfig {

            constructor() {
                this._initUrlResolveMode();
                this._initLocalize();
                this._initAdapters();
                this._initStates();
            }

            _initAdapters() {

                const Transport = window.TransportNodeHid;

                ds.signAdapters.adapterList.forEach((Adapter) => Adapter.initOptions({
                    networkCode: WavesApp.network.code.charCodeAt(0),
                    openTimeout: WavesApp.sign.openTimeout,
                    listenTimeout: WavesApp.sign.listenTimeout,
                    exchangeTimeout: WavesApp.sign.exchangeTimeout,
                    debug: !WavesApp.isProduction(),
                    transport: Transport && Transport.default,
                    extension: () => typeof Waves === 'undefined' ? null : Waves
                }));
            }

            /**
             * @private
             */
            _initUrlResolveMode() {
                if (WavesApp.isWeb()) {
                    const base = document.createElement('base');
                    base.href = '/';
                    document.head.appendChild(base);
                    $locationProvider.html5Mode(true);
                }
                $urlRouterProvider.otherwise('/');
            }

            /**
             * @private
             */
            _initLocalize() {

                const BIG_NUMBER_FORMAT = {
                    decimalSeparator: '.',
                    groupSeparator: ',',
                    groupSize: 3,
                    secondaryGroupSize: 0,
                    fractionGroupSeparator: ' ',
                    fractionGroupSize: 0
                };

                i18next
                    .use(i18nextLocizeBackend)
                    .init({
                        lng: AppConfig.getUserLang(),
                        debug: !WavesApp.isProduction(),
                        ns: WavesApp.modules
                            .filter(
                                tsUtils.filterList(
                                    tsUtils.notContains('app.templates'),
                                    tsUtils.notContains('app.keeper'),
                                    tsUtils.notContains('app.wallet'),
                                    tsUtils.notContains('app.stand')
                                )
                            ),
                        fallbackLng: 'en',
                        whitelist: Object.keys(WavesApp.localize),
                        defaultNS: 'app',
                        useCookie: false,
                        useLocalStorage: false,
                        interpolation: {
                            format: function (value, format) {
                                switch (format) {
                                    case 'money':
                                        return value && value.getTokens().toFixed() || '';
                                    case 'money-currency':
                                        if (value) {
                                            return `${value.getTokens().toFixed()} ${value.asset.displayName}`;
                                        } else {
                                            return '';
                                        }
                                    case 'money-fee':
                                        return (
                                            value &&
                                            `${value.getTokens().toFixed()} ${value.asset.displayName}` ||
                                            ''
                                        );
                                    case 'BigNumber':
                                        return value && value.toFixed() || '';
                                    default:
                                        throw new Error('Wrong format type!');
                                }
                            }
                        },
                        backend: {
                            loadPath: `/locales/{{lng}}/{{ns}}.json?${WavesApp.version}`,
                            referenceLng: 'en'
                        }
                    });

                i18next.on('initialized', () => {
                    const localeData = WavesApp.getLocaleData().separators;

                    BigNumber.config({
                        ROUNDING_MODE: BigNumber.ROUND_DOWN,
                        FORMAT: tsUtils.merge(Object.create(null), BIG_NUMBER_FORMAT, {
                            groupSeparator: localeData.group,
                            decimalSeparator: localeData.decimal
                        })
                    });

                    if (WavesApp.isDesktop()) {
                        transfer('setLanguage', i18next.language);
                    }

                    i18next.on('languageChanged', () => {
                        if (WavesApp.isDesktop()) {
                            transfer('setLanguage', i18next.language);
                        }

                        const localeData = WavesApp.getLocaleData().separators;

                        BigNumber.config({
                            ROUNDING_MODE: BigNumber.ROUND_DOWN,
                            FORMAT: tsUtils.merge(Object.create(null), BIG_NUMBER_FORMAT, {
                                groupSeparator: localeData.group,
                                decimalSeparator: localeData.decimal
                            })
                        });
                    });
                });
            }

            /**
             * @private
             */
            _initStates() {

                const defaultUrl = AppConfig.getUrlFromState(WavesApp.stateTree.find('welcome'));
                $urlRouterProvider.when('', defaultUrl);

                WavesApp.stateTree.toArray()
                    .slice(1)
                    .forEach((item) => {
                        const abstract = item.get('abstract');
                        const url = AppConfig.getUrlFromState(item);
                        const redirectTo = item.get('redirectTo');
                        const reloadOnSearch = item.get('reloadOnSearch');

                        const views = item.get('views').reduce((views, viewData) => {
                            const controller = (
                                (abstract || viewData.noController) ?
                                    undefined :
                                    AppConfig.getCtrlName(item.get('controller') || tsUtils.camelCase(item.id))
                            );
                            const template = viewData.template;
                            const templateUrl = (
                                template ?
                                    undefined :
                                    (
                                        viewData.templateUrl ||
                                        AppConfig.getTemplateUrl(WavesApp.stateTree.getPath(item.id))
                                    )
                            );
                            views[viewData.name] = { controller, template, templateUrl };

                            return views;
                        }, Object.create(null));

                        $stateProvider.state(WavesApp.stateTree.getPath(item.id).join('.'), {
                            abstract,
                            url,
                            redirectTo,
                            views,
                            reloadOnSearch
                        });
                    });
            }

            static getCtrlName(name) {
                return `${name.charAt(0)
                    .toUpperCase() + name.substr(1)}Ctrl as $ctrl`;
            }

            static getTemplateUrl(path) {
                return path.filter((id) => !WavesApp.stateTree.find(id).get('abstract'))
                    .reduce((result, item, index, array) => {
                        item = tsUtils.camelCase(item);
                        if (index === array.length - 1) {
                            result += `/modules/${item}/templates/${item}.html`;
                        } else {
                            result += `/modules/${item}`;
                        }
                        return result;
                    }, '')
                    .replace(/\/\//g, '/')
                    .substr(1);
            }

            static getUserLang() {
                const available = Object.keys(WavesApp.localize);
                const langFromStorage = localStorage.getItem('lng');
                if (available.indexOf(langFromStorage) !== -1) {
                    return langFromStorage;
                }
                const cookieLng = Cookies.get('locale');
                const userLang = navigator.language || navigator.userLanguage;

                if (available.indexOf(cookieLng) !== -1) {
                    return cookieLng;
                }

                if (!userLang) {
                    return 'en';
                } else if (available.indexOf(userLang) !== -1) {
                    return userLang;
                } else {
                    let lng = null;
                    userLang.split(/\W/).some((part) => {
                        if (available.indexOf(part) !== -1) {
                            lng = part;
                        } else if (available.indexOf(part.toLowerCase()) !== -1) {
                            lng = part.toLowerCase();
                        }
                        return !!lng;
                    });

                    return lng || 'en';
                }
            }

            /**
             * @param {BaseTree} state
             */
            static getUrlFromState(state) {
                return state.get('abstract') ? undefined : state.get('url') || `/${state.id}`;
            }

        }

        return new AppConfig();
    };

    config.$inject = ['$urlRouterProvider', '$stateProvider', '$locationProvider'];

    angular.module('app')
        .config(config);
})();
<|endoftext|>


<commit_before>import getInventoryById from '../ajax/getInventoryById';
import getItemImg from '../common/getItemImg';
import {itemRE} from '../support/dataObj';
import showQuickWear from './quickwear';
import * as layout from '../support/layout';
import * as system from '../support/system';

var content;
var scrapedItems;
var folders;
var invItems;

function getItems(doc) {
  var imgList = getItemImg(doc.getElementById('pCC'));
  Array.prototype.forEach.call(imgList, function(img) {
    var thisItem = itemRE.exec(img.dataset.tipped);
    if (!scrapedItems[thisItem[2]]) {
      scrapedItems[thisItem[2]] = {
        n: img.parentNode.parentNode.nextElementSibling.textContent.trim(),
        src: img.src,
        tip: img.dataset.tipped
      };
    }
  });
  return scrapedItems;
}

function queryFolders(doc) {
  return doc.querySelectorAll('a[href*="folder_id="]');
}

function folderNotActive(fldr) {
  return fldr.firstChild.src.indexOf('folder_on.gif') === -1;
}

function displayProgress(msg) {
  content.insertAdjacentHTML('beforeend', msg + '
');
}

function folderProgress(folderName) {
  displayProgress('Checking folder ' + folderName + '...');
}

function folderText(fldr) {
  return fldr.parentNode.textContent;
}

function gotOtherPage(folderName, html) {
  folderProgress(folderName);
  getItems(system.createDocument(html));
}

function gotSecondPage(folderName, html) {
  folderProgress(folderName);
  var doc = system.createDocument(html);
  var myFolders = queryFolders(doc);
  if (folderNotActive(myFolders[1])) {
    displayProgress('No more folders...');
    return html;
  }
  getItems(doc);
  var prm = [];
  for (var i = 2; i < myFolders.length; i += 1) {
    prm.push($.get(myFolders[i].href)
      .done(gotOtherPage.bind(null, folderText(myFolders[i]))));
  }
  return $.when.apply($, prm);
}

function gotGuildStoreItems(html) {
  displayProgress('Checking Guild Store Items...');
  getItems(system.createDocument(html));
}

function getGuildStoreItems() {
  return $.get('index.php?cmd=guild&subcmd=inventory&subcmd2=storeitems')
    .done(gotGuildStoreItems);
}

function imDone() {
  displayProgress('I\'m done.');
  var itemAry = Object.keys(scrapedItems).map(function(invId) {
    return {
      a: invId,
      eq: Number(invItems[invId].type) < 9,
      f: Number(invItems[invId].folder_id),
      n: scrapedItems[invId].n,
      src: scrapedItems[invId].src,
      tip: scrapedItems[invId].tip,
      u: ['10', '12', '15', '16'].indexOf(invItems[invId].type) !== -1 ||
        scrapedItems[invId].n === 'Zombie Coffin'
    };
  });
  showQuickWear(content, itemAry, folders);
}

function gotInv(data) {
  displayProgress('Checking Inventory...');
  // console.log('Inventory', data);
  folders = data.folders;
  invItems = data.items;
}

function gotFirstPage(html) {
  folderProgress('Main');
  var doc = system.createDocument(html);
  var myFolders = queryFolders(doc);
  if (folderNotActive(myFolders[0])) {
    displayProgress('Something has gone horribly wrong!');
    return;
  }
  var prm = [getInventoryById().done(gotInv)];
  if (myFolders[1]) {
    var fn = gotSecondPage.bind(null, folderText(myFolders[1]));
    prm.push($.get(myFolders[1].href).pipe(fn));
  }
  getItems(doc);
  $.when.apply($, prm).pipe(getGuildStoreItems).done(imDone);
}

export default function insertQuickWear(injector) {
  content = injector || layout.pCC;
  if (!content) {return;}
  displayProgress('Getting item list from backpack...');
  scrapedItems = {};
  $.get('index.php?cmd=profile&subcmd=dropitems')
    .done(gotFirstPage);
}
<commit_msg>Build 15.19.26 - correct capitalisation for Linux<commit_after>import getInventoryById from '../ajax/getInventoryById';
import getItemImg from '../common/getItemImg';
import {itemRE} from '../support/dataObj';
import showQuickWear from './quickWear';
import * as layout from '../support/layout';
import * as system from '../support/system';

var content;
var scrapedItems;
var folders;
var invItems;

function getItems(doc) {
  var imgList = getItemImg(doc.getElementById('pCC'));
  Array.prototype.forEach.call(imgList, function(img) {
    var thisItem = itemRE.exec(img.dataset.tipped);
    if (!scrapedItems[thisItem[2]]) {
      scrapedItems[thisItem[2]] = {
        n: img.parentNode.parentNode.nextElementSibling.textContent.trim(),
        src: img.src,
        tip: img.dataset.tipped
      };
    }
  });
  return scrapedItems;
}

function queryFolders(doc) {
  return doc.querySelectorAll('a[href*="folder_id="]');
}

function folderNotActive(fldr) {
  return fldr.firstChild.src.indexOf('folder_on.gif') === -1;
}

function displayProgress(msg) {
  content.insertAdjacentHTML('beforeend', msg + '
');
}

function folderProgress(folderName) {
  displayProgress('Checking folder ' + folderName + '...');
}

function folderText(fldr) {
  return fldr.parentNode.textContent;
}

function gotOtherPage(folderName, html) {
  folderProgress(folderName);
  getItems(system.createDocument(html));
}

function gotSecondPage(folderName, html) {
  folderProgress(folderName);
  var doc = system.createDocument(html);
  var myFolders = queryFolders(doc);
  if (folderNotActive(myFolders[1])) {
    displayProgress('No more folders...');
    return html;
  }
  getItems(doc);
  var prm = [];
  for (var i = 2; i < myFolders.length; i += 1) {
    prm.push($.get(myFolders[i].href)
      .done(gotOtherPage.bind(null, folderText(myFolders[i]))));
  }
  return $.when.apply($, prm);
}

function gotGuildStoreItems(html) {
  displayProgress('Checking Guild Store Items...');
  getItems(system.createDocument(html));
}

function getGuildStoreItems() {
  return $.get('index.php?cmd=guild&subcmd=inventory&subcmd2=storeitems')
    .done(gotGuildStoreItems);
}

function imDone() {
  displayProgress('I\'m done.');
  var itemAry = Object.keys(scrapedItems).map(function(invId) {
    return {
      a: invId,
      eq: Number(invItems[invId].type) < 9,
      f: Number(invItems[invId].folder_id),
      n: scrapedItems[invId].n,
      src: scrapedItems[invId].src,
      tip: scrapedItems[invId].tip,
      u: ['10', '12', '15', '16'].indexOf(invItems[invId].type) !== -1 ||
        scrapedItems[invId].n === 'Zombie Coffin'
    };
  });
  showQuickWear(content, itemAry, folders);
}

function gotInv(data) {
  displayProgress('Checking Inventory...');
  // console.log('Inventory', data);
  folders = data.folders;
  invItems = data.items;
}

function gotFirstPage(html) {
  folderProgress('Main');
  var doc = system.createDocument(html);
  var myFolders = queryFolders(doc);
  if (folderNotActive(myFolders[0])) {
    displayProgress('Something has gone horribly wrong!');
    return;
  }
  var prm = [getInventoryById().done(gotInv)];
  if (myFolders[1]) {
    var fn = gotSecondPage.bind(null, folderText(myFolders[1]));
    prm.push($.get(myFolders[1].href).pipe(fn));
  }
  getItems(doc);
  $.when.apply($, prm).pipe(getGuildStoreItems).done(imDone);
}

export default function insertQuickWear(injector) {
  content = injector || layout.pCC;
  if (!content) {return;}
  displayProgress('Getting item list from backpack...');
  scrapedItems = {};
  $.get('index.php?cmd=profile&subcmd=dropitems')
    .done(gotFirstPage);
}
<|endoftext|>