openstax_kitchen 15.0.0 → 18.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +38 -0
  3. data/Gemfile.lock +5 -5
  4. data/lib/kitchen/directions/bake_annotation_classes/v1.rb +11 -2
  5. data/lib/kitchen/directions/bake_autotitled_exercise/main.rb +4 -0
  6. data/lib/kitchen/directions/bake_autotitled_exercise/v3.rb +35 -0
  7. data/lib/kitchen/directions/bake_chapter_references/main.rb +15 -0
  8. data/lib/kitchen/directions/bake_chapter_references/v2.rb +53 -0
  9. data/lib/kitchen/directions/bake_chapter_references/v3.rb +45 -0
  10. data/lib/kitchen/directions/bake_chapter_summary.rb +5 -4
  11. data/lib/kitchen/directions/bake_exercise_prefixes/main.rb +14 -0
  12. data/lib/kitchen/directions/bake_exercise_prefixes/v1.rb +22 -0
  13. data/lib/kitchen/directions/bake_footnotes/v1.rb +1 -1
  14. data/lib/kitchen/directions/bake_handbook/v1.rb +1 -1
  15. data/lib/kitchen/directions/bake_index/v1.rb +13 -8
  16. data/lib/kitchen/directions/bake_index/v1.xhtml.erb +1 -1
  17. data/lib/kitchen/directions/{bake_injected_exercise.rb → bake_injected_exercise/bake_injected_exercise.rb} +10 -1
  18. data/lib/kitchen/directions/bake_injected_exercise/bake_injected_exercise_question.rb +4 -0
  19. data/lib/kitchen/directions/bake_link_placeholders.rb +1 -1
  20. data/lib/kitchen/directions/bake_lo_link_labels.rb +19 -0
  21. data/lib/kitchen/directions/bake_non_introduction_pages.rb +3 -1
  22. data/lib/kitchen/directions/bake_numbered_table/bake_table_body.rb +6 -1
  23. data/lib/kitchen/directions/bake_numbered_table/v2.rb +2 -1
  24. data/lib/kitchen/directions/bake_references/v1.rb +13 -0
  25. data/lib/kitchen/directions/bake_screenreader_spans.rb +22 -0
  26. data/lib/kitchen/directions/bake_unit_page_title/v1.rb +8 -2
  27. data/lib/kitchen/directions/bake_unnumbered_tables.rb +4 -0
  28. data/lib/kitchen/element_base.rb +25 -5
  29. data/lib/kitchen/element_enumerator_base.rb +18 -0
  30. data/lib/kitchen/injected_exercise_element.rb +41 -0
  31. data/lib/kitchen/injected_exercise_element_enumerator.rb +21 -0
  32. data/lib/kitchen/injected_question_element.rb +7 -0
  33. data/lib/kitchen/patches/i18n.rb +4 -0
  34. data/lib/kitchen/patches/integer.rb +32 -3
  35. data/lib/kitchen/patches/nokogiri.rb +5 -4
  36. data/lib/kitchen/selectors/base.rb +3 -0
  37. data/lib/kitchen/selectors/standard_1.rb +1 -0
  38. data/lib/kitchen/version.rb +1 -1
  39. data/lib/locales/en.yml +16 -0
  40. metadata +12 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 074dfc264ca97aab5805a3ab189b49fe1e97a1108366b592b5599a70e15f1345
4
- data.tar.gz: fee3491d899d7983e55e5c6eb22010b49d23e8dd43e54ca97950a7be2fbda93f
3
+ metadata.gz: 4343b799c2a49cec3c179ba3af59dae109cdb4fb4839fd7993710d6a1125d322
4
+ data.tar.gz: 6ac5fcdb6adda5fbf080b507676156c42dfc301f8060bae4dabe1bf3561fbebf
5
5
  SHA512:
6
- metadata.gz: d9a5936f931cf6b1c3485543adf375426d71b444109c68fc80b0a06ef6f953e4ac43cd40b27941c06dec0541e02dd2b3387074aa8a956821cdca781f97607f1d
7
- data.tar.gz: 13d5bf302fe8144acef670f017bc8393847292d37f4a4dae68922cbf56c52da41c18719ec586ca1b5b1a135dc0c303e168f5515903e2b667299347cba909e7d4
6
+ metadata.gz: 4dad4bfc3619807740965bc6d8f9c4332e13d2031433a5b32576d3fb66d79005ae08bebd9e25398097921ff15e1c835ebefa2b3a35d30185f194442412355f89
7
+ data.tar.gz: 6372b3c2860ef570dd6e0ff9011ee08759138843eef48953a2187452581edcccd96df337d1441bad5f03c32482c9058104f2ae20d4e50a905baf279a478fdb87
data/CHANGELOG.md CHANGED
@@ -6,6 +6,44 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+
10
+ ## [18.0.0] - 2022-01-14
11
+
12
+ * Fix links in `BakeHanbook` outline to point sections (patch)
13
+ * Add unit title prefix with number to `BakeUnitPageTitle` (patch)
14
+ * Add `scope="col"` attribute to `BakeTableBody` and `BakeUnnumberedTables` (major)
15
+
16
+ ## [17.1.0] - 2021-12-17
17
+
18
+ * Add append_to support to `BakeChapterSummary` (minor)
19
+ * Add support for italicized terms in index (minor)
20
+ * Fix problem with namespaces in `BakeLinkPlaceholders` and `BakeIndex` (minor)
21
+ * Create `V3` for `BakeChapterReferences` which sorts references alphabetically (minor)
22
+
23
+ ## [17.0.0] - 2021-12-3
24
+
25
+ * Create method in `Integer` class that generate roman numbers up to 100 (minor)
26
+ * Add more roman numbers to `Integer` class (patch)
27
+ * Create `V2` for `BakeChapterReferences` (minor)
28
+ * Create `BakeExercisePrefixes` direction adding prefixes for exercises in selected sections (minor)
29
+ * Add support for 'text-heavy-top-titled' tables in `BakeTableBody` (minor)
30
+ * Remove `Nokigiri#previous` patch, `ElementBase#previous` now uses `#previous_element` (minor)
31
+ * Modifies `BakeAnnotationClasses` for annotations wrapper to be a `span` intead of a `div` (major)
32
+
33
+ ## [16.0.0] - 2021-11-19
34
+
35
+ * Add reference link separator to `BakeReferences.v1` (patch)
36
+ * Modify `BakeFootnotes` to be more general (minor)
37
+ * Add `#preceded_by_text` method to element_base and the nokigiri patch (minor)
38
+ * Remove `Nokigiri#previous` patch, `ElementBase#previous` now uses `#previous_element` (minor)
39
+ * Broaden caption selection for `BakeNumberedTable#v2` (patch)
40
+ * Add details of question count to injected exercises in `BakeInjectedExercise` (major)
41
+ * Add target labels to chapter content module pages option in `BakeNonIntroductionPages`, create a separate directory `BakeLOLinkLabels` to add `.label-text`, `.label-counter` spans wrappers for links with `.lo-reference` class (minor)
42
+ * Add `BakeScreenreaderSpans` direction (minor)
43
+ * Fix `BakeIndex` to group terms by character in polish books and transliterate it for others (minor)
44
+ * Add optional bake `exercies-context` figure_reference if there is one present in singular part exercises to `BakeInjectedExercise`,`BakeInjectedQuestion` to move it down from exercise to question problem container. (major)
45
+ * Create v3 for autotitled exercises with os-hasSolution class
46
+
9
47
  ## [15.0.0] - 2021-11-05
