class Hatemile::Implementation::AccessibleNavigationImplementation

The AccessibleNavigationImplementation class is official implementation of AccessibleNavigation interface.

Constants

The HTML class of force link, after it.

The HTML class of force link, before it.

CLASS_HEADING_ANCHOR

The HTML class of anchor of heading link.

CLASS_SKIPPER_ANCHOR

The HTML class of anchor of skipper.

CLASS_TEXT_HEADING

The HTML class of text of description of container of heading links.

DATA_ANCHOR_FOR

The name of attribute that links the anchor of skipper with the element.

DATA_ATTRIBUTE_LONG_DESCRIPTION_OF

The name of attribute that link the anchor of long description with the image.

DATA_HEADING_ANCHOR_FOR

The name of attribute that links the anchor of heading link with heading.

DATA_HEADING_LEVEL

The name of attribute that indicates the level of heading of link.

ID_CONTAINER_HEADING_AFTER

The id of list element that contains the links for the headings, after the whole content of page.

ID_CONTAINER_HEADING_BEFORE

The id of list element that contains the links for the headings, before the whole content of page.

ID_CONTAINER_SKIPPERS

The id of list element that contains the skippers.

Public Class Methods

new(parser, configure, skipper_file_name = nil) click to toggle source

Initializes a new object that manipulate the accessibility of the navigation of parser.

@param parser [Hatemile::Util::Html::HTMLDOMParser] The HTML parser. @param configure [Hatemile::Util::Configure] The configuration of

HaTeMiLe.

@param skipper_file_name [String] The file path of skippers

configuration.
# File lib/hatemile/implementation/accessible_navigation_implementation.rb, line 330
def initialize(parser, configure, skipper_file_name = nil)
  Hatemile::Helper.require_not_nil(parser, configure)
  Hatemile::Helper.require_valid_type(
    parser,
    Hatemile::Util::Html::HTMLDOMParser
  )
  Hatemile::Helper.require_valid_type(
    configure,
    Hatemile::Util::Configure
  )
  Hatemile::Helper.require_valid_type(skipper_file_name, String)

  @parser = parser
  @id_generator = Hatemile::Util::IDGenerator.new('navigation')
  @elements_heading_before = configure.get_parameter(
    'elements-heading-before'
  )
  @elements_heading_after = configure.get_parameter(
    'elements-heading-after'
  )
  @attribute_long_description_prefix_before = configure.get_parameter(
    'attribute-longdescription-prefix-before'
  )
  @attribute_long_description_suffix_before = configure.get_parameter(
    'attribute-longdescription-suffix-before'
  )
  @attribute_long_description_prefix_after = configure.get_parameter(
    'attribute-longdescription-prefix-after'
  )
  @attribute_long_description_suffix_after = configure.get_parameter(
    'attribute-longdescription-suffix-after'
  )
  @skippers = get_skippers(configure, skipper_file_name)
  @list_skippers_added = false
  @list_heading_added = false
  @validate_heading = false
  @valid_heading = false
  @list_skippers = nil
  @list_heading_before = nil
  @list_heading_after = nil
end

Public Instance Methods

provide_navigation_by_all_headings() click to toggle source

@see Hatemile::AccessibleNavigation#provide_navigation_by_all_headings

# File lib/hatemile/implementation/accessible_navigation_implementation.rb, line 495
def provide_navigation_by_all_headings
  headings = @parser.find('h1,h2,h3,h4,h5,h6').list_results
  headings.each do |heading|
    if Hatemile::Util::CommonFunctions.is_valid_element?(heading)
      provide_navigation_by_heading(heading)
    end
  end
end
provide_navigation_by_all_skippers() click to toggle source

@see Hatemile::AccessibleNavigation#provide_navigation_by_all_skippers

# File lib/hatemile/implementation/accessible_navigation_implementation.rb, line 414
def provide_navigation_by_all_skippers
  @skippers.each do |skipper|
    elements = @parser.find(skipper[:selector]).list_results
    elements.each do |element|
      next unless Hatemile::Util::CommonFunctions.is_valid_element?(
        element
      )

      provide_navigation_by_skipper(element)
    end
  end
end
provide_navigation_by_heading(heading) click to toggle source

@see Hatemile::AccessibleNavigation#provide_navigation_by_heading

