text: # -*- coding: utf-8 -*-
  """
      sphinx.domains.javascript
      ~~~~~~~~~~~~~~~~~~~~~~~~~
  
      The JavaScript domain.
  
      :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
      :license: BSD, see LICENSE for details.
  """
  
  from sphinx import addnodes
  from sphinx.domains import Domain, ObjType
  from sphinx.locale import l_, _
  from sphinx.directives import ObjectDescription
  from sphinx.roles import XRefRole
  from sphinx.domains.python import _pseudo_parse_arglist
  from sphinx.util.nodes import make_refnode
  from sphinx.util.docfields import Field, GroupedField, TypedField
  
  
  class JSObject(ObjectDescription):
      """
      Description of a JavaScript object.
      """
      #: If set to ``True`` this object is callable and a `desc_parameterlist` is
      #: added
      has_arguments = False
  
      #: what is displayed right before the documentation entry
      display_prefix = None
  
      def handle_signature(self, sig, signode):
          sig = sig.strip()
          if '(' in sig and sig[-1:] == ')':
              prefix, arglist = sig.split('(', 1)
              arglist = arglist[:-1].strip()
          else:
              prefix = sig
              arglist = None
          if '.' in prefix:
              nameprefix, name = prefix.rsplit('.', 1)
          else:
              nameprefix = None
              name = prefix
  
          objectname = self.env.temp_data.get('js:object')
          if nameprefix:
              if objectname:
                  # someone documenting the method of an attribute of the current
                  # object? shouldn't happen but who knows...
                  nameprefix = objectname + '.' + nameprefix
              fullname = nameprefix + '.' + name
          elif objectname:
              fullname = objectname + '.' + name
          else:
              # just a function or constructor
              objectname = ''
              fullname = name
  
          signode['object'] = objectname
          signode['fullname'] = fullname
  
          if self.display_prefix:
              signode += addnodes.desc_annotation(self.display_prefix,
                                                  self.display_prefix)
          if nameprefix:
              signode += addnodes.desc_addname(nameprefix + '.', nameprefix + '.')
          signode += addnodes.desc_name(name, name)
          if self.has_arguments:
              if not arglist:
                  signode += addnodes.desc_parameterlist()
              else:
                  _pseudo_parse_arglist(signode, arglist)
          return fullname, nameprefix
  
      def add_target_and_index(self, name_obj, sig, signode):
          objectname = self.options.get(
              'object', self.env.temp_data.get('js:object'))
          fullname = name_obj[0]
          if fullname not in self.state.document.ids:
              signode['names'].append(fullname)
              signode['ids'].append(fullname.replace('$', '_S_'))
              signode['first'] = not self.names
              self.state.document.note_explicit_target(signode)
              objects = self.env.domaindata['js']['objects']
              if fullname in objects:
                  self.env.warn(
                      self.env.docname,
                      'duplicate object description of %s, ' % fullname +
                      'other instance in ' +
                      self.env.doc2path(objects[fullname][0]),
                      self.lineno)
              objects[fullname] = self.env.docname, self.objtype
  
          indextext = self.get_index_text(objectname, name_obj)
          if indextext:
              self.indexnode['entries'].append(('single', indextext,
                                                fullname, ''))
  
      def get_index_text(self, objectname, name_obj):
          name, obj = name_obj
          if self.objtype == 'function':
              if not obj:
                  return _('%s() (built-in function)') % name
              return _('%s() (%s method)') % (name, obj)
          elif self.objtype == 'class':
              return _('%s() (class)') % name
          elif self.objtype == 'data':
              return _('%s (global variable or constant)') % name
          elif self.objtype == 'attribute':
              return _('%s (%s attribute)') % (name, obj)
          return ''
  
  
  class JSCallable(JSObject):
      """Description of a JavaScript function, method or constructor."""
      has_arguments = True
  
      doc_field_types = [
          TypedField('arguments', label=l_('Arguments'),
                     names=('argument', 'arg', 'parameter', 'param'),
                     typerolename='func', typenames=('paramtype', 'type')),
          GroupedField('errors', label=l_('Throws'), rolename='err',
                       names=('throws', ),
                       can_collapse=True),
          Field('returnvalue', label=l_('Returns'), has_arg=False,
                names=('returns', 'return')),
      ]
  
  
  class JSConstructor(JSCallable):
      """Like a callable but with a different prefix."""
      display_prefix = 'class '
  
  
  class JSXRefRole(XRefRole):
      def process_link(self, env, refnode, has_explicit_title, title, target):
          # basically what sphinx.domains.python.PyXRefRole does
          refnode['js:object'] = env.temp_data.get('js:object')
          if not has_explicit_title:
              title = title.lstrip('.')
              target = target.lstrip('~')
              if title[0:1] == '~':
                  title = title[1:]
                  dot = title.rfind('.')
                  if dot != -1:
                      title = title[dot+1:]
          if target[0:1] == '.':
              target = target[1:]
              refnode['refspecific'] = True
          return title, target
  
  
  class JavaScriptDomain(Domain):
      """JavaScript language domain."""
      name = 'js'
      label = 'JavaScript'
      # if you add a new object type make sure to edit JSObject.get_index_string
      object_types = {
          'function':  ObjType(l_('function'),  'func'),
          'class':     ObjType(l_('class'),     'class'),
          'data':      ObjType(l_('data'),      'data'),
          'attribute': ObjType(l_('attribute'), 'attr'),
      }
      directives = {
          'function':  JSCallable,
          'class':     JSConstructor,
          'data':      JSObject,
          'attribute': JSObject,
      }
      roles = {
          'func':  JSXRefRole(fix_parens=True),
          'class': JSXRefRole(fix_parens=True),
          'data':  JSXRefRole(),
          'attr':  JSXRefRole(),
      }
      initial_data = {
          'objects': {}, # fullname -> docname, objtype
      }
  
      def clear_doc(self, docname):
          for fullname, (fn, _) in self.data['objects'].items():
              if fn == docname:
                  del self.data['objects'][fullname]
  
      def find_obj(self, env, obj, name, typ, searchorder=0):
          if name[-2:] == '()':
              name = name[:-2]
          objects = self.data['objects']
          newname = None
          if searchorder == 1:
              if obj and obj + '.' + name in objects:
                  newname = obj + '.' + name
              else:
                  newname = name
          else:
              if name in objects:
                  newname = name
              elif obj and obj + '.' + name in objects:
                  newname = obj + '.' + name
          return newname, objects.get(newname)
  
      def resolve_xref(self, env, fromdocname, builder, typ, target, node,
                       contnode):
          objectname = node.get('js:object')
          searchorder = node.hasattr('refspecific') and 1 or 0
          name, obj = self.find_obj(env, objectname, target, typ, searchorder)
          if not obj:
              return None
          return make_refnode(builder, fromdocname, obj[0], name, contnode, name)
  
      def get_objects(self):
          for refname, (docname, type) in self.data['objects'].iteritems():
              yield refname, refname, type, docname, refname, 1
  