text: /* global mozContact, oauthflow, ImportStatusData */
  'use strict';
  
  var utils = window.utils || {};
  
  // Scale ratio for different devices
  var SCALE_RATIO = window.devicePixelRatio || 1;
  
  var LAST_IMPORT_TIMESTAMP_SUFFIX = '_last_import_timestamp';
  
  function scale(size) {
    return Math.round(SCALE_RATIO * size);
  }
  
  if (!utils.misc) {
    utils.misc = {};
    utils.misc.toMozContact = function(contact) {
      var outContact = contact;
      if (!(contact instanceof mozContact)) {
        outContact = new mozContact(contact);
        outContact.id = contact.id || outContact.id;
      }
  
      return outContact;
    };
  
    // This year indicates that the year can be ignored
    const FLAG_YEAR_IGNORED = 9996;
    utils.misc.FLAG_YEAR_IGNORED = FLAG_YEAR_IGNORED;
  
    utils.misc.formatDate = function(date, customFormat) {
      var dateString = null;
  
      try {
        var format = customFormat || {
          month: 'short',
          day: 'numeric'
        };
  
        var offset = date.getTimezoneOffset() * 60 * 1000;
        var normalizedDate = new Date(date.getTime() + offset);
  
        var year = normalizedDate.getFullYear();
        if (year !== FLAG_YEAR_IGNORED) {
          format.year = 'numeric';
        }
        var f = new Intl.DateTimeFormat(navigator.languages, format);
  
        dateString = f.format(date);
      } catch (err) {
        console.error('Error parsing date: ', err);
        throw err;
      }
  
      return dateString;
    };
  
    // Parses a name to obtain a givenName and a familyName
    // This is useful while importing contacts from SIM card
    utils.misc.parseName = function(nameString) {
      // Minimum length to consider a token as significative
      var MIN_LENGHT_SIGNIFICATIVE = 3;
      // Minimum length of a token to consider it a familyName
      var MIN_LENGHT_FN = 2;
  
      var out = {
        givenName: '',
        familyName: ''
      };
  
      if (!nameString) {
        return out;
      }
      var str = nameString.trim();
      if (!str) {
        return out;
      }
  
      function startsWithUpper(str) {
        var firstLetter = str.charAt(0);
        var capFirstLetter = firstLetter.toLocaleUpperCase();
  
        return (capFirstLetter === firstLetter);
      }
  
      // First step of the algorithm is to split the name in its tokens
      var tokens = nameString.split(/\s+/);
      var significativeTokens = {};
      for (var j = 0; j < tokens.length; j++) {
        var token = tokens[j];
        // A significative token os one with that min length and which starts with
        // uppercase letter
        if (token.length > MIN_LENGHT_SIGNIFICATIVE || startsWithUpper(token)) {
          significativeTokens[token] = true;
        }
      }
  
      var totalTokens = tokens.length;
      var lastToken = totalTokens - 1;
      var rolePrevToken;
      var remainingTokens = Object.keys(significativeTokens).length;
      var outGivenNames = [], outFamilyNames = [];
      // We need to keep the totals in a different variable as a familyName
      // can be composed by more than one token
      var numFns = 0;
  
      while (lastToken >= 0) {
        if (significativeTokens[tokens[lastToken]]) {
          remainingTokens--;
        }
  
        var currToken = tokens[lastToken],
            nextToken = tokens[lastToken + 1],
            prevToken = tokens[lastToken - 1];
  
        // The last element is taken as part of the family name
        if (!nextToken && prevToken) {
            outFamilyNames.push(currToken);
            numFns++;
            rolePrevToken = 'FN';
        }
        else if (!prevToken) {
          outGivenNames.push(currToken);
          rolePrevToken = 'GN';
        }
        else if (nextToken) {
          if (!significativeTokens[currToken]) {
            if (rolePrevToken === 'FN') {
              // Here the number of fns is not incremented as this is a non
              // significative token
              outFamilyNames.push(currToken);
            }
            else {
              outGivenNames.push(currToken);
            }
          }
          else if (currToken.length < MIN_LENGHT_FN) {
            outGivenNames.push(currToken);
            rolePrevToken = 'GN';
          }
          else {
            // Number of familyNames will be two at most
            // Preference is given to the givenName over familyName
            // We prefer two givenNames instead of two family Names
            if (remainingTokens >= 2 && numFns < 2) {
              outFamilyNames.push(currToken);
              numFns++;
              rolePrevToken = 'FN';
            }
            else {
              outGivenNames.push(currToken);
              rolePrevToken = 'GN';
            }
          }
        }
  
        lastToken--;
      }
  
      out.givenName = outGivenNames.reverse().join(' ').trim();
      out.familyName = outFamilyNames.reverse().join(' ').trim();
  
      return out;
    };
  
    utils.misc.getPreferredPictureBox = function() {
      var imgThumbSize = oauthflow.params.facebook.imgThumbSize;
      var out = {
        width: scale(imgThumbSize)
      };
  
      out.height = out.width;
  
      return out;
    };
  
    utils.misc.getPreferredPictureDetail = function() {
      var imgDetailWidth = oauthflow.params.facebook.imgDetailWidth;
      return scale(imgDetailWidth);
    };
  
    utils.misc.setTimestamp = function(type, callback) {
      ImportStatusData.put(type + LAST_IMPORT_TIMESTAMP_SUFFIX, Date.now())
          .then(callback);
    };
  
    utils.misc.getTimestamp = function(type, callback) {
      ImportStatusData.get(type + LAST_IMPORT_TIMESTAMP_SUFFIX)
          .then(callback);
    };
  }
  