class Hatemile::Implementation::AccessibleCSSImplementation

The AccessibleCSSImplementation class is official implementation of AccessibleCSS.

Constants

DATA_ISOLATOR_ELEMENT

The name of attribute for identify isolator elements.

DATA_SPEAK

The name of attribute for identify the element created or modified to support speak property.

DATA_SPEAK_AS

The name of attribute for identify the element created or modified to support speak-as property.

VALID_INHERIT_TAGS

The valid element tags for inherit the speak and speak-as properties.

VALID_TAGS

The valid element tags for speak and speak-as properties.

Public Class Methods

new(html_parser, css_parser, configure, symbol_file_name = nil) click to toggle source

Initializes a new object that improve the accessibility of associations of parser.

@param html_parser [Hatemile::Util::Html::HTMLDOMParser] The HTML

parser.

@param css_parser [Hatemile::Util::Css::StyleSheetParser] The CSS

parser.

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

HaTeMiLe.

@param symbol_file_name [String] The file path of symbol configuration.

# File lib/hatemile/implementation/accessible_css_implementation.rb, line 710
def initialize(html_parser, css_parser, configure, symbol_file_name = nil)
  Hatemile::Helper.require_not_nil(html_parser, css_parser, configure)
  Hatemile::Helper.require_valid_type(
    html_parser,
    Hatemile::Util::Html::HTMLDOMParser
  )
  Hatemile::Helper.require_valid_type(
    css_parser,
    Hatemile::Util::Css::StyleSheetParser
  )
  Hatemile::Helper.require_valid_type(
    configure,
    Hatemile::Util::Configure
  )
  Hatemile::Helper.require_valid_type(symbol_file_name, String)

  @html_parser = html_parser
  @css_parser = css_parser
  @configure = configure
  @id_generator = Hatemile::Util::IDGenerator.new('css')
  set_symbols(symbol_file_name)
end

Public Instance Methods

provide_all_speak_properties() click to toggle source

@see Hatemile::AccessibleCSS#provide_all_speak_properties

# File lib/hatemile/implementation/accessible_css_implementation.rb, line 749
def provide_all_speak_properties
  selector = nil
  rules = @css_parser.get_rules(
    %w[speak speak-punctuation speak-numeral speak-header speak-as]
  )
  rules.each do |rule|
    selector = if selector.nil?
                 rule.get_selector
               else
                 "#{selector},#{rule.get_selector}"
               end
  end

  return if selector.nil?

  @html_parser.find(selector).list_results.each do |element|
    if Hatemile::Util::CommonFunctions.is_valid_element?(element)
      provide_speak_properties(element)
    end
  end
end
provide_speak_properties(element) click to toggle source

@see Hatemile::AccessibleCSS#provide_speak_properties

# File lib/hatemile/implementation/accessible_css_implementation.rb, line 735
def provide_speak_properties(element)
  rules = @css_parser.get_rules(
    %w[speak speak-punctuation speak-numeral speak-header speak-as]
  )
  rules.each do |rule|
    speak_elements = @html_parser.find(rule.get_selector).list_results
    if speak_elements.include?(element)
      provide_speak_properties_with_rule(element, rule)
    end
  end
end

Protected Instance Methods

create_aural_content_element(content, data_property_value) click to toggle source

Create a element to show the content, only to aural displays.

@param content [String] The text content of element. @param data_property_value [String] The value of custom attribute used

to identify the fix.

@return [Hatemile::Util::Html::HTMLDOMElement] The element to show the

content.
# File lib/hatemile/implementation/accessible_css_implementation.rb, line 349
def create_aural_content_element(content, data_property_value)
  content_element = create_content_element(content, data_property_value)
  content_element.set_attribute('unselectable', 'on')
  content_element.set_attribute('class', 'screen-reader-only')
  content_element
end
create_content_element(content, data_property_value) click to toggle source

Create a element to show the content.

@param content [String] The text content of element. @param data_property_value [String] The value of custom attribute used

to identify the fix.

@return [Hatemile::Util::Html::HTMLDOMElement] The element to show the

