text: /**
      @module jsdoc/src/handlers
   */
  
  
  var currentModule = null;
  
  /**
      Attach these event handlers to a particular instance of a parser.
      @param parser
   */
  exports.attachTo = function(parser) {
      var jsdoc = {doclet: require('jsdoc/doclet'), name: require('jsdoc/name')};
  
      // handles JSDoc comments that include a @name tag -- the code is ignored in such a case
      parser.on('jsdocCommentFound', function(e) {
          var newDoclet = new jsdoc.doclet.Doclet(e.comment, e);
  
          if (!newDoclet.name) {
              return false; // only interested in virtual comments (with a @name) here
          }
  
          addDoclet.call(this, newDoclet);
          if (newDoclet.kind === 'module') {
              currentModule = newDoclet.longname;
          }
          e.doclet = newDoclet;
  
          resolveProperties(newDoclet);
      });
  
      // handles named symbols in the code, may or may not have a JSDoc comment attached
      parser.on('symbolFound', function(e) {
          var subDoclets = e.comment.split(/@also\b/g);
  
          for (var i = 0, l = subDoclets.length; i < l; i++) {
              newSymbolDoclet.call(this, subDoclets[i], e);
          }
      });
  
      function newSymbolDoclet(docletSrc, e) {
          var memberofName = null,
              newDoclet = new jsdoc.doclet.Doclet(docletSrc, e);
  
          // an undocumented symbol right after a virtual comment? rhino mistakenly connected the two
          if (newDoclet.name) { // there was a @name in comment
              // try again, without the comment
              e.comment = '@undocumented';
              newDoclet = new jsdoc.doclet.Doclet(e.comment, e);
          }
  
          if (newDoclet.alias) {
              if (newDoclet.alias === '{@thisClass}') {
                  memberofName = this.resolveThis(e.astnode);
  
                  // "class" refers to the owner of the prototype, not the prototype itself
                  if ( /^(.+?)(\.prototype|#)$/.test(memberofName) ) {
                      memberofName = RegExp.$1;
                  }
                  newDoclet.alias = memberofName;
              }
              newDoclet.addTag('name', newDoclet.alias);
              newDoclet.postProcess();
          }
          else if (e.code && e.code.name) { // we need to get the symbol name from code
              newDoclet.addTag('name', e.code.name);
              if (!newDoclet.memberof && e.astnode) {
                  var basename = null,
                      scope = '';
                  if ( /^((module.)?exports|this)(\.|$)/.test(newDoclet.name) ) {
                      var nameStartsWith = RegExp.$1;
  
                      newDoclet.name = newDoclet.name.replace(/^(exports|this)(\.|$)/, '');
  
                      // like /** @module foo */ exports.bar = 1;
                      if (nameStartsWith === 'exports' && currentModule) {
                          memberofName = currentModule;
                          scope = 'static';
                      }
                      else if (newDoclet.name === 'module.exports' && currentModule) {
                              newDoclet.addTag('name', currentModule);
                              newDoclet.postProcess();
                      }
                      else {
                          // like /** @module foo */ exports = {bar: 1};
                          // or /** blah */ this.foo = 1;
                          memberofName = this.resolveThis(e.astnode);
                          scope = nameStartsWith === 'exports'? 'static' : 'instance';
  
                          // like /** @module foo */ this.bar = 1;
                          if (nameStartsWith === 'this' && currentModule && !memberofName) {
                              memberofName = currentModule;
                              scope = 'static';
                          }
                      }
  
                      if (memberofName) {
                          if (newDoclet.name) {
                              newDoclet.name = memberofName + (scope === 'instance'? '#' : '.') + newDoclet.name;
                          }
                          else { newDoclet.name = memberofName; }
                      }
                  }
                  else {
                      memberofName = this.astnodeToMemberof(e.astnode);
                      if(memberofName instanceof Array) {
                          basename = memberofName[1];
                          memberofName = memberofName[0];
                      }
                  }
  
                  if (memberofName) {
                      newDoclet.addTag( 'memberof', memberofName);
                      if (basename) {
                          newDoclet.name = newDoclet.name.replace(new RegExp('^' + RegExp.escape(basename) + '.'), '');
                      }
                  }
                  else {
                      if (currentModule) {
                          if (!newDoclet.scope){newDoclet.addTag( 'inner');}
                          if (!newDoclet.memberof && newDoclet.scope !== 'global'){newDoclet.addTag( 'memberof', currentModule);}
                      }
                  }
              }
  
              newDoclet.postProcess();
          }
          else {
              return false;
          }
  
          resolveProperties(newDoclet);
  
          if (!newDoclet.memberof) {
              newDoclet.scope = 'global';
          }
  
          addDoclet.call(this, newDoclet);
          e.doclet = newDoclet;
      }
  
      //parser.on('fileBegin', function(e) { });
  
      parser.on('fileComplete', function(e) {
          currentModule = null;
      });
  
      function addDoclet(newDoclet) {
          var e;
          if (newDoclet) {
              e = { doclet: newDoclet };
              this.fire('newDoclet', e);
  
              if (!e.defaultPrevented) {
                  if ( !filter(newDoclet) ) {
                      this.addResult(newDoclet);
                  }
              }
          }
      }
  
      function filter(doclet) {
          // you can't document prototypes
          if ( /#$/.test(doclet.longname) ) {
              return true;
          }
          // you can't document symbols added by the parser with a dummy name
          if (doclet.meta.code && doclet.meta.code.name === '____') {
              return true;
          }
  
          return false;
      }
  
      function resolveProperties(newDoclet) {
          // find name and description from each property tag text
          if (newDoclet.properties) {
              for (var i = 0, len = newDoclet.properties.length; i < len; i++) {
                  var property = newDoclet.properties[i];
  
                  var parts = jsdoc.name.splitName(property.description);
                  property.name = parts.name;
                  property.description = parts.description;
              }
          }
      }
  };
  
  