class OpenStax::Content::Page

Constants

APBIO_DEF_NODE_CSS
LO_DEF_NODE_CSS

Find nodes that define relevant tags

LO_REGEX

Find specific tags and extract the relevant parts

ROOT_CSS

Start parsing here

SNAP_LAB_CSS

Find snap lab notes

SNAP_LAB_TITLE_CSS
STD_DEF_NODE_CSS
STD_DESC_NODE_CSS
STD_NAME_NODE_CSS
STD_REGEX
TEKS_DEF_NODE_CSS
TEKS_REGEX

Attributes

chapter_section[RW]
hash[R]
uuid[R]

Public Class Methods

feature_node(node, feature_ids) click to toggle source
# File lib/openstax/content/page.rb, line 27
def self.feature_node(node, feature_ids)
  feature_ids = [feature_ids].flatten
  return if feature_ids.empty?

  feature_id_css = feature_ids.map { |feature_id| "##{feature_id}" }.join(', ')
  node.at_css(feature_id_css)
end
new(book: nil, hash: {}, uuid: nil, url: nil, title: nil, content: nil) click to toggle source
# File lib/openstax/content/page.rb, line 35
def initialize(book: nil, hash: {}, uuid: nil, url: nil, title: nil, content: nil)
  @uuid    = uuid || hash['id']&.split('@', 2)&.first
  raise ArgumentError, 'Either uuid or hash with id key is required' if @uuid.nil?

  @book    = book
  @hash    = hash
  @url     = url
  @title   = title || hash['title']
  @content = content
end

Public Instance Methods

aplos() click to toggle source
# File lib/openstax/content/page.rb, line 117
def aplos
  @aplos ||= tags.select { |tag| tag[:type] == :aplo }.map { |tag| tag[:value] }
end
book() click to toggle source
# File lib/openstax/content/page.rb, line 49
def book
  raise ArgumentError, 'Book was not specified' if @book.nil?

  @book
end
book_location() click to toggle source
# File lib/openstax/content/page.rb, line 63
def book_location
  parsed_title.book_location
end
content() click to toggle source
# File lib/openstax/content/page.rb, line 79
def content
  @content ||= full_hash.fetch('content')
end
convert_content!() click to toggle source

Replaces links to embeddable sims (and maybe videos in the future) with iframes Changes exercise urls in the doc to be absolute

# File lib/openstax/content/page.rb, line 97
def convert_content!
  OpenStax::Content::Fragment::Interactive.replace_interactive_links_with_iframes!(doc)
  OpenStax::Content::Fragment::Exercise.absolutize_exercise_urls!(doc)
  map_note_format!(doc)
  @content = doc.to_html
  @root = nil
end
doc() click to toggle source
# File lib/openstax/content/page.rb, line 83
def doc
  @doc ||= Nokogiri::HTML(content)
end
footnotes() click to toggle source
# File lib/openstax/content/page.rb, line 91
def footnotes
  @footnotes ||= doc.css('[role=doc-footnote]')
end
full_hash() click to toggle source
# File lib/openstax/content/page.rb, line 71
def full_hash
  @full_hash ||= book.archive.json url
end
los() click to toggle source
# File lib/openstax/content/page.rb, line 113
def los
  @los ||= tags.select { |tag| tag[:type] == :lo }.map { |tag| tag[:value] }
end
parsed_title() click to toggle source
# File lib/openstax/content/page.rb, line 59
def parsed_title
  @parsed_title ||= OpenStax::Content::Title.new @title
end
root() click to toggle source
# File lib/openstax/content/page.rb, line 87
def root
  @root ||= doc.at_css(ROOT_CSS)
end
short_id() click to toggle source
# File lib/openstax/content/page.rb, line 75
def short_id
  @short_id ||= full_hash.fetch('shortId', nil)
end
snap_lab_nodes() click to toggle source
# File lib/openstax/content/page.rb, line 105
def snap_lab_nodes
  root.css(SNAP_LAB_CSS)
end
snap_lab_title(snap_lab) click to toggle source
# File lib/openstax/content/page.rb, line 109
def snap_lab_title(snap_lab)
  snap_lab.at_css(SNAP_LAB_TITLE_CSS).try(:text)
end
tags() click to toggle source
# File lib/openstax/content/page.rb, line 121
def tags
  return @tags.values unless @tags.nil?

  # Start with default cnxmod tag
  cnxmod_value = "context-cnxmod:#{uuid}"
  @tags = { cnxmod_value => { value: cnxmod_value, type: :cnxmod } }

  # Extract tag name and description from .ost-standards-def and .os-learning-objective-def.

  # LO tags
  root.css(LO_DEF_NODE_CSS).each do |node|
    klass = node.attr('class')
    lo_value = LO_REGEX.match(klass).try(:[], 1)
    next if lo_value.nil?

    teks_value = TEKS_REGEX.match(klass).try(:[], 1)
    description = node.content.strip

    @tags[lo_value] = {
      value: lo_value,
      description: description,
      teks: teks_value,
      type: :lo
    }
  end

  # Other standards
  root.css(STD_DEF_NODE_CSS).each do |node|
    klass = node.attr('class')
    name = node.at_css(STD_NAME_NODE_CSS).try(:content).try(:strip)
    description = node.at_css(STD_DESC_NODE_CSS).try(:content).try(:strip)
    value = nil

    if node.matches?(TEKS_DEF_NODE_CSS)
      value = TEKS_REGEX.match(klass).try(:[], 1)
      type = :teks
    elsif node.matches?(APBIO_DEF_NODE_CSS)
      value = LO_REGEX.match(klass).try(:[], 1)
      type = :aplo
    end

    next if value.nil?

    @tags[value] = {
      value: value,
      name: name,
      description: description,
      type: type
    }
  end

  @tags.values
end
title() click to toggle source
# File lib/openstax/content/page.rb, line 67
def title
  parsed_title.text
end
url() click to toggle source
# File lib/openstax/content/page.rb, line 55
def url
  @url ||= "#{book.url_fragment}:#{uuid}.json"
end

Protected Instance Methods

map_note_format!(node) click to toggle source

Adds a container div around note content for styling

# File lib/openstax/content/page.rb, line 178
  def map_note_format!(node)
    note_selector = <<-eos
      .note:not(.learning-objectives),
      .example,
      .grasp-check,
      [data-type="note"],
      [data-element-type="check-understanding"]
    eos

    note_selector = note_selector.gsub(/\s+/, "")

    node.css(note_selector).each do |note|
      note.set_attribute('data-tutor-transform', true)
      body = Nokogiri::XML::Node.new('div', doc)
      body.set_attribute('data-type', 'content')

      content = note.css('>*:not([data-type=title])')
      content.unlink()

      body.children = content
      note.add_child(body)
    end
  end