content.
# File lib/hatemile/implementation/accessible_css_implementation.rb, line 333
def create_content_element(content, data_property_value)
  content_element = @html_parser.create_element('span')
  content_element.set_attribute(DATA_ISOLATOR_ELEMENT, 'true')
  content_element.set_attribute(DATA_SPEAK_AS, data_property_value)
  content_element.append_text(content)
  content_element
end
create_visual_content_element(content, data_property_value) click to toggle source

Create a element to show the content, only to visual displays.

@param content [String] The text content of element. @param data_property_value [String] The value of custom attribute used

to identify the fix.

@return [Hatemile::Util::Html::HTMLDOMElement] The element to show the

content.
# File lib/hatemile/implementation/accessible_css_implementation.rb, line 364
def create_visual_content_element(content, data_property_value)
  content_element = create_content_element(content, data_property_value)
  content_element.set_attribute('aria-hidden', 'true')
  content_element.set_attribute('role', 'presentation')
  content_element
end
get_description_of_symbol(symbol) click to toggle source

Returns the description of symbol.

@param symbol [String] The symbol. @return [String] The description of symbol.

# File lib/hatemile/implementation/accessible_css_implementation.rb, line 222
def get_description_of_symbol(symbol)
  @symbols.each do |dict_symbol|
    return dict_symbol[:description] if dict_symbol[:symbol] == symbol
  end
  nil
end
get_regular_expression_of_symbols() click to toggle source

Returns the regular expression to search all symbols.

@return [String] The regular expression to search all symbols.

# File lib/hatemile/implementation/accessible_css_implementation.rb, line 233
def get_regular_expression_of_symbols
  regular_expression = nil
  @symbols.each do |symbol|
    formated_symbol = Regexp.escape(symbol[:symbol])
    regular_expression = if regular_expression.nil?
                           "(#{formated_symbol})"
                         else
                           "#{regular_expression}|(#{formated_symbol})"
                         end
  end
  Regexp.new(regular_expression)
end
is_valid_element?(element) click to toggle source

Check that the element can be manipulated to apply the CSS properties.

@param element [Hatemile::Util::Html::HTMLDOMElement] The element. @return [Boolean] True if the element can be manipulated to apply the

CSS properties or False if the element cannot be manipulated to apply
the CSS properties.
# File lib/hatemile/implementation/accessible_css_implementation.rb, line 266
def is_valid_element?(element)
  VALID_TAGS.include?(element.get_tag_name)
end
is_valid_inherit_element?(element) click to toggle source

Check that the children of element can be manipulated to apply the CSS properties.

@param element [Hatemile::Util::Html::HTMLDOMElement] The element. @return [Boolean] True if the children of element can be manipulated to

apply the CSS properties or false if the children of element cannot be
manipulated to apply the CSS properties.
# File lib/hatemile/implementation/accessible_css_implementation.rb, line 254
def is_valid_inherit_element?(element)
  VALID_INHERIT_TAGS.include?(element.get_tag_name) &&
    Hatemile::Util::CommonFunctions.is_valid_element?(element)
end
isolate_text_node(element) click to toggle source

Isolate text nodes of element nodes.

@param element [Hatemile::Util::Html::HTMLDOMElement] The element.

# File lib/hatemile/implementation/accessible_css_implementation.rb, line 274
def isolate_text_node(element)
  unless element.has_children_elements? && is_valid_element?(element)
    return
  end

  element.get_children.each do |child_node|
    next unless child_node.is_a?(Hatemile::Util::Html::HTMLDOMTextNode)

    span = @html_parser.create_element('span')
    span.set_attribute(DATA_ISOLATOR_ELEMENT, 'true')
    span.append_text(child_node.get_text_content)

    child_node.replace_node(span)
  end
  element.get_children_elements.each do |child|
    isolate_text_node(child)
  end
end
operation_speak_as_digits(content, index, children) click to toggle source

The operation method of _speak_as method for digits.

@param content [String] The text content of element. @param index [Integer] The index of pattern in text content of element. @param children [Array<Hatemile::Util::Html::HTMLDOMElement>] The

children of element.

@return [Array<Hatemile::Util::Html::HTMLDOMElement>] The new children