# File lib/hatemile/implementation/accessible_navigation_implementation.rb, line 429
def provide_navigation_by_heading(heading)
  @valid_heading = valid_heading? unless @validate_heading

  return unless @valid_heading

  anchor = generate_anchor_for(
    heading,
    DATA_HEADING_ANCHOR_FOR,
    CLASS_HEADING_ANCHOR
  )

  return if anchor.nil?

  generate_list_heading unless @list_heading_added
  list_before = nil
  list_after = nil
  level = get_heading_level(heading)
  if level == 1
    list_before = @list_heading_before
    list_after = @list_heading_after
  else
    selector = "[#{DATA_HEADING_LEVEL}=\"#{level - 1}\"]"
    unless @list_heading_before.nil?
      super_item_before = @parser.find(
        @list_heading_before
      ).find_descendants(selector).last_result
    end
    unless super_item_before.nil?
      list_before = @parser.find(
        super_item_before
      ).find_children('ol').first_result
      if list_before.nil?
        list_before = @parser.create_element('ol')
        super_item_before.append_element(list_before)
      end
    end
    unless @list_heading_after.nil?
      super_item_after = @parser.find(
        @list_heading_after
      ).find_descendants(selector).last_result
    end
    unless super_item_after.nil?
      list_after = @parser.find(
        super_item_after
      ).find_children('ol').first_result
      if list_after.nil?
        list_after = @parser.create_element('ol')
        super_item_after.append_element(list_after)
      end
    end
  end

  item = @parser.create_element('li')
  item.set_attribute(DATA_HEADING_LEVEL, level.to_s)

  link = @parser.create_element('a')
  link.set_attribute('href', "##{anchor.get_attribute('name')}")
  link.append_text(heading.get_text_content)
  item.append_element(link)

  list_before.append_element(item.clone_element) unless list_before.nil?
  list_after.append_element(item.clone_element) unless list_after.nil?
end
provide_navigation_by_skipper(element) click to toggle source

@see Hatemile::AccessibleNavigation#provide_navigation_by_skipper

# File lib/hatemile/implementation/accessible_navigation_implementation.rb, line 374
def provide_navigation_by_skipper(element)
  skipper = nil
  @skippers.each do |auxiliar_skipper|
    elements = @parser.find(auxiliar_skipper[:selector]).list_results
    if elements.include?(element)
      skipper = auxiliar_skipper
      break
    end
  end

  return if skipper.nil?

  @list_skippers = generate_list_skippers unless @list_skippers_added

  return if @list_skippers.nil?

  anchor = generate_anchor_for(
    element,
    DATA_ANCHOR_FOR,
    CLASS_SKIPPER_ANCHOR
  )

  return if anchor.nil?

  item_link = @parser.create_element('li')
  link = @parser.create_element('a')
  link.set_attribute('href', "##{anchor.get_attribute('name')}")
  link.append_text(skipper[:description])

  free_shortcut(skipper[:shortcut])
  link.set_attribute('accesskey', skipper[:shortcut])

  @id_generator.generate_id(link)

  item_link.append_element(link)
  @list_skippers.append_element(item_link)
end
provide_navigation_to_all_long_descriptions() click to toggle source

@see Hatemile::AccessibleNavigation#provide_navigation_to_all_long_descriptions

# File lib/hatemile/implementation/accessible_navigation_implementation.rb, line 551
def provide_navigation_to_all_long_descriptions
  images = @parser.find('[alt][longdesc]').list_results
  images.each do |image|
    if Hatemile::Util::CommonFunctions.is_valid_element?(image)
      provide_navigation_to_long_description(image)
    end
  end
end
provide_navigation_to_long_description(image) click to toggle source

@see Hatemile::AccessibleNavigation#provide_navigation_to_long_description