10
48
 
11
49
  * Add unstyled tables to `BakeTableBody` (minor)
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- openstax_kitchen (15.0.0)
4
+ openstax_kitchen (18.0.0)
5
5
  activesupport
6
6
  i18n
7
7
  nokogiri
@@ -11,7 +11,7 @@ PATH
11
11
  GEM
12
12
  remote: https://rubygems.org/
13
13
  specs:
14
- activesupport (6.1.4.1)
14
+ activesupport (6.1.4.4)
15
15
  concurrent-ruby (~> 1.0, >= 1.0.2)
16
16
  i18n (>= 1.6, < 2)
17
17
  minitest (>= 5.1)
@@ -36,7 +36,7 @@ GEM
36
36
  yard (~> 0.9.12)
37
37
  method_source (1.0.0)
38
38
  mini_portile2 (2.5.0)
39
- minitest (5.14.4)
39
+ minitest (5.15.0)
40
40
  nokogiri (1.11.1)
41
41
  mini_portile2 (~> 2.5.0)
42
42
  racc (~> 1.4)
@@ -99,7 +99,7 @@ GEM
99
99
  tins (~> 1.0)
100
100
  tins (1.26.0)
101
101
  sync
102
- twitter_cldr (6.7.0)
102
+ twitter_cldr (6.11.0)
103
103
  camertron-eprun
104
104
  cldr-plurals-runtime-rb (~> 1.1)
105
105
  tzinfo
@@ -107,7 +107,7 @@ GEM
107
107
  concurrent-ruby (~> 1.0)
108
108
  unicode-display_width (1.7.0)
109
109
  yard (0.9.24)
110
- zeitwerk (2.5.1)
110
+ zeitwerk (2.5.3)
111
111
 
112
112
  PLATFORMS
113
113
  ruby
@@ -20,9 +20,18 @@ module Kitchen::Directions::BakeAnnotationClasses
20
20
  kinesthetic-icon]
21
21
  annotation_icon_classes.each do |annotation_icon_class|
22
22
  book.search("p.#{annotation_icon_class}").each do |annotation_with_icon_class|
23
- annotation_with_icon_class.search('div.os-icons').first.append(child:
23
+ annotation_with_icon_class.search('div.os-icons').first&.name = 'span'
24
+
25
+ icon_title = I18n.t(:"annotation_icons.#{annotation_icon_class}.title")
26
+
27
+ annotation_with_icon_class.search('span.os-icons').first.append(child:
24
28
  <<~HTML
25
- <span class = "#{annotation_icon_class}"></span>
29
+ <span
30
+ class = "#{annotation_icon_class}"
31
+ role="img"
32
+ title="#{icon_title}"
33
+ aria-label="#{icon_title}"
34
+ ></span>
26
35
  HTML
27
36
  )
28
37
  end
@@ -10,6 +10,10 @@ module Kitchen
10
10
  def self.v2(exercise:, title:)
11
11
  V2.new.bake(exercise: exercise, title: title)
12
12
  end
13
+
14
+ def self.v3(exercise:, title:)
15
+ V3.new.bake(exercise: exercise, title: title)
16
+ end
13
17
  end
14
18
  end
15
19
  end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kitchen::Directions::BakeAutotitledExercise
4
+ # Differences from V2:
5
+ # 1. Exercise with solution has class os-hasSolution
6
+ # 2. Solution id is based on exercise id
7
+ class V3
8
+ def bake(exercise:, title:)
9
+ exercise.add_class('unnumbered')
10
+ exercise.titles.first&.trash
11
+
12
+ # bake problem
13
+ exercise.prepend(child:
14
+ <<~HTML
15
+ <h3 class="os-title" data-type="title">
16
+ <span class="os-title-label">#{title}</span>
17
+ </h3>
18
+ HTML
19
+ )
20
+ exercise.problem.wrap_children(class: 'os-problem-container')
21
+ return unless exercise.solution
22
+
23
+ exercise.add_class('os-hasSolution')
24
+ exercise.solution.id = "#{exercise.id}-solution"
25
+ exercise.solution.wrap_children(class: 'os-solution-container')
26
+ exercise.solution.prepend(child:
27
+ <<~HTML
28
+ <h4 class="solution-title" data-type="title">
29
+ <span class="os-text">#{I18n.t(:solution)}</span>
30
+ </h4>
31
+ HTML
32
+ )
33
+ end
34
+ end
35
+ end
@@ -10,6 +10,21 @@ module Kitchen
10
10
  uuid_prefix: uuid_prefix,
11
11
  klass: klass)