of element.
# File lib/hatemile/implementation/accessible_css_implementation.rb, line 175
def operation_speak_as_digits(content, index, children)
  data_property_value = 'digits'
  unless index.zero?
    children.push(
      create_content_element(content[0..(index - 1)], data_property_value)
    )
  end
  children.push(create_aural_content_element(' ', data_property_value))

  children.push(
    create_content_element(
      content[index..index],
      data_property_value
    )
  )

  children
end
operation_speak_as_literal_punctuation(content, index, children) click to toggle source

The operation method of _speak_as method for literal-punctuation.

@param content [String] The text content of element. @param index [Integer] The index of pattern in text content of element. @param children [Array<Hatemile::Util::Html::HTMLDOMElement>] The

children of element.

@return [Array<Hatemile::Util::Html::HTMLDOMElement>] The new children

of element.
# File lib/hatemile/implementation/accessible_css_implementation.rb, line 117
def operation_speak_as_literal_punctuation(content, index, children)
  data_property_value = 'literal-punctuation'
  unless index.zero?
    children.push(
      create_content_element(content[0..(index - 1)], data_property_value)
    )
  end
  children.push(
    create_aural_content_element(
      " #{get_description_of_symbol(content[index..index])} ",
      data_property_value
    )
  )

  children.push(
    create_visual_content_element(
      content[index..index],
      data_property_value
    )
  )

  children
end
operation_speak_as_no_punctuation(content, index, children) click to toggle source

The operation method of _speak_as method for no-punctuation.

@param content [String] The text content of element. @param index [Integer] The index of pattern in text content of element. @param children [Array<Hatemile::Util::Html::HTMLDOMElement>] The

children of element.

@return [Array<Hatemile::Util::Html::HTMLDOMElement>] The new children

of element.
# File lib/hatemile/implementation/accessible_css_implementation.rb, line 150
def operation_speak_as_no_punctuation(content, index, children)
  unless index.zero?
    children.push(
      create_content_element(content[0..(index - 1)], 'no-punctuation')
    )
  end
  children.push(
    create_visual_content_element(
      content[index..index],
      'no-punctuation'
    )
  )

  children
end
operation_speak_as_spell_out(content, index, children) click to toggle source

The operation method of _speak_as method for spell-out.

@param content [String] The text content of element. @param index [Integer] The index of pattern in text content of element. @param children [Array<Hatemile::Util::Html::HTMLDOMElement>] The

children of element.

@return [Array<Hatemile::Util::Html::HTMLDOMElement>] The new children

of element.
# File lib/hatemile/implementation/accessible_css_implementation.rb, line 98
def operation_speak_as_spell_out(content, index, children)
  children.push(
    create_content_element(content[0..index], 'spell-out')
  )

  children.push(create_aural_content_element(' ', 'spell-out'))

  children
end
provide_speak_properties_with_rule(element, rule) click to toggle source

Provide the CSS features of speaking and speech properties in element.

@param element [Hatemile::Util::Html::HTMLDOMElement] The element. @param rule [Hatemile::Util::Css::StyleSheetRule] The stylesheet rule.

