       #
       # TODO - find out why the panel is neither an ElementFinder nor a
       # WebElement and replace the select below with panel.element or
-      # panel.findElement. Add panel to the clinicalProfileTablePanel
-      # function arguments.
-      @select(selector).then (table) ->
+      # panel.findElement. Add panel to the clinicalProfile function
+      # arguments.
+      @find(selector).then (table) ->
         # Add the fields.
         addTextFields(table, bindings) if table
         # Return the table, or null if the table was not found.

Page = require './helpers/page'

class SubjectDetailPage extends Page
  # Finds the profile table WebElements for the given Page.select()
  # selector. The return value is an array of table promises.
  # Each table promise resolves to an object {header, body},
  # where:
  # * header is a promise which resolves to an array of table
  #   heading text value promises
  # * body is a promise which resolves to an array of row promises
  #
  # Each row promise resolves to an array of column text promises.
  #
  # If the function argument is provided, then it is applied to
  # each table cell text value promise.
  #
  # Example:
  # page.findProfileTables 'qi-clinical-table', (tables) ->
  #   expect(tables).to.exist.and.not.be.empty
  #   for table in tables
  #     table.header.then (headings) ->
  #       expect(headings).to.exist.and.not.be.empty
  #       expect(heading[0]).to.equal('Age')
  #     table.body.then (rows) ->
  #       expect(rows).to.exist.and.not.be.empty
  #       for row in rows
  #          row.then (columns) ->
  #            expect(columns).to.exist.and.not.be.empty
  #            expect(columns[0]).to.equal('32')
  #
  # @param selector the Protractor CSS locator argument
  # @param fn the optional function to apply to each table cell
  #   text value
  # @returns the table body promises
  findProfileTables: (selector, fn) ->
    headings = (table) ->
      table.all(By.tagName('th'))

    rows = (table) ->
      body = table.element(By.tagName('tbody'))
      body.all(By.tagName('tr'))

    columns = (row) ->
      row.all(By.tagName('td'))

    text = (col) ->
      col.getText().then (text) ->
        fn(text) if text? and fn?
        text

    mapTables = (tables) ->
      mapTable = (table) ->
        mapHeader = ->
          # Return the [heading values] promise.
          headings(table).map(text)
        
        mapBody = ->
          mapRow = (row) ->
            # Return the [column values] promise.
            columns(row).map(text)

          # Return the [rows] promise.
          rows(table).map(mapRow)
        
        # Return the {header, body}
        header: mapHeader()
        body: mapBody()

      # Return the [{header, body}, ...] promise.
      tables.map(mapTable)

    # Return the [tables] promise.
    element.all(By.css(selector))
      .then(mapTables)

  # @param fn optional function to apply to the resolved button
  #   ElementFinder
  # @returns a promise which resolves to the image profile button
  #   ElementFinder
  imageProfileFormatButton: (fn) ->
    @select('.qi-modeling-profile button').then (button) ->
      fn(button) if button? and fn?
      button

  # Finds the imaging profile table panel WebElement.
  #
  # @param fn optional function to run on the resolved panel
  # @returns the imaging profile table panel promise
  imagingProfileTablePanel: (fn) ->
    @select('qi-modeling-tables').then (panel) ->
      fn(panel) if panel? and fn?
      panel

  # Finds the imaging profile table WebElements.
  # See findProfileTables.
  #
  # @param fn optional function to run on the resolved tables
  # @returns the imaging profile tables promise
  imagingProfileTables: (fn) ->
    click = (button) ->
      button.click()

    tables = =>
      @findProfileTables('.qi-modeling-table', fn)

    # Click the imaging profile format button to show the imaging
    # profile tables.
    @imageProfileFormatButton()
      .then(click)
      .then(tables)

  # @param fn optional function to run on the resolved tables
  modelingChart: (fn) ->
    @select('#qi-modeling-chart').then (chart) ->
      fn(chart) if chart? and fn?
      chart

  # @param fn optional function to run on the resolved chart
  # @returns the date line chart promise
  dateLine: (fn) ->
    @select('//qi-visit-dateline//nvd3-line-chart').then (chart) ->
      fn(chart) if chart? and fn?
      chart

  # Finds the clinical profile table panel WebElement.
  #
  # @param fn optional function to run on the resolved panel
  # @returns the clinical profile table panel promise
  clinicalProfileTablePanel: (fn) ->
    # @param selector the selection criterion
    # @param bindings the table field {property: binding} bindings
    # @returns the target table augmented with the component table
    #   access functions, or null if the table was not found
    selectTableWithBindings = (selector, bindings) =>
      # Gets the given WebElement text.
      text = (elt) -> elt.getText() if elt

      # Searches for a WebElement and resolves to its text.
      selectText = (selector) => @select(selector).then(text)

      # @param table the target table
      # @param bindings the table field bindings

            sbjBinding = 'subject.' + binding
            # Make the field element search selector.
            selector = By.binding(sbjBinding)
            findText(selector)

        addTextField(prop, binding) for prop, binding of bindings

      # Find the table.
      #
      # Note: A better solution is to perform the field selects below
      # against the table rather than the page. However,
      # panel.findElement('qi-demographics-table')
      # results in an error, since the untyped panel object has most, but
      # not all, of the WebElement function properties. In particular, it