12
12
  end
13
+
14
+ def self.v2(chapter:, metadata_source:, uuid_prefix: '.', klass: 'references')
15
+ V2.new.bake(
16
+ chapter: chapter,
17
+ metadata_source: metadata_source,
18
+ uuid_prefix: uuid_prefix,
19
+ klass: klass)
20
+ end
21
+
22
+ def self.v3(chapter:, metadata_source:, uuid_prefix: '.')
23
+ V3.new.bake(
24
+ chapter: chapter,
25
+ metadata_source: metadata_source,
26
+ uuid_prefix: uuid_prefix)
27
+ end
13
28
  end
14
29
  end
15
30
  end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kitchen::Directions::BakeChapterReferences
4
+ class V2
5
+ def bake(chapter:, metadata_source:, uuid_prefix: '.', klass: 'references')
6
+ chapter.pages.each do |page|
7
+ bake_page_cite(page: page)
8
+ bake_page_references(page: page)
9
+ end
10
+
11
+ return if chapter.pages.references.none?
12
+
13
+ content = chapter.pages.references.cut.paste
14
+
15
+ Kitchen::Directions::CompositePageContainer.v1(
16
+ container_key: klass,
17
+ uuid_key: "#{uuid_prefix}#{klass}",
18
+ metadata_source: metadata_source,
19
+ content: content,
20
+ append_to: chapter
21
+ )
22
+ end
23
+
24
+ def bake_page_cite(page:)
25
+ page.search('[data-type="cite"]').each do |link|
26
+ link.id = "#{page.id}-endNote#{link.count_in(:chapter)}"
27
+
28
+ link.prepend(child:
29
+ <<~HTML
30
+ <sup class="os-end-note-number">#{link.count_in(:chapter)}</sup>
31
+ HTML
32
+ )
33
+
34
+ link.search('.delete-me').trash
35
+ end
36
+ end
37
+
38
+ def bake_page_references(page:)
39
+ page.references.each do |reference|
40
+ Kitchen::Directions::RemoveSectionTitle.v1(section: reference)
41
+
42
+ reference.search('a').each do |ref_link|
43
+ ref_link.replace_children(with:
44
+ <<~HTML
45
+ <span>#{ref_link.count_in(:chapter)}.</span>
46
+ HTML
47
+ )
48
+ ref_link.href = "##{page.id}-endNote#{ref_link.count_in(:chapter)}"
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kitchen::Directions::BakeChapterReferences
4
+ class V3
5
+ class Reference
6
+ attr_reader :element
7
+
8
+ def initialize(element)
9
+ reference_item = Kitchen::I18nString.new(element.text.downcase)
10
+ @sortable = [reference_item]
11
+ @element = element
12
+ end
13
+
14
+ def <=>(other)
15
+ sortable <=> other.sortable
16
+ end
17
+
18
+ protected
19
+
20
+ attr_reader :sortable
21
+ end
22
+
23
+ def bake(chapter:, metadata_source:, append_to: nil, uuid_prefix: '.')
24
+ @references = []
25
+
26
+ chapter.references.search('p').each do |reference_element|
27
+ @references.push(Reference.new(reference_element.cut))
28
+ end
29
+
30
+ chapter.references.trash
31
+
32
+ content = @references.sort.map { |reference| reference.element.paste }.join
33
+
34
+ return if content.empty?
35
+
36
+ Kitchen::Directions::CompositePageContainer.v1(
37
+ container_key: 'references',
38
+ uuid_key: "#{uuid_prefix}references",
39
+ metadata_source: metadata_source,
40
+ content: content,
41
+ append_to: append_to || chapter
42
+ )
43
+ end
44
+ end
45
+ end
@@ -5,17 +5,18 @@ module Kitchen
5
5
  # Bake directions for eoc summary
6
6
  #
7
7
  module BakeChapterSummary
8
- def self.v1(chapter:, metadata_source:, klass: 'summary', uuid_prefix: '.')
8
+ def self.v1(chapter:, metadata_source:, klass: 'summary', uuid_prefix: '.', append_to: nil)
9
9
  V1.new.bake(
10
10
  chapter: chapter,
11
11
  metadata_source: metadata_source,
12
12
  uuid_prefix: uuid_prefix,
13
- klass: klass
13
+ klass: klass,
14
+ append_to: append_to
14
15
  )
15
16
  end
16
17
 
17
18
  class V1
18
- def bake(chapter:, metadata_source:, uuid_prefix: '.', klass: 'summary')
19
+ def bake(chapter:, metadata_source:, uuid_prefix: '.', klass: 'summary', append_to: nil)
19
20
  summaries = Clipboard.new
20
21
 
21
22
  chapter.pages.each do |page|
@@ -36,7 +37,7 @@ module Kitchen
36
37
  uuid_key: "#{uuid_prefix}#{klass}",
37
38
  metadata_source: metadata_source,
38
39
  content: summaries.paste,
39
- append_to: chapter
40
+ append_to: append_to || chapter
40
41
  )
41
42
  end
42
43
  end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kitchen