# File lib/hatemile/implementation/accessible_css_implementation.rb, line 629
def provide_speak_properties_with_rule(element, rule)
  if rule.has_property?('speak')
    declarations = rule.get_declarations('speak')
    declarations.each do |declaration|
      property_value = declaration.get_value
      if property_value == 'none'
        speak_none_inherit(element)
      elsif property_value == 'normal'
        speak_normal_inherit(element)
      elsif property_value == 'spell-out'
        speak_as_spell_out_inherit(element)
      end
    end
  end
  if rule.has_property?('speak-as')
    declarations = rule.get_declarations('speak-as')
    declarations.each do |declaration|
      speak_as_values = declaration.get_values
      speak_as_normal(element)
      speak_as_values.each do |speak_as_value|
        if speak_as_value == 'spell-out'
          speak_as_spell_out_inherit(element)
        elsif speak_as_value == 'literal-punctuation'
          speak_as_literal_punctuation_inherit(element)
        elsif speak_as_value == 'no-punctuation'
          speak_as_no_punctuation_inherit(element)
        elsif speak_as_value == 'digits'
          speak_as_digits_inherit(element)
        end
      end
    end
  end
  if rule.has_property?('speak-punctuation')
    declarations = rule.get_declarations('speak-punctuation')
    declarations.each do |declaration|
      property_value = declaration.get_value
      if property_value == 'code'
        speak_as_literal_punctuation_inherit(element)
      elsif property_value == 'none'
        speak_as_no_punctuation_inherit(element)
      end
    end
  end
  if rule.has_property?('speak-numeral')
    declarations = rule.get_declarations('speak-numeral')
    declarations.each do |declaration|
      property_value = declaration.get_value
      if property_value == 'digits'
        speak_as_digits_inherit(element)
      elsif property_value == 'continuous'
        speak_as_continuous_inherit(element)
      end
    end
  end

  return unless rule.has_property?('speak-header')

  declarations = rule.get_declarations('speak-header')
  declarations.each do |declaration|
    property_value = declaration.get_value
    if property_value == 'always'
      speak_header_always_inherit(element)
    elsif property_value == 'once'
      speak_header_once_inherit(element)
    end
  end
end
replace_element_by_own_content(element) click to toggle source

Replace the element by own text content.

@param element [Hatemile::Util::Html::HTMLDOMElement] The element.

# File lib/hatemile/implementation/accessible_css_implementation.rb, line 297
def replace_element_by_own_content(element)
  if element.has_children_elements?
    element.get_children_elements.each do |child|
      element.insert_before(child)
    end
    element.remove_node
  elsif element.has_children?
    element.replace_node(element.get_first_node_child)
  end
end
reverse_speak_as(element, data_property_value) click to toggle source

Revert changes of a speak_as method for element and descendants.

@param element [Hatemile::Util::Html::HTMLDOMElement] The element. @param data_property_value [String] The value of custom attribute used

to identify the fix.
# File lib/hatemile/implementation/accessible_css_implementation.rb, line 457
def reverse_speak_as(element, data_property_value)
  data_property = "[#{DATA_SPEAK_AS}=\"#{data_property_value}\"]"

  auxiliar_elements = @html_parser.find(element).find_descendants(
    "#{data_property}[unselectable=\"on\"]"
  ).list_results
  auxiliar_elements.each(&:remove_node)

  content_elements = @html_parser.find(element).find_descendants(
    "#{data_property}[#{DATA_ISOLATOR_ELEMENT}=\"true\"],
    #{data_property}[aria-hidden]"
  ).list_results
  content_elements.each do |content_element|
    replace_element_by_own_content(content_element)
  end
  element.normalize
end
set_symbols(file_name) click to toggle source

Load the symbols with configuration.

@param file_name [String] The file path of symbol configuration.

# File lib/hatemile/implementation/accessible_css_implementation.rb, line 198
def set_symbols(file_name)
  @symbols = []
  if file_name.nil?
    file_name = File.join(
      File.dirname(File.dirname(File.dirname(__FILE__))),
      'symbols.xml'
    )
  end
  document = REXML::Document.new(File.read(file_name))
  document.elements.each('symbols/symbol') do |symbol_xml|
    symbol = {}
    symbol[:symbol] = symbol_xml.attribute('symbol').value
    symbol[:description] = @configure.get_parameter(
      symbol_xml.attribute('description').value
    )
    @symbols.push(symbol)
  end
end
speak_as(element, regular_expression, data_property_value, operation) click to toggle source

Execute a operation by regular expression for element only.

@param element [Hatemile::Util::Html::HTMLDOMElement] The element. @param regular_expression [Regexp] The regular expression. @param data_property_value [String] The value of custom attribute used

to identify the fix.

@param operation [Method] The operation to be executed.

# File lib/hatemile/implementation/accessible_css_implementation.rb, line 426
def speak_as(element, regular_expression, data_property_value, operation)
  children = []
  content = element.get_text_content
  until content.empty?
    index = content.index(regular_expression)

    break if index.nil?

    children = operation.call(content, index, children)

    new_index = index + 1
    content = content[new_index..-1]
  end

  return if children.empty?

  unless content.empty?
    children.push(create_content_element(content, data_property_value))
  end
  element.get_first_node_child.remove_node while element.has_children?
  children.each do |child|
    element.append_element(child)
  end
