text: /**
   * Minifies and unminifies configs by replacing frequent keys
   * and values with one letter substitutes. Config options must
   * retain array position/index, add new options at the end.
   *
   * @constructor
   */
  lm.utils.ConfigMinifier = function () {
    this._keys = [
      'settings',
      'hasHeaders',
      'constrainDragToContainer',
      'selectionEnabled',
      'dimensions',
      'borderWidth',
      'minItemHeight',
      'minItemWidth',
      'headerHeight',
      'dragProxyWidth',
      'dragProxyHeight',
      'labels',
      'close',
      'maximise',
      'minimise',
      'popout',
      'content',
      'componentName',
      'componentState',
      'id',
      'width',
      'type',
      'height',
      'isClosable',
      'title',
      'popoutWholeStack',
      'openPopouts',
      'parentId',
      'activeItemIndex',
      'reorderEnabled',
      'borderGrabWidth',
  
      //Maximum 36 entries, do not cross this line!
    ];
    if (this._keys.length > 36) {
      throw new Error('Too many keys in config minifier map');
    }
  
    this._values = [
      true,
      false,
      'row',
      'column',
      'stack',
      'component',
      'close',
      'maximise',
      'minimise',
      'open in new window',
    ];
  };
  
  lm.utils.copy(lm.utils.ConfigMinifier.prototype, {
    /**
     * Takes a GoldenLayout configuration object and
     * replaces its keys and values recursively with
     * one letter counterparts
     *
     * @param   {Object} config A GoldenLayout config object
     *
     * @returns {Object} minified config
     */
    minifyConfig: function (config) {
      var min = {};
      this._nextLevel(config, min, '_min');
      return min;
    },
  
    /**
     * Takes a configuration Object that was previously minified
     * using minifyConfig and returns its original version
     *
     * @param   {Object} minifiedConfig
     *
     * @returns {Object} the original configuration
     */
    unminifyConfig: function (minifiedConfig) {
      var orig = {};
      this._nextLevel(minifiedConfig, orig, '_max');
      return orig;
    },
  
    /**
     * Recursive function, called for every level of the config structure
     *
     * @param   {Array|Object} orig
     * @param   {Array|Object} min
     * @param    {String} translationFn
     *
     * @returns {void}
     */
    _nextLevel: function (from, to, translationFn) {
      var key, minKey;
  
      for (key in from) {
        /**
         * For in returns array indices as keys, so let's cast them to numbers
         */
        if (from instanceof Array) key = parseInt(key, 10);
  
        /**
         * In case something has extended Object prototypes
         */
        if (!from.hasOwnProperty(key)) continue;
  
        /**
         * Translate the key to a one letter substitute
         */
        minKey = this[translationFn](key, this._keys);
  
        /**
         * For Arrays and Objects, create a new Array/Object
         * on the minified object and recurse into it
         */
        if (typeof from[key] === 'object') {
          to[minKey] = from[key] instanceof Array ? [] : {};
          this._nextLevel(from[key], to[minKey], translationFn);
  
          /**
           * For primitive values (Strings, Numbers, Boolean etc.)
           * minify the value
           */
        } else {
          to[minKey] = this[translationFn](from[key], this._values);
        }
      }
    },
  
    /**
     * Minifies value based on a dictionary
     *
     * @param   {String|Boolean} value
     * @param   {Array<String|Boolean>} dictionary
     *
     * @returns {String} The minified version
     */
    _min: function (value, dictionary) {
      /**
       * If a value actually is a single character, prefix it
       * with ___ to avoid mistaking it for a minification code
       */
      if (typeof value === 'string' && value.length === 1) {
        return '___' + value;
      }
  
      var index = lm.utils.indexOf(value, dictionary);
  
      /**
       * value not found in the dictionary, return it unmodified
       */
      if (index === -1) {
        return value;
  
        /**
         * value found in dictionary, return its base36 counterpart
         */
      } else {
        return index.toString(36);
      }
    },
  
    _max: function (value, dictionary) {
      /**
       * value is a single character. Assume that it's a translation
       * and return the original value from the dictionary
       */
      if (typeof value === 'string' && value.length === 1) {
        return dictionary[parseInt(value, 36)];
      }
  
      /**
       * value originally was a single character and was prefixed with ___
       * to avoid mistaking it for a translation. Remove the prefix
       * and return the original character
       */
      if (typeof value === 'string' && value.substr(0, 3) === '___') {
        return value[3];
      }
      /**
       * value was not minified
       */
      return value;
    },
  });
  