4
+ module Directions
5
+ module BakeExercisePrefixes
6
+ def self.v1(chapter:, sections_prefixed:)
7
+ V1.new.bake(
8
+ chapter: chapter,
9
+ sections_prefixed: sections_prefixed
10
+ )
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kitchen::Directions::BakeExercisePrefixes
4
+ class V1
5
+ def bake(chapter:, sections_prefixed:)
6
+ sections_prefixed.each do |section_key|
7
+ chapter.composite_pages.each do |composite_page|
8
+ composite_page.search("section.#{section_key}").exercises.each do |exercise|
9
+ problem = exercise.problem
10
+ exercise_prefix =
11
+ "<span class='os-text'>#{I18n.t(:"sections_prefixed.#{section_key}")}</span>"
12
+ problem.prepend(child:
13
+ <<~HTML
14
+ #{exercise_prefix}
15
+ HTML
16
+ )
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -30,7 +30,7 @@ module Kitchen::Directions::BakeFootnotes
30
30
  aside_id_to_footnote_number[aside_id] = footnote_number
31
31
  if anchor.parent.name == 'p'
32
32
  anchor.parent.add_class('has-noteref')
33
- elsif anchor.parent.name == 'em' && anchor.parent.parent.name == 'p'
33
+ elsif anchor.parent.name != 'p' && anchor.parent.parent.name == 'p'
34
34
  anchor.parent.parent.add_class('has-noteref')
35
35
  end
36
36
  end
@@ -43,7 +43,7 @@ module Kitchen::Directions::BakeHandbook
43
43
 
44
44
  outline_item_html = <<~HTML
45
45
  <div class="os-handbook-objective">
46
- <a class="os-handbook-objective" href="##{first_section_title[:id]}">
46
+ <a class="os-handbook-objective" href="##{section[:id]}">
47
47
  #{first_section_title.children}
48
48
  </a>
49
49
  </div>
@@ -11,12 +11,14 @@ module Kitchen::Directions::BakeIndex
11
11
  attr_reader :id
12
12
  attr_reader :group_by
13
13
  attr_reader :page_title
14
+ attr_reader :italicized
14
15
 
15
- def initialize(text:, id:, group_by:, page_title:)
16
+ def initialize(text:, id:, group_by:, page_title:, italicized:)
16
17
  @text = text.strip
17
18
  @id = id
18
19
  @group_by = group_by
19
20
  @page_title = page_title
21
+ @italicized = italicized
20
22
  end
21
23
  end
22
24
 
@@ -126,7 +128,8 @@ module Kitchen::Directions::BakeIndex
126
128
  page = term_element.ancestor(:page)
127
129
  term_element.id = "auto_#{page.id}_term#{term_element.count_in(:book)}"
128
130
  page_title = page.title.text
129
- add_term_to_index(term_element, page_title)
131
+ term_italicized = term_element&.first("em[data-effect='italics']")
132
+ add_term_to_index(term_element, page_title, term_italicized)
130
133
  end
131
134
 
132
135
  book.chapters.composite_pages.terms.each do |term_element|
@@ -135,7 +138,8 @@ module Kitchen::Directions::BakeIndex
135
138
  term_element.id = "auto_composite_page_term#{term_element.count_in(:book)}"
136
139
  chapter_number = chapter.count_in(:book)
137
140
  page_title = "#{chapter_number} #{page.title.text.strip}".strip
138
- add_term_to_index(term_element, page_title)
141
+ term_italicized = term_element&.first("em[data-effect='italics']")
142
+ add_term_to_index(term_element, page_title, term_italicized)
139
143
  end
140
144
 
141
145
  types.each do |type|
@@ -150,22 +154,22 @@ module Kitchen::Directions::BakeIndex
150
154
  end
151
155
  end
152
156
 
153
- def add_term_to_index(term_element, page_title)
157
+ def add_term_to_index(term_element, page_title, term_italicized)
154
158
  type =
155
159
  if !term_element.key?('index')
156
160
  'term'
157
- elsif term_element['cxlxt:index'] == 'name'
161
+ elsif term_element['cxlxt:index'] == 'name' || term_element['index'] == 'name'
158
162
  'name'
159
- elsif term_element['cxlxt:index'] == 'foreign'
163
+ elsif term_element['cxlxt:index'] == 'foreign' || term_element['index'] == 'foreign'
160
164
  'foreign'
161
165
  end
162
166
 
163
167
  if term_element.key?('reference')
164
- term_reference = term_element['cmlnle:reference']
168
+ term_reference = term_element['cmlnle:reference'] || term_element['reference']
165
169
  group_by = term_reference[0]
166
170
  content = term_reference
167
171
  else
168
- group_by = I18n.transliterate(term_element.text.strip[0])
172
+ group_by = I18n.character_to_group(term_element.text.strip[0])
169
173
  content = term_element.text
170
174
  end
171
175
 
@@ -175,6 +179,7 @@ module Kitchen::Directions::BakeIndex
175
179
  # Add it to our index object
