API Docs for: v3.5.2
Show:

File: node_modules/ember-inflector/addon/lib/system/inflector.js

import { capitalize } from '@ember/string';

const BLANK_REGEX = /^\s*$/;
const LAST_WORD_DASHED_REGEX = /([\w/-]+[_/\s-])([a-z\d]+$)/;
const LAST_WORD_CAMELIZED_REGEX = /([\w/\s-]+)([A-Z][a-z\d]*$)/;
const CAMELIZED_REGEX = /[A-Z][a-z\d]*$/;

function loadUncountable(rules, uncountable) {
  for (let i = 0, length = uncountable.length; i < length; i++) {
    rules.uncountable[uncountable[i].toLowerCase()] = true;
  }
}

function loadIrregular(rules, irregularPairs) {
  let pair;

  for (let i = 0, length = irregularPairs.length; i < length; i++) {
    pair = irregularPairs[i];

    //pluralizing
    rules.irregular[pair[0].toLowerCase()] = pair[1];
    rules.irregular[pair[1].toLowerCase()] = pair[1];

    //singularizing
    rules.irregularInverse[pair[1].toLowerCase()] = pair[0];
    rules.irregularInverse[pair[0].toLowerCase()] = pair[0];
  }
}

/**
  Inflector.Ember provides a mechanism for supplying inflection rules for your
  application. Ember includes a default set of inflection rules, and provides an
  API for providing additional rules.

  Examples:

  Creating an inflector with no rules.

  ```js
  var inflector = new Ember.Inflector();
  ```

  Creating an inflector with the default ember ruleset.

  ```js
  var inflector = new Ember.Inflector(Ember.Inflector.defaultRules);

  inflector.pluralize('cow'); //=> 'kine'
  inflector.singularize('kine'); //=> 'cow'
  ```

  Creating an inflector and adding rules later.

  ```javascript
  var inflector = Ember.Inflector.inflector;

  inflector.pluralize('advice'); // => 'advices'
  inflector.uncountable('advice');
  inflector.pluralize('advice'); // => 'advice'

  inflector.pluralize('formula'); // => 'formulas'
  inflector.irregular('formula', 'formulae');
  inflector.pluralize('formula'); // => 'formulae'

  // you would not need to add these as they are the default rules
  inflector.plural(/$/, 's');
  inflector.singular(/s$/i, '');
  ```

  Creating an inflector with a nondefault ruleset.

  ```javascript
  var rules = {
    plurals:  [
      [ /$/, 's' ]
    ],
    singular: [
      [ /\s$/, '' ]
    ],
    irregularPairs: [
      [ 'cow', 'kine' ]
    ],
    uncountable: [ 'fish' ]
  };

  var inflector = new Ember.Inflector(rules);
  ```

  @class Inflector
  @namespace Ember
*/
function Inflector(ruleSet) {
  ruleSet = ruleSet || {};
  ruleSet.uncountable = ruleSet.uncountable || makeDictionary();
  ruleSet.irregularPairs = ruleSet.irregularPairs || makeDictionary();

  const rules = this.rules = {
    plurals:  ruleSet.plurals || [],
    singular: ruleSet.singular || [],
    irregular: makeDictionary(),
    irregularInverse: makeDictionary(),
    uncountable: makeDictionary()
  };

  loadUncountable(rules, ruleSet.uncountable);
  loadIrregular(rules, ruleSet.irregularPairs);

  this.enableCache();
}

if (!Object.create && !Object.create(null).hasOwnProperty) {
  throw new Error("This browser does not support Object.create(null), please polyfil with es5-sham: http://git.io/yBU2rg");
}

function makeDictionary() {
  var cache = Object.create(null);
  cache['_dict'] = null;
  delete cache['_dict'];
  return cache;
}