# File lib/hatemile/implementation/accessible_navigation_implementation.rb, line 506
def provide_navigation_to_long_description(image)
  unless image.has_attribute?('longdesc') && image.has_attribute?('alt')
    return
  end

  @id_generator.generate_id(image)
  id = image.get_attribute('id')

  selector = "[#{DATA_ATTRIBUTE_LONG_DESCRIPTION_OF}=\"#{id}\"]"
  return unless @parser.find(selector).first_result.nil?

  alternative_text = image.get_attribute('alt').gsub(
    /[ \n\t\r]+/,
    ' '
  ).strip
  unless @attribute_long_description_prefix_before.empty? &&
         @attribute_long_description_suffix_before.empty?
    before_text = "#{@attribute_long_description_prefix_before}" \
                  "#{alternative_text}" \
                  "#{@attribute_long_description_suffix_before}"
    before_anchor = @parser.create_element('a')
    before_anchor.set_attribute('href', image.get_attribute('longdesc'))
    before_anchor.set_attribute('target', '_blank')
    before_anchor.set_attribute(DATA_ATTRIBUTE_LONG_DESCRIPTION_OF, id)
    before_anchor.set_attribute('class', CLASS_FORCE_LINK_BEFORE)
    before_anchor.append_text(before_text)
    image.insert_after(before_anchor)
  end
  unless @attribute_long_description_prefix_after.empty? &&
         @attribute_long_description_suffix_after.empty?
    after_text = "#{@attribute_long_description_prefix_after}" \
                  "#{alternative_text}" \
                  "#{@attribute_long_description_suffix_after}"
    after_anchor = @parser.create_element('a')
    after_anchor.set_attribute('href', image.get_attribute('longdesc'))
    after_anchor.set_attribute('target', '_blank')
    after_anchor.set_attribute(DATA_ATTRIBUTE_LONG_DESCRIPTION_OF, id)
    after_anchor.set_attribute('class', CLASS_FORCE_LINK_AFTER)
    after_anchor.append_text(after_text)
    image.insert_after(after_anchor)
  end
end

Protected Instance Methods

free_shortcut(shortcut) click to toggle source

Replace the shortcut of elements, that has the shortcut passed.

@param shortcut [String] The shortcut. @return [void]

# File lib/hatemile/implementation/accessible_navigation_implementation.rb, line 257
def free_shortcut(shortcut)
  found = false
  alpha_numbers = '1234567890abcdefghijklmnopqrstuvwxyz'
  elements = @parser.find('[accesskey]').list_results
  elements.each do |element|
    shortcuts = element.get_attribute('accesskey').downcase

    unless Hatemile::Util::CommonFunctions.in_list?(shortcuts, shortcut)
      next
    end

    (0..alpha_numbers.length - 1).each do |i|
      key = alpha_numbers[i, i + 1]
      found = true
      elements.each do |element_with_shortcuts|
        shortcuts = element_with_shortcuts.get_attribute(
          'accesskey'
        ).downcase

        unless Hatemile::Util::CommonFunctions.in_list?(shortcuts, key)
          next
        end

        element.set_attribute('accesskey', key)
        found = false
        break
      end
      break if found
    end
    break if found
  end
end
generate_anchor_for(element, data_attribute, anchor_class) click to toggle source

Generate an anchor for the element.

@param element [Hatemile::Util::Html::HTMLDOMElement] The element. @param data_attribute [String] The name of attribute that links the

element with the anchor.

@param anchor_class [String] The HTML class of anchor. @return [Hatemile::Util::Html::HTMLDOMElement] The anchor.

# File lib/hatemile/implementation/accessible_navigation_implementation.rb, line 230
def generate_anchor_for(element, data_attribute, anchor_class)
  @id_generator.generate_id(element)
  anchor = nil
  if @parser.find(
    "[#{data_attribute}=\"#{element.get_attribute('id')}\"]"
  ).first_result.nil?
    if element.get_tag_name == 'A'
      anchor = element
    else
      anchor = @parser.create_element('a')
      @id_generator.generate_id(anchor)
      anchor.set_attribute('class', anchor_class)
      element.insert_before(anchor)
    end
    unless anchor.has_attribute?('name')
      anchor.set_attribute('name', anchor.get_attribute('id'))
    end
    anchor.set_attribute(data_attribute, element.get_attribute('id'))
  end
  anchor
end
generate_list_heading() click to toggle source

Generate the list of heading links of page.

