text: /**
  @module ember
  @submodule ember-application
  */
  
  import { dictionary } from 'ember-utils';
  import { assert, info, get } from 'ember-metal';
  import {
    String as StringUtils,
    Object as EmberObject,
    Namespace
  } from 'ember-runtime';
  import validateType from '../utils/validate-type';
  import { getTemplate } from 'ember-glimmer';
  
  export const Resolver = EmberObject.extend({
    /*
      This will be set to the Application instance when it is
      created.
  
      @property namespace
    */
    namespace: null,
    normalize:         null, // required
    resolve:           null, // required
    parseName:         null, // required
    lookupDescription: null, // required
    makeToString:      null, // required
    resolveOther:      null, // required
    _logLookup:        null  // required
  });
  
  /**
    The DefaultResolver defines the default lookup rules to resolve
    container lookups before consulting the container for registered
    items:
  
    * templates are looked up on `Ember.TEMPLATES`
    * other names are looked up on the application after converting
      the name. For example, `controller:post` looks up
      `App.PostController` by default.
    * there are some nuances (see examples below)
  
    ### How Resolving Works
  
    The container calls this object's `resolve` method with the
    `fullName` argument.
  
    It first parses the fullName into an object using `parseName`.
  
    Then it checks for the presence of a type-specific instance
    method of the form `resolve[Type]` and calls it if it exists.
    For example if it was resolving 'template:post', it would call
    the `resolveTemplate` method.
  
    Its last resort is to call the `resolveOther` method.
  
    The methods of this object are designed to be easy to override
    in a subclass. For example, you could enhance how a template
    is resolved like so:
  
    ```javascript
    App = Ember.Application.create({
      Resolver: Ember.DefaultResolver.extend({
        resolveTemplate: function(parsedName) {
          let resolvedTemplate = this._super(parsedName);
          if (resolvedTemplate) { return resolvedTemplate; }
          return Ember.TEMPLATES['not_found'];
        }
      })
    });
    ```
  
    Some examples of how names are resolved:
  
    ```
    'template:post'           //=> Ember.TEMPLATES['post']
    'template:posts/byline'   //=> Ember.TEMPLATES['posts/byline']
    'template:posts.byline'   //=> Ember.TEMPLATES['posts/byline']
    'template:blogPost'       //=> Ember.TEMPLATES['blogPost']
                              //   OR
                              //   Ember.TEMPLATES['blog_post']
    'controller:post'         //=> App.PostController
    'controller:posts.index'  //=> App.PostsIndexController
    'controller:blog/post'    //=> Blog.PostController
    'controller:basic'        //=> Ember.Controller
    'route:post'              //=> App.PostRoute
    'route:posts.index'       //=> App.PostsIndexRoute
    'route:blog/post'         //=> Blog.PostRoute
    'route:basic'             //=> Ember.Route
    'view:post'               //=> App.PostView
    'view:posts.index'        //=> App.PostsIndexView
    'view:blog/post'          //=> Blog.PostView
    'view:basic'              //=> Ember.View
    'foo:post'                //=> App.PostFoo
    'model:post'              //=> App.Post
    ```
  
    @class DefaultResolver
    @namespace Ember
    @extends Ember.Object
    @public
  */
  
  export default EmberObject.extend({
    /**
      This will be set to the Application instance when it is
      created.
  
      @property namespace
      @public
    */
    namespace: null,
  
    init() {
      this._parseNameCache = dictionary(null);
    },
    normalize(fullName) {
      var [
        type,
        name
      ] = fullName.split(':', 2);
  
      assert(
        'Tried to normalize a container name without a colon (:) in it. ' +
        'You probably tried to lookup a name that did not contain a type, ' +
        'a colon, and a name. A proper lookup name would be `view:post`.',
        fullName.split(':').length === 2
      );
  
      if (type !== 'template') {
        var result = name;
  
        if (result.indexOf('.') > -1) {
          result = result.replace(/\.(.)/g, function(m) {
            return m.charAt(1).toUpperCase();
          });
        }
  
        if (name.indexOf('_') > -1) {
          result = result.replace(/_(.)/g, function(m) {
            return m.charAt(1).toUpperCase();
          });
        }
  
        if (name.indexOf('-') > -1) {
          result = result.replace(/-(.)/g, function(m) {
            return m.charAt(1).toUpperCase();
          });
        }
  
        return type + ':' + result;
      } else {
        return fullName;
      }
    },
  
  
    /**
      This method is called via the container's resolver method.
      It parses the provided `fullName` and then looks up and
      returns the appropriate template or class.
  
      @method resolve
      @param {String} fullName the lookup string
      @return {Object} the resolved factory
      @public
    */
    resolve(fullName) {
      var parsedName = this.parseName(fullName);
      var resolveMethodName = parsedName.resolveMethodName;
      var resolved;
  
      if (this[resolveMethodName]) {
        resolved = this[resolveMethodName](parsedName);
      }
  
      resolved = resolved || this.resolveOther(parsedName);
  
      if (parsedName.root && parsedName.root.LOG_RESOLVER) {
        this._logLookup(resolved, parsedName);
      }
  
      if (resolved) {
        validateType(resolved, parsedName);
      }
  
      return resolved;
    },
  
    /**
      Convert the string name of the form 'type:name' to
      a Javascript object with the parsed aspects of the name
      broken out.
  
      @param {String} fullName the lookup string
      @method parseName
      @protected
    */
  
    parseName(fullName) {
      return this._parseNameCache[fullName] || (
        (this._parseNameCache[fullName] = this._parseName(fullName))
      );
    },
  
    _parseName(fullName) {
      let [
        type,
        fullNameWithoutType
      ] = fullName.split(':');
  
      var name = fullNameWithoutType;
      var namespace = get(this, 'namespace');
      var root = namespace;
      let lastSlashIndex = name.lastIndexOf('/');
      let dirname = lastSlashIndex !== -1 ? name.slice(0, lastSlashIndex) : null;
  
      if (type !== 'template' && lastSlashIndex !== -1) {
        let parts = name.split('/');
        name = parts[parts.length - 1];
        let namespaceName = StringUtils.capitalize(parts.slice(0, -1).join('.'));
        root = Namespace.byName(namespaceName);
  
        assert(
          'You are looking for a ' + name + ' ' + type + ' in the ' +
          namespaceName + ' namespace, but the namespace could not be found',
          root
        );
      }
  
      let resolveMethodName = fullNameWithoutType === 'main' ? 'Main' : StringUtils.classify(type);
  
      if (!(name && type)) {
        throw new TypeError('Invalid fullName: `' + fullName + '`, must be of the form `type:name` ');
      }
  
      return {
        fullName,
        type,
        fullNameWithoutType,
        dirname,
        name,
        root,
        resolveMethodName: 'resolve' + resolveMethodName
      };
    },
  
    /**
      Returns a human-readable description for a fullName. Used by the
      Application namespace in assertions to describe the
      precise name of the class that Ember is looking for, rather than
      container keys.
  
      @param {String} fullName the lookup string
      @method lookupDescription
      @protected
    */
    lookupDescription(fullName) {
      let parsedName = this.parseName(fullName);
      let description;
  
      if (parsedName.type === 'template') {
        return 'template at ' + parsedName.fullNameWithoutType.replace(/\./g, '/');
      }
  
      description = parsedName.root + '.' + StringUtils.classify(parsedName.name).replace(/\./g, '');
  
      if (parsedName.type !== 'model') {
        description += StringUtils.classify(parsedName.type);
      }
  
      return description;
    },
  
    makeToString(factory, fullName) {
      return factory.toString();
    },
  
    /**
      Given a parseName object (output from `parseName`), apply
      the conventions expected by `Ember.Router`
  
      @param {Object} parsedName a parseName object with the parsed
        fullName lookup string
      @method useRouterNaming
      @protected
    */
    useRouterNaming(parsedName) {
      parsedName.name = parsedName.name.replace(/\./g, '_');
      if (parsedName.name === 'basic') {
        parsedName.name = '';
      }
    },
    /**
      Look up the template in Ember.TEMPLATES
  
      @param {Object} parsedName a parseName object with the parsed
        fullName lookup string
      @method resolveTemplate
      @protected
    */
    resolveTemplate(parsedName) {
      let templateName = parsedName.fullNameWithoutType.replace(/\./g, '/');
  
      return getTemplate(templateName) || getTemplate(StringUtils.decamelize(templateName));
    },
  
    /**
      Lookup the view using `resolveOther`
  
      @param {Object} parsedName a parseName object with the parsed
        fullName lookup string
      @method resolveView
      @protected
    */
    resolveView(parsedName) {
      this.useRouterNaming(parsedName);
      return this.resolveOther(parsedName);
    },
  
    /**
      Lookup the controller using `resolveOther`
  
      @param {Object} parsedName a parseName object with the parsed
        fullName lookup string
      @method resolveController
      @protected
    */
    resolveController(parsedName) {
      this.useRouterNaming(parsedName);
      return this.resolveOther(parsedName);
    },
    /**
      Lookup the route using `resolveOther`
  
      @param {Object} parsedName a parseName object with the parsed
        fullName lookup string
      @method resolveRoute
      @protected
    */
    resolveRoute(parsedName) {
      this.useRouterNaming(parsedName);
      return this.resolveOther(parsedName);
    },
  
    /**
      Lookup the model on the Application namespace
  
      @param {Object} parsedName a parseName object with the parsed
        fullName lookup string
      @method resolveModel
      @protected
    */
    resolveModel(parsedName) {
      let className = StringUtils.classify(parsedName.name);
      let factory = get(parsedName.root, className);
  
      return factory;
    },
    /**
      Look up the specified object (from parsedName) on the appropriate
      namespace (usually on the Application)
  
      @param {Object} parsedName a parseName object with the parsed
        fullName lookup string
      @method resolveHelper
      @protected
    */
    resolveHelper(parsedName) {
      return this.resolveOther(parsedName);
    },
    /**
      Look up the specified object (from parsedName) on the appropriate
      namespace (usually on the Application)
  
      @param {Object} parsedName a parseName object with the parsed
        fullName lookup string
      @method resolveOther
      @protected
    */
    resolveOther(parsedName) {
      let className = StringUtils.classify(parsedName.name) + StringUtils.classify(parsedName.type);
      let factory = get(parsedName.root, className);
      return factory;
    },
  
    resolveMain(parsedName) {
      let className = StringUtils.classify(parsedName.type);
      return get(parsedName.root, className);
    },
  
    /**
     @method _logLookup
     @param {Boolean} found
     @param {Object} parsedName
     @private
    */
    _logLookup(found, parsedName) {
      let symbol, padding;
  
      if (found) {
        symbol = '[✓]';
      } else {
        symbol = '[ ]';
      }
  
      if (parsedName.fullName.length > 60) {
        padding = '.';
      } else {
        padding = new Array(60 - parsedName.fullName.length).join('.');
      }
  
      info(symbol, parsedName.fullName, padding, this.lookupDescription(parsedName.fullName));
    },
  
    /**
     Used to iterate all items of a given type.
  
     @method knownForType
     @param {String} type the type to search for
     @private
     */
    knownForType(type) {
      let namespace = get(this, 'namespace');
      let suffix = StringUtils.classify(type);
      let typeRegexp = new RegExp(`${suffix}$`);
  
      let known = dictionary(null);
      let knownKeys = Object.keys(namespace);
      for (let index = 0; index < knownKeys.length; index++) {
        let name = knownKeys[index];
  
        if (typeRegexp.test(name)) {
          let containerName = this.translateToContainerFullname(type, name);
  
          known[containerName] = true;
        }
      }
  
      return known;
    },
  
    /**
     Converts provided name from the backing namespace into a container lookup name.
  
     Examples:
  
     App.FooBarHelper -> helper:foo-bar
     App.THelper -> helper:t
  
     @method translateToContainerFullname
     @param {String} type
     @param {String} name
     @private
     */
  
    translateToContainerFullname(type, name) {
      let suffix = StringUtils.classify(type);
      let namePrefix = name.slice(0, suffix.length * -1);
      let dasherizedName = StringUtils.dasherize(namePrefix);
  
      return `${type}:${dasherizedName}`;
    }
  });
  