Inflector.prototype = {
  /**
    @public

    As inflections can be costly, and commonly the same subset of words are repeatedly
    inflected an optional cache is provided.

    @method enableCache
  */
  enableCache() {
    this.purgeCache();

    this.singularize = function(word) {
      this._cacheUsed = true;
      return this._sCache[word] || (this._sCache[word] = this._singularize(word));
    };

    this.pluralize = function(numberOrWord, word, options = {}) {
      this._cacheUsed = true;
      var cacheKey = [numberOrWord, word, options.withoutCount]
      return this._pCache[cacheKey] || (this._pCache[cacheKey] = this._pluralize(numberOrWord, word, options));
    };
  },

  /**
    @public

    @method purgedCache
  */
  purgeCache() {
    this._cacheUsed = false;
    this._sCache = makeDictionary();
    this._pCache = makeDictionary();
  },

  /**
    @public
    disable caching

    @method disableCache;
  */
  disableCache() {
    this._sCache = null;
    this._pCache = null;
    this.singularize = function(word) {
      return this._singularize(word);
    };

    this.pluralize = function() {
      return this._pluralize(...arguments);
    };
  },

  /**
    @method plural
    @param {RegExp} regex
    @param {String} string
  */
  plural(regex, string) {
    if (this._cacheUsed) { this.purgeCache(); }
    this.rules.plurals.push([regex, string.toLowerCase()]);
  },

  /**
    @method singular
    @param {RegExp} regex
    @param {String} string
  */
  singular(regex, string) {
    if (this._cacheUsed) { this.purgeCache(); }
    this.rules.singular.push([regex, string.toLowerCase()]);
  },

  /**
    @method uncountable
    @param {String} regex
  */
  uncountable(string) {
    if (this._cacheUsed) { this.purgeCache(); }
    loadUncountable(this.rules, [string.toLowerCase()]);
  },

  /**
    @method irregular
    @param {String} singular
    @param {String} plural
  */
  irregular(singular, plural) {
    if (this._cacheUsed) { this.purgeCache(); }
    loadIrregular(this.rules, [[singular, plural]]);
  },

  /**
    @method pluralize
    @param {String} word
  */
  pluralize() {
    return this._pluralize(...arguments);
  },

  _pluralize(wordOrCount, word, options = {}) {
    if (word === undefined) {
     return this.inflect(wordOrCount, this.rules.plurals, this.rules.irregular);
    }

    if (parseFloat(wordOrCount) !== 1) {
      word = this.inflect(word, this.rules.plurals, this.rules.irregular);
    }

    return options.withoutCount ? word : `${wordOrCount} ${word}`;
  },
  /**
    @method singularize
    @param {String} word
  */
  singularize(word) {
    return this._singularize(word);
  },

  _singularize(word) {
    return this.inflect(word, this.rules.singular,  this.rules.irregularInverse);
  },

  /**
    @protected

    @method inflect
    @param {String} word
    @param {Object} typeRules
    @param {Object} irregular
  */
  inflect(word, typeRules, irregular) {
    let inflection, substitution, result, lowercase, wordSplit,
      lastWord, isBlank, isCamelized, rule, isUncountable;

    isBlank = !word || BLANK_REGEX.test(word);
    isCamelized = CAMELIZED_REGEX.test(word);

    if (isBlank) {
      return word;
    }

    lowercase = word.toLowerCase();
    wordSplit = LAST_WORD_DASHED_REGEX.exec(word) || LAST_WORD_CAMELIZED_REGEX.exec(word);

    if (wordSplit){
      lastWord = wordSplit[2].toLowerCase();
    }

    isUncountable = this.rules.uncountable[lowercase] || this.rules.uncountable[lastWord];

    if (isUncountable) {
      return word;
    }

    for (rule in irregular) {
      if (lowercase.match(rule+"$")) {
        substitution = irregular[rule];

        if (isCamelized && irregular[lastWord]) {
          substitution = capitalize(substitution);
          rule = capitalize(rule);
        }

        return word.replace(new RegExp(rule, 'i'), substitution);
      }
    }

    for (var i = typeRules.length, min = 0; i > min; i--) {
      inflection = typeRules[i-1];
      rule = inflection[0];

      if (rule.test(word)) {
        break;
      }
    }

    inflection = inflection || [];

    rule = inflection[0];
    substitution = inflection[1];

    result = word.replace(rule, substitution);

    return result;
  }
};

export default Inflector;