end
speak_as_continuous_inherit(element) click to toggle source

Speaks the numbers for element and descendants as a word number.

@param element [Hatemile::Util::Html::HTMLDOMElement] The element.

# File lib/hatemile/implementation/accessible_css_implementation.rb, line 589
def speak_as_continuous_inherit(element)
  reverse_speak_as(element, 'digits')
end
speak_as_digits(element) click to toggle source

Speak the digit at a time for each number for element only.

@param element [Hatemile::Util::Html::HTMLDOMElement] The element.

# File lib/hatemile/implementation/accessible_css_implementation.rb, line 569
def speak_as_digits(element)
  speak_as(element, /[0-9]/, 'digits', method(:operation_speak_as_digits))
end
speak_as_digits_inherit(element) click to toggle source

Speak the digit at a time for each number for element and descendants.

@param element [Hatemile::Util::Html::HTMLDOMElement] The element.

# File lib/hatemile/implementation/accessible_css_implementation.rb, line 577
def speak_as_digits_inherit(element)
  reverse_speak_as(element, 'digits')

  isolate_text_node(element)

  visit(element, method(:speak_as_digits))
end
speak_as_literal_punctuation(element) click to toggle source

Speak the punctuation for elements only.

@param element [Hatemile::Util::Html::HTMLDOMElement] The element.

# File lib/hatemile/implementation/accessible_css_implementation.rb, line 516
def speak_as_literal_punctuation(element)
  speak_as(
    element,
    get_regular_expression_of_symbols,
    'literal-punctuation',
    method(:operation_speak_as_literal_punctuation)
  )
end
speak_as_literal_punctuation_inherit(element) click to toggle source

Speak the punctuation for elements and descendants.

@param element [Hatemile::Util::Html::HTMLDOMElement] The element.

# File lib/hatemile/implementation/accessible_css_implementation.rb, line 529
def speak_as_literal_punctuation_inherit(element)
  reverse_speak_as(element, 'literal-punctuation')
  reverse_speak_as(element, 'no-punctuation')

  isolate_text_node(element)

  visit(element, method(:speak_as_literal_punctuation))
end
speak_as_no_punctuation(element) click to toggle source

No speak the punctuation for element only.

@param element [Hatemile::Util::Html::HTMLDOMElement] The element.

# File lib/hatemile/implementation/accessible_css_implementation.rb, line 542
def speak_as_no_punctuation(element)
  escape_punctuation = Regexp.escape('!"#$%&\'()*+,-./:;<=>?@[]^_`{|}~\\')
  speak_as(
    element,
    Regexp.new("[#{escape_punctuation}]"),
    'no-punctuation',
    method(:operation_speak_as_no_punctuation)
  )
end
speak_as_no_punctuation_inherit(element) click to toggle source

No speak the punctuation for element and descendants.

@param element [Hatemile::Util::Html::HTMLDOMElement] The element.

# File lib/hatemile/implementation/accessible_css_implementation.rb, line 556
def speak_as_no_punctuation_inherit(element)
  reverse_speak_as(element, 'literal-punctuation')
  reverse_speak_as(element, 'no-punctuation')

  isolate_text_node(element)

  visit(element, method(:speak_as_no_punctuation))
end
speak_as_normal(element) click to toggle source

Use the default speak configuration of user agent for element and descendants.

@param element [Hatemile::Util::Html::HTMLDOMElement] The element.

# File lib/hatemile/implementation/accessible_css_implementation.rb, line 480
def speak_as_normal(element)
  reverse_speak_as(element, 'spell-out')
  reverse_speak_as(element, 'literal-punctuation')
  reverse_speak_as(element, 'no-punctuation')
  reverse_speak_as(element, 'digits')
end
speak_as_spell_out(element) click to toggle source

Speak one letter at a time for each word for element only.

@param element [Hatemile::Util::Html::HTMLDOMElement] The element.