# File lib/hatemile/implementation/accessible_navigation_implementation.rb, line 128
def generate_list_heading
  local = @parser.find('body').first_result

  return if local.nil?

  container_before = @parser.find(
    "##{ID_CONTAINER_HEADING_BEFORE}"
  ).first_result
  if container_before.nil? && !@elements_heading_before.empty?
    container_before = @parser.create_element('div')
    container_before.set_attribute('id', ID_CONTAINER_HEADING_BEFORE)

    text_container_before = @parser.create_element('span')
    text_container_before.set_attribute('class', CLASS_TEXT_HEADING)
    text_container_before.append_text(@elements_heading_before)

    container_before.append_element(text_container_before)
    local.prepend_element(container_before)
  end
  unless container_before.nil?
    @list_heading_before = @parser.find(
      container_before
    ).find_children('ol').first_result
    if @list_heading_before.nil?
      @list_heading_before = @parser.create_element('ol')
      container_before.append_element(@list_heading_before)
    end
  end

  container_after = @parser.find(
    "##{ID_CONTAINER_HEADING_AFTER}"
  ).first_result
  if container_after.nil? && !@elements_heading_after.empty?
    container_after = @parser.create_element('div')
    container_after.set_attribute('id', ID_CONTAINER_HEADING_AFTER)

    text_container_after = @parser.create_element('span')
    text_container_after.set_attribute('class', CLASS_TEXT_HEADING)
    text_container_after.append_text(@elements_heading_after)

    container_after.append_element(text_container_after)
    local.append_element(container_after)
  end
  unless container_after.nil?
    @list_heading_after = @parser.find(
      container_after
    ).find_children('ol').first_result
    if @list_heading_after.nil?
      @list_heading_after = @parser.create_element('ol')
      container_after.append_element(@list_heading_after)
    end
  end

  @list_heading_added = true
end
generate_list_skippers() click to toggle source

Generate the list of skippers of page.

@return [Hatemile::Util::Html::HTMLDOMElement] The list of skippers of

page.
# File lib/hatemile/implementation/accessible_navigation_implementation.rb, line 103
def generate_list_skippers
  container = @parser.find("##{ID_CONTAINER_SKIPPERS}").first_result
  html_list = nil
  if container.nil?
    local = @parser.find('body').first_result
    unless local.nil?
      container = @parser.create_element('div')
      container.set_attribute('id', ID_CONTAINER_SKIPPERS)
      local.prepend_element(container)
    end
  end
  unless container.nil?
    html_list = @parser.find(container).find_children('ul').first_result
    if html_list.nil?
      html_list = @parser.create_element('ul')
      container.append_element(html_list)
    end
  end
  @list_skippers_added = true

  html_list
end
get_heading_level(element) click to toggle source

Returns the level of heading.

@param element [Hatemile::Util::Html::HTMLDOMElement] The heading. @return [Integer] The level of heading.

# File lib/hatemile/implementation/accessible_navigation_implementation.rb, line 189
def get_heading_level(element)
  tag = element.get_tag_name
  return 1 if tag == 'H1'
  return 2 if tag == 'H2'
  return 3 if tag == 'H3'
  return 4 if tag == 'H4'
  return 5 if tag == 'H5'
  return 6 if tag == 'H6'
  -1
end
get_skippers(configure, file_name) click to toggle source

Returns the skippers of configuration.

@param configure [Hatemile::Util::Configure] The configuration of

HaTeMiLe.

@param file_name [String] The file path of skippers configuration. @return [Array<Hash>] The skippers of configuration.

# File lib/hatemile/implementation/accessible_navigation_implementation.rb, line 298
def get_skippers(configure, file_name)
  skippers = []
  if file_name.nil?
    file_name = File.join(
      File.dirname(File.dirname(File.dirname(__FILE__))),
      'skippers.xml'
    )
  end
  document = REXML::Document.new(File.read(file_name))
  document.elements.each('skippers/skipper') do |skipper_xml|
    skipper = {}
    skipper[:selector] = skipper_xml.attribute('selector').value
    skipper[:description] = configure.get_parameter(
      skipper_xml.attribute('description').value
    )
    skipper[:shortcut] = skipper_xml.attribute('shortcut').value
    skippers.push(skipper)
  end
  skippers
end
valid_heading?() click to toggle source

Check that the headings of page are sintatic correct.

@return [Boolean] True if the headings of page are sintatic correct or

false if not.
# File lib/hatemile/implementation/accessible_navigation_implementation.rb, line 205
def valid_heading?
  elements = @parser.find('h1,h2,h3,h4,h5,h6').list_results
  last_level = 0
  count_main_heading = 0
  @validate_heading = true
  elements.each do |element|
    level = get_heading_level(element)
    if level == 1
      return false if count_main_heading == 1
      count_main_heading = 1
    end
    return false if (level - last_level) > 1
    last_level = level
  end
  true
end