176
180
  @indexes[type].add_term(
177
181
  Term.new(
182
+ italicized: term_italicized,
178
183
  text: content,
179
184
  id: term_element.id,
180
185
  group_by: group_by,
@@ -13,7 +13,7 @@
13
13
  <div class="os-index-item">
14
14
  <%- item.terms.each_with_index do |term, ii| -%>
15
15
  <%- if ii == 0 -%>
16
- <span class="os-term" group-by="<%= term.group_by %>"><%= term.text %></span>
16
+ <span class="os-term" group-by="<%= term.group_by %>"><%- if term.italicized -%><em data-effect="italics"><%= term.text %></em><%- else -%><%= term.text %><% end %></span>
17
17
  <%- else -%><span class="os-index-link-separator">, </span><% end %>
18
18
  <a class="os-term-section-link" href="#<%= term.id %>"><span class="os-term-section"><%= term.page_title %></span></a><!--
19
19
  --><%- end %>
@@ -7,12 +7,21 @@ module Kitchen::Directions::BakeInjectedExercise
7
7
 
8
8
  class V1
9
9
  def bake(exercise:)
10
- context = exercise.search('div[data-type="exercise-context"]')&.first
10
+ question_count = exercise.injected_questions.count
11
+ exercise[:'data-question-count'] = question_count
12
+ exercise[:'data-is-multipart'] = question_count > 1 ? 'True' : 'False'
13
+
14
+ context = exercise&.exercise_context
15
+
11
16
  return unless context
12
17
 
13
18
  # link replacement is done by BakeLinkPlaceholders
14
19
  link = context.first('a').cut
15
20
  context.replace_children(with: "#{I18n.t(:context_lead_text)}#{link.paste}")
21
+ return unless question_count == 1
22
+
23
+ question = exercise.exercise_question
24
+ question.prepend(child: context.cut.paste)
16
25
  end
17
26
  end
18
27
  end
@@ -41,11 +41,15 @@ module Kitchen::Directions::BakeInjectedExerciseQuestion
41
41
  end
42
42
  end
43
43
 
44
+ context = question.exercise_context_in_question&.cut&.paste
45
+
44
46
  question.prepend(child:
45
47
  <<~HTML
46
48
  #{problem_number unless only_number_solution}
47
49
  #{"<span class='os-divider'>. </span>" unless only_number_solution}
48
50
  <div class="os-problem-container">
51
+ #{context if context.present?}
52
+ #{"<span class='os-divider'>. </span>" if context.present?}
49
53
  #{question.stimulus&.cut&.paste}
50
54
  #{question.stem.cut.paste}
51
55
  #{question.answers&.cut&.paste}
@@ -9,7 +9,7 @@ module Kitchen
9
9
  book.search('a').each do |anchor|
10
10
  next unless anchor.text == '[link]'
11
11
 
12
- label_case = anchor['cmlnle:case']
12
+ label_case = anchor['cmlnle:case'] || anchor['case']
13
13
  id = anchor[:href][1..-1]
14
14
 
15
15
  if cases
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kitchen
4
+ module Directions
5
+ # Bake directions for LO link labels
6
+ module BakeLOLinkLabels
7
+ def self.v1(book:)
8
+ book.search('a.lo-reference').each do |anchor|
9
+ anchor.wrap_children('span', class: 'label-counter')
10
+ anchor.prepend(child:
11
+ <<~HTML
12
+ <span class="label-text">#{I18n.t(:lo_label_text)}</span>
13
+ HTML
14
+ )
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -3,13 +3,15 @@
3
3
  module Kitchen
4
4
  module Directions
5
5
  module BakeNonIntroductionPages
6
- def self.v1(chapter:)
6
+ def self.v1(chapter:, add_target_label: false)
7
7
  chapter.non_introduction_pages.each do |page|
8
8
  number = "#{chapter.count_in(:book)}.#{page.count_in(:chapter)}"
9
9
 
10
10
  page.search("div[data-type='description']").each(&:trash)
11
11
  page.add_class('chapter-content-module')
12
12
 
13
+ page.target_label(custom_content: number) if add_target_label
14
+
13
15
  title = page.title
14
16
  title.name = 'h2'
15
17
  title.replace_children(with:
@@ -40,12 +40,17 @@ module Kitchen
40
40
  table.remove_attribute('summary')
41
41
  table.wrap(%(<div class="os-table">))
42
42
 
43
+ table.search('th').each do |header|
44
+ header[:scope] = 'col'
45
+ end
46
+
43
47
  # Store label information
44
48
  table.target_label(label_text: 'table', custom_content: number, cases: cases)
45
49
 
46
50
  if table.top_titled?
51
+ klass = table.text_heavy? ? 'text-heavy-top-titled' : 'top-titled'
47
52
  custom_table = CustomBody.new(table: table,
48
- klass: 'top-titled',
53
+ klass: klass,
49
54
  fake_title_class: 'os-table-title',
50
55
  fake_title: table.title,
51
56
  to_trash: table.title_row)
@@ -9,7 +9,8 @@ module Kitchen::Directions::BakeNumberedTable
9
9
  Kitchen::Directions::BakeTableBody::V1.new.bake(table: table, number: number, cases: cases)
10
10
 
11
11
  caption = ''
12
- if table&.caption&.first("span[data-type='title']") && !table.top_captioned?
12
+ if (table&.caption&.first("span[data-type='title']") || table&.caption) \
13
+ && !table.top_captioned?
13
14
  caption_el = table.caption
14
15
  caption_el.add_class('os-caption')
15
16
  caption_el.name = 'span'
@@ -12,6 +12,19 @@ module Kitchen::Directions::BakeReferences
12
12
  <sup class="os-citation-number">#{link.count_in(:chapter)}</sup>
13
13
  HTML
14
14
  )
15
+
16
+ next if link.nil?
17
+
18
+ link_sibling = link.previous unless link.preceded_by_text?
19
+ next if link_sibling.nil?
20
+
21
+ next unless link_sibling&.raw&.attr('data-type') == 'cite'
22
+
23
+ link.prepend(sibling:
24
+ <<~HTML
25
+ <span class="os-reference-link-separator">, </span>
26
+ HTML
27
+ )
15
28
  end
16
29
 
17
30
  chapter.references.each do |reference|
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kitchen
4
+ module Directions
5
+ module BakeScreenreaderSpans
6
+ # Add text for accessibility.
7
+ # Additional screenreader spans can be added below.
8
+ def self.v1(book:)
9
+ book.search('u[data-effect="double-underline"]').each do |element|
10
+ element.add_previous_sibling(
11
+ '<span data-screenreader-only="true">double underline</span>'
12
+ )
13
+ end
14
+ book.search('u[data-effect="underline"]').each do |element|
15
+ element.add_previous_sibling(
16
+ '<span data-screenreader-only="true">underline</span>'
17
+ )
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -4,17 +4,23 @@ module Kitchen::Directions::BakeUnitPageTitle
4
4
  class V1
5
5
  def bake(book:)
6
6
  book.units.each do |unit|
7
+ @unit_title = I18n.t(:unit)
8
+ @unit_number = unit.count_in(:book)
7
9
  unit.element_children.only(Kitchen::PageElement).each do |page|
8
- compose_unit_page_title(page: page)
10
+ compose_unit_page_title(page: page, unit_title_prefix: @unit_title,
11
+ unit_number: @unit_number)
9
12
  end
10
13
  end
11
14
  end
12
15
 
13
- def compose_unit_page_title(page:)
16
+ def compose_unit_page_title(page:, unit_title_prefix:, unit_number:)
14
17
  title = page.title
15
18
  title.name = 'h2'
16
19
  title.replace_children(with:
17
20
  <<~HTML
21
+ <span class="os-part-text">#{unit_title_prefix} </span>
22
+ <span class="os-number">#{unit_number}</span>
23
+ <span class="os-divider"> </span>
18
24
  <span data-type="" itemprop="" class="os-text">#{title.text}</span>
19
25
  HTML
20
26
  )
@@ -10,6 +10,10 @@ module Kitchen
10
10
  table.parent.add_class('os-unstyled-container') if table.unstyled?
11
11
  table.parent.add_class('os-column-header-container') if table.column_header?
12
12
  table.parent.add_class('os-top-titled-container') if table.top_titled?
13
+
14
+ table.search('th').each do |header|
15
+ header[:scope] = 'col'
16
+ end
13
17
  end
14
18
  end
15
19
  end
@@ -82,9 +82,13 @@ module Kitchen
82
82
  # Set the inner HTML for this element
83
83
  # @see https://www.rubydoc.info/github/sparklemotion/nokogiri/Nokogiri/XML/Node#inner_html=-instance_method Nokogiri::XML::Node#inner_html=
84
84
  # @return Object
85
+ # @!method preceded_by_text
86
+ # Returns true if the immediately preceding sibling is text
87
+ # @return Boolean
85
88
  def_delegators :@node, :name=, :name, :[], :[]=, :add_class, :remove_class,
86
89
  :text, :wrap, :children, :to_html, :remove_attribute,
87
- :key?, :classes, :path, :inner_html=
90
+ :key?, :classes, :path, :inner_html=, :add_previous_sibling,
91
+ :preceded_by_text?
88
92
 
89
93
  # @!method config
90
94
  # Get the config for this element's document
@@ -219,6 +223,22 @@ module Kitchen
219
223
  self[:'data-type']
220
224
  end
221
225
 
226
+ # Returns the element's href
227
+ #
228
+ # @return [String]
229
+ #
230
+ def href
231
+ self[:href]
232
+ end
233
+
234
+ # Sets the element's href
235
+ #
236
+ # @param value [String] the new value for the href
237
+ #
238
+ def href=(value)
239
+ self[:href] = value
240
+ end
241
+
222
242
  # A way to set values and chain them
223
243
  #
224
244
  # @param property [String, Symbol] the name of the property to set
@@ -502,12 +522,12 @@ module Kitchen
502
522
  Element.new(node: raw.parent, document: document, short_type: "parent(#{short_type})")
503
523
  end
504
524
 
505
- # returns previous element
506
- # skips double indentations that the nokigiri sometimes picks up
525
+ # returns previous element sibling
526
+ # (only returns elements or nil)
507
527
  # nil if there's no previous sibling
508
528
  #
509
529
  def previous
510
- prev = raw.previous
530
+ prev = raw.previous_element
511
531
  return prev if prev.nil?
512
532
 
513
533
  Element.new(
@@ -754,7 +774,7 @@ module Kitchen
754
774
  def_delegators :as_enumerator, :pages, :chapters, :terms, :figures, :notes, :tables, :examples,
755
775
  :metadatas, :non_introduction_pages, :units, :titles, :exercises, :references,
756
776
  :composite_pages, :composite_chapters, :solutions, :injected_questions,
757
- :search_with, :sections
777
+ :search_with, :sections, :injected_exercises
758
778
 
759
779
  # Returns this element as an enumerator (over only one element, itself)
760
780
  #
@@ -336,6 +336,24 @@ module Kitchen
336
336
  css_or_xpath: css_or_xpath, only: only, except: except)
337
337
  end
338
338
 
339
+ # Returns an enumerator that iterates through injected exercises within the scope of this enumerator
340
+ #
341
+ # @param css_or_xpath [String] additional selectors to further narrow the element iterated over;
342
+ # a "$" in this argument will be replaced with the default selector for the element being
343
+ # iterated over.
344
+ # @param only [Symbol, Callable] the name of a method to call on an element or a
345
+ # lambda or proc that accepts an element; elements will only be included in the
346
+ # search results if the method or callable returns true
347
+ # @param except [Symbol, Callable] the name of a method to call on an element or a
348
+ # lambda or proc that accepts an element; elements will not be included in the
349
+ # search results if the method or callable returns false
350
+ #
351
+ def injected_exercises(css_or_xpath=nil, only: nil, except: nil)
352
+ block_error_if(block_given?)
353
+ chain_to(InjectedExerciseElementEnumerator,
354
+ css_or_xpath: css_or_xpath, only: only, except: except)
355
+ end
356
+
339
357
  # Returns an enumerator that iterates within the scope of this enumerator
340
358
  #
341
359
  # @param css_or_xpath [String] additional selectors to further narrow the element iterated over
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kitchen
4
+ # An element for an example
5
+ #
6
+ class InjectedExerciseElement < ElementBase
7
+
8
+ # Creates a new +InjectedQuestionElement+
9
+ #
10
+ # @param node [Nokogiri::XML::Node] the node this element wraps
11
+ # @param document [Document] this element's document
12
+ #
13
+ def initialize(node:, document: nil)
14
+ super(node: node,
15
+ document: document,
16
+ enumerator_class: InjectedExerciseElementEnumerator)
17
+ end
18
+
19
+ # Returns the short type
20
+ # @return [Symbol]
21
+ #
22
+ def self.short_type
23
+ :injected_exercise
24
+ end
25
+
26
+ # Returns the exercise context element.
27
+ # @return [Element]
28
+ #
29
+ def exercise_context
30
+ first("div[data-type='exercise-context']")
31
+ end
32
+
33
+ # Returns the exercise question element.
34
+ # @return [Element]
35
+ #
36
+ def exercise_question
37
+ first("div[data-type='exercise-question']")
38
+ end
39
+
40
+ end
41
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kitchen
4
+ # An enumerator for example elements
5
+ #
6
+ class InjectedExerciseElementEnumerator < ElementEnumeratorBase
7
+
8
+ # Returns a factory for this enumerator
9
+ #
10
+ # @return [ElementEnumeratorFactory]
11
+ #
12
+ def self.factory
13
+ ElementEnumeratorFactory.new(
14
+ default_css_or_xpath: Selector.named(:injected_exercise),
15
+ sub_element_class: InjectedExerciseElement,
16
+ enumerator_class: self
17
+ )
18
+ end
19
+
20
+ end
21
+ end
@@ -55,6 +55,13 @@ module Kitchen
55
55
  first("div[data-type='question-solution']")
56
56
  end
57
57
 
58
+ # Returns the exercise context element.
59
+ # @return [Element]
60
+ #
61
+ def exercise_context_in_question
62
+ first("div[data-type='exercise-context']")
63
+ end
64
+
58
65
  # Returns the answer correctness given an alphabet
59
66
  #
60
67
  # @return [Array]
@@ -4,6 +4,10 @@ require 'twitter_cldr'
4
4
 
5
5
  # rubocop:disable Style/Documentation
6
6
  module I18n
7
+ def self.character_to_group(character)
8
+ I18n.locale == :pl ? character : I18n.transliterate(character)
9
+ end
10
+
7
11
  def self.sort_strings(first, second)
8
12
  string_sorter.compare(first, second)
9
13
  end
@@ -3,7 +3,22 @@
3
3
  # Monkey patches for +Integer+
4
4
  #
5
5
  class Integer
6
- ROMAN_NUMERALS = %w[0 i ii iii iv v vi vii viii ix x xi xii xiii xiv xv xvi xvii xviii xix xx].freeze
6
+
7
+ @roman_numerals = {
8
+ 100 => 'c',
9
+ 90 => 'xc',
10
+ 50 => 'l',
11
+ 40 => 'xl',
12
+ 10 => 'x',
13
+ 9 => 'ix',
14
+ 5 => 'v',
15
+ 4 => 'iv',
16
+ 1 => 'i'
17
+ }
18
+
19
+ class << self
20
+ attr_accessor :roman_numerals
21
+ end
7
22
 
8
23
  # Formats as different types of integers, including roman numerals.
9
24
  #
@@ -14,11 +29,25 @@ class Integer
14
29
  when :arabic
15
30
  to_s
16
31
  when :roman
17
- raise 'Unknown conversion to Roman numerals' if self >= ROMAN_NUMERALS.size
32
+ raise 'Unknown conversion to Roman numerals' if self > self.class.roman_numerals.keys.first
18
33
 
19
- ROMAN_NUMERALS[self]
34
+ to_roman
20
35
  else
21
36
  raise 'Unknown integer format'
22
37
  end
23
38
  end
39
+
40
+ def to_roman
41
+ return 0 if zero?
42
+
43
+ roman = ''
44
+ integer = self
45
+ self.class.roman_numerals.each do |number, letter|
46
+ until integer < number
47
+ roman += letter
48
+ integer -= number
49
+ end
50
+ end
51
+ roman
52
+ end
24
53
  end
@@ -67,11 +67,12 @@ module Nokogiri
67
67
  self[:class]&.split || []
68
68
  end
69
69
 
70
- def previous
71
- prev = previous_element
72
- return nil if prev.nil?
70
+ def preceded_by_text?
71
+ prev = previous_sibling
72
+ while !prev.nil? && prev.blank? do prev = prev.previous_sibling end
73
+ return false if prev.nil?
73
74
 
74
- prev.text? ? prev.previous : prev
75
+ prev.text?
75
76
  end
76
77
 
77
78
  def self.selector_to_css_nodes(selector)
@@ -65,6 +65,9 @@ module Kitchen
65
65
  # Selector for a section
66
66
  # @return [String]
67
67
  attr_accessor :section
68
+ # Selector for an injected exercise
69
+ # @return [String]
70
+ attr_accessor :injected_exercise
68
71
 
69
72
  # Override specific selectors
70
73
  #
@@ -30,6 +30,7 @@ module Kitchen
30
30
  "div[data-type='question-solution']"
31
31
  self.injected_question = "div[data-type='exercise-question']"
32
32
  self.section = 'section'
33
+ self.injected_exercise = "div[data-type='injected-exercise']"
33
34
  end
34
35
 
35
36
  end
@@ -3,5 +3,5 @@
3
3
  # A library for modifying the structure of OpenStax book XML.
4
4
  #
5
5
  module Kitchen
6
- VERSION = '15.0.0'
6
+ VERSION = '18.0.0'
7
7
  end
data/lib/locales/en.yml CHANGED
@@ -28,6 +28,7 @@ en:
28
28
  iframe_link_text: Click to view content
29
29
  handbook_outline_title: Outline
30
30
  context_lead_text: 'Refer to '
31
+ lo_label_text: 'LO '
31
32
  eoc:
32
33
  glossary: Key Terms
33
34
  key-equations: Key Equations
@@ -45,3 +46,18 @@ en:
45
46
  reference: References
46
47
  index:
47
48
  main: Index
49
+ annotation_icons:
50
+ linguistic-icon:
51
+ title: Language lens icon
52
+ culture-icon:
53
+ title: Culture lens icon
54
+ dreaming-icon:
55
+ title: Generating and capturing ideas icon
56
+ visual-icon:
57
+ title: Visual learning style icon
58
+ speech-icon:
59
+ title: Voice to text icon
60
+ auditory-icon:
61
+ title: Auditory learning style icon
62
+ kinesthetic-icon:
63
+ title: Kinesthetic learning style icon
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: openstax_kitchen
3
3
  version: !ruby/object:Gem::Version
4
- version: 15.0.0
4
+ version: 18.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - JP Slavinsky
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-11-05 00:00:00.000000000 Z
11
+ date: 2022-01-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -252,6 +252,7 @@ files:
252
252
  - lib/kitchen/directions/bake_autotitled_exercise/main.rb
253
253
  - lib/kitchen/directions/bake_autotitled_exercise/v1.rb
254
254
  - lib/kitchen/directions/bake_autotitled_exercise/v2.rb
255
+ - lib/kitchen/directions/bake_autotitled_exercise/v3.rb
255
256
  - lib/kitchen/directions/bake_chapter_glossary/main.rb
256
257
  - lib/kitchen/directions/bake_chapter_glossary/v1.rb
257
258
  - lib/kitchen/directions/bake_chapter_introductions/bake_chapter_objectives.rb
@@ -264,6 +265,8 @@ files:
264
265
  - lib/kitchen/directions/bake_chapter_key_equations.rb
265
266
  - lib/kitchen/directions/bake_chapter_references/main.rb
266
267
  - lib/kitchen/directions/bake_chapter_references/v1.rb
268
+ - lib/kitchen/directions/bake_chapter_references/v2.rb
269
+ - lib/kitchen/directions/bake_chapter_references/v3.rb
267
270
  - lib/kitchen/directions/bake_chapter_section_exercises/main.rb
268
271
  - lib/kitchen/directions/bake_chapter_section_exercises/v1.rb
269
272
  - lib/kitchen/directions/bake_chapter_solutions/main.rb
@@ -280,6 +283,8 @@ files:
280
283
  - lib/kitchen/directions/bake_eoc_section_content/remove_section_title.rb
281
284
  - lib/kitchen/directions/bake_equations.rb
282
285
  - lib/kitchen/directions/bake_example.rb
286
+ - lib/kitchen/directions/bake_exercise_prefixes/main.rb
287
+ - lib/kitchen/directions/bake_exercise_prefixes/v1.rb
283
288
  - lib/kitchen/directions/bake_figure.rb
284
289
  - lib/kitchen/directions/bake_first_elements.rb
285
290
  - lib/kitchen/directions/bake_folio.rb
@@ -296,13 +301,14 @@ files:
296
301
  - lib/kitchen/directions/bake_index/main.rb
297
302
  - lib/kitchen/directions/bake_index/v1.rb
298
303
  - lib/kitchen/directions/bake_index/v1.xhtml.erb
299
- - lib/kitchen/directions/bake_injected_exercise.rb
300
304
  - lib/kitchen/directions/bake_injected_exercise/add_injected_exercise_id.rb
305
+ - lib/kitchen/directions/bake_injected_exercise/bake_injected_exercise.rb
301
306
  - lib/kitchen/directions/bake_injected_exercise/bake_injected_exercise_question.rb
302
307
  - lib/kitchen/directions/bake_inline_lists.rb
303
308
  - lib/kitchen/directions/bake_learning_objectives.rb
304
309
  - lib/kitchen/directions/bake_link_placeholders.rb
305
310
  - lib/kitchen/directions/bake_lists_with_para.rb
311
+ - lib/kitchen/directions/bake_lo_link_labels.rb
306
312
  - lib/kitchen/directions/bake_math_in_paragraph.rb
307
313
  - lib/kitchen/directions/bake_non_introduction_pages.rb
308
314
  - lib/kitchen/directions/bake_notes/bake_autotitled_notes.rb
@@ -326,6 +332,7 @@ files:
326
332
  - lib/kitchen/directions/bake_references/v1.rb
327
333
  - lib/kitchen/directions/bake_references/v2.rb
328
334
  - lib/kitchen/directions/bake_references/v3.rb
335
+ - lib/kitchen/directions/bake_screenreader_spans.rb
329
336
  - lib/kitchen/directions/bake_stepwise.rb
330
337
  - lib/kitchen/directions/bake_suggested_reading.rb
331
338
  - lib/kitchen/directions/bake_toc.rb
@@ -373,6 +380,8 @@ files:
373
380
  - lib/kitchen/figure_element_enumerator.rb
374
381
  - lib/kitchen/i18n_string.rb
375
382
  - lib/kitchen/id_tracker.rb
383
+ - lib/kitchen/injected_exercise_element.rb
384
+ - lib/kitchen/injected_exercise_element_enumerator.rb
376
385
  - lib/kitchen/injected_question_element.rb
377
386
  - lib/kitchen/injected_question_element_enumerator.rb
378
387
  - lib/kitchen/metadata_element.rb