# File lib/hatemile/implementation/accessible_css_implementation.rb, line 491
def speak_as_spell_out(element)
  speak_as(
    element,
    /[a-zA-Z]/,
    'spell-out',
    method(:operation_speak_as_spell_out)
  )
end
speak_as_spell_out_inherit(element) click to toggle source

Speak one letter at a time for each word for elements and descendants.

@param element [Hatemile::Util::Html::HTMLDOMElement] The element.

# File lib/hatemile/implementation/accessible_css_implementation.rb, line 504
def speak_as_spell_out_inherit(element)
  reverse_speak_as(element, 'spell-out')

  isolate_text_node(element)

  visit(element, method(:speak_as_spell_out))
end
speak_header_always_inherit(element) click to toggle source

The cells headers will be spoken for every data cell for element and descendants.

@param element [Hatemile::Util::Html::HTMLDOMElement] The element.

# File lib/hatemile/implementation/accessible_css_implementation.rb, line 598
def speak_header_always_inherit(element)
  speak_header_once_inherit(element)

  cell_elements = @html_parser.find(element).find_descendants(
    'td[headers],th[headers]'
  ).list_results
  accessible_display = AccessibleDisplayImplementation(
    @html_parser,
    @configure
  )
  cell_elements.each do |cell_element|
    accessible_display.display_cell_header(cell_element)
  end
end
speak_header_once_inherit(element) click to toggle source

The cells headers will be spoken one time for element and descendants.

@param element [Hatemile::Util::Html::HTMLDOMElement] The element.

# File lib/hatemile/implementation/accessible_css_implementation.rb, line 617
def speak_header_once_inherit(element)
  header_elements = @html_parser.find(element).find_descendants(
    "[#{DATA_ATTRIBUTE_HEADERS_OF}]"
  ).list_results
  header_elements.each(&:remove_node)
end
speak_none(element) click to toggle source

No speak any content of element only.

@param element [Hatemile::Util::Html::HTMLDOMElement] The element.

# File lib/hatemile/implementation/accessible_css_implementation.rb, line 402
def speak_none(element)
  element.set_attribute('role', 'presentation')
  element.set_attribute('aria-hidden', 'true')
  element.set_attribute(DATA_SPEAK, 'none')
end
speak_none_inherit(element) click to toggle source

No speak any content of element and descendants.

@param element [Hatemile::Util::Html::HTMLDOMElement] The element.

# File lib/hatemile/implementation/accessible_css_implementation.rb, line 412
def speak_none_inherit(element)
  isolate_text_node(element)

  visit(element, method(:speak_none))
end
speak_normal(element) click to toggle source

Speak the content of element only.

@param element [Hatemile::Util::Html::HTMLDOMElement] The element.

# File lib/hatemile/implementation/accessible_css_implementation.rb, line 375
def speak_normal(element)
  return unless element.has_attribute?(DATA_SPEAK)

  if element.get_attribute(DATA_SPEAK) == 'none' &&
     !element.has_attribute?(DATA_ISOLATOR_ELEMENT)
    element.remove_attribute('role')
    element.remove_attribute('aria-hidden')
    element.remove_attribute(DATA_SPEAK)
  else
    replace_element_by_own_content(element)
  end
end
speak_normal_inherit(element) click to toggle source

Speak the content of element and descendants.

@param element [Hatemile::Util::Html::HTMLDOMElement] The element.

# File lib/hatemile/implementation/accessible_css_implementation.rb, line 392
def speak_normal_inherit(element)
  visit(element, method(:speak_normal))

  element.normalize
end
visit(element, operation) click to toggle source

Visit and execute a operation in element and descendants.

@param element [Hatemile::Util::Html::HTMLDOMElement] The element. @param operation [Method] The operation to be executed.

# File lib/hatemile/implementation/accessible_css_implementation.rb, line 313
def visit(element, operation)
  return unless is_valid_inherit_element?(element)

  if element.has_children_elements?
    element.get_children_elements.each do |child|
      visit(child, operation)
    end
  elsif is_valid_element?(element)
    operation.call(element)
  end
end