openstax_kitchen 11.1.0 → 12.2.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 (39) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +42 -0
  3. data/Gemfile.lock +2 -2
  4. data/lib/kitchen/chapter_element.rb +7 -0
  5. data/lib/kitchen/directions/bake_accessibility_fixes.rb +11 -0
  6. data/lib/kitchen/directions/bake_autotitled_exercise/main.rb +11 -0
  7. data/lib/kitchen/directions/bake_autotitled_exercise/v1.rb +28 -0
  8. data/lib/kitchen/directions/bake_chapter_introductions/bake_chapter_objectives.rb +2 -0
  9. data/lib/kitchen/directions/bake_chapter_introductions/v1.rb +8 -48
  10. data/lib/kitchen/directions/bake_chapter_references/v1.rb +2 -0
  11. data/lib/kitchen/directions/bake_eoc_section_content/remove_section_title.rb +2 -2
  12. data/lib/kitchen/directions/bake_example.rb +3 -3
  13. data/lib/kitchen/directions/bake_figure.rb +3 -2
  14. data/lib/kitchen/directions/bake_footnotes/v1.rb +5 -1
  15. data/lib/kitchen/directions/bake_free_response/v1.rb +3 -1
  16. data/lib/kitchen/directions/bake_injected_exercise/add_injected_exercise_id.rb +16 -0
  17. data/lib/kitchen/directions/{bake_injected_exercise_question.rb → bake_injected_exercise/bake_injected_exercise_question.rb} +0 -0
  18. data/lib/kitchen/directions/{bake_page_abstracts.rb → bake_learning_objectives.rb} +10 -5
  19. data/lib/kitchen/directions/bake_notes/bake_autotitled_notes.rb +36 -4
  20. data/lib/kitchen/directions/bake_notes/bake_numbered_notes/v3.rb +5 -0
  21. data/lib/kitchen/directions/bake_numbered_table/bake_table_body.rb +3 -0
  22. data/lib/kitchen/directions/bake_numbered_table/v1.rb +2 -0
  23. data/lib/kitchen/directions/bake_numbered_table/v2.rb +2 -0
  24. data/lib/kitchen/directions/bake_references/main.rb +3 -2
  25. data/lib/kitchen/directions/bake_references/v1.rb +8 -3
  26. data/lib/kitchen/directions/bake_stepwise.rb +1 -1
  27. data/lib/kitchen/directions/bake_unnumbered_tables.rb +1 -0
  28. data/lib/kitchen/directions/move_solutions_to_answer_key/v1.rb +3 -1
  29. data/lib/kitchen/element_base.rb +10 -18
  30. data/lib/kitchen/element_enumerator_base.rb +17 -0
  31. data/lib/kitchen/example_element.rb +2 -1
  32. data/lib/kitchen/exercise_element.rb +42 -0
  33. data/lib/kitchen/figure_element.rb +1 -1
  34. data/lib/kitchen/metadata_element.rb +6 -1
  35. data/lib/kitchen/note_element.rb +19 -5
  36. data/lib/kitchen/table_element.rb +8 -0
  37. data/lib/kitchen/version.rb +1 -1
  38. data/lib/locales/en.yml +1 -0
  39. metadata +8 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4340461612470e8ddff3976af6bf342821a18f1e01c70d5c6d4dcbf08636ac24
4
- data.tar.gz: 293967ff3cf8af70f6d9d5f5d20866c77608af41c279590b3abb39966813f048
3
+ metadata.gz: f36305ee23acfe22fc13a9c06ee76f69243f1cd2cba33fe86c28c8c90b5eb36d
4
+ data.tar.gz: fd501ef342ff2c7334455cc214139581dadf1619fde7841a29650b4fc10acd79
5
5
  SHA512:
6
- metadata.gz: 70eedaaa535e97066b5b5b0d80e9c8916ffb6de5ecdf063f7a58d2c984253771742832278e53f5ce8bea3f0f3c7cbdd97da058258e5ace42dda0da372243ff2d
7
- data.tar.gz: 72001901b5c82c46bd835521d9a815d178cc6bcea04502fd6b9b10bf36309ed42028b92714fd003e1a0042c60ce9eb4ab505dc627d8dd763f9c6892ff1fee7ea
6
+ metadata.gz: e3b939d8a9851db4940213aae3350f720369eb794b8551627d2a44565af36aeda5ad701b62c2c1ec98fb7dcfabafa8bd2e4531642bf40dd777a635c0117560b6
7
+ data.tar.gz: 5ec8d3e59faa619a3b23a84ce74514c96b6d360dc1b46d233cd1566b63815a5a7edaa73980f102b4c6656c603c43ea349fcc2eac99149cbafb8fa91d8f0e2fdc
data/CHANGELOG.md CHANGED
@@ -6,6 +6,48 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [12.2.0] - 2021-10-1
10
+
11
+ * Add `context_lead_text` to translations (minor)
12
+ * Make `ElementBase#search_with` callable from an `ElementEnumerator` (minor)
13
+ * Support top-titled tables in `BakeUnnumberedTables` (minor)
14
+ * Stop `NoteElement#title` from breaking for empty notes (patch)
15
+ * Add text heavy tables to `BakeTableBody` (minor)
16
+ * Modify `BakeAutotitledNotes` to bake unnumbered exercises with solution (minor)
17
+ * Create `AddInjectedExerciseId` to separate creating ids from `BakeInjectedExerciseQuestion` (minor)
18
+ * Rework `AddInjectedExerciseId` to use loop inside module (minor)
19
+
20
+ ## [12.1.0] - 2021-09-24
21
+
22
+ * Fix `BakeExample#titles_to_rename` to exclude exercise titles (patch)
23
+ * Modify `BakeFigure` to bake unnumbered figures with caption (minor)
24
+ * Fix `NoteElement#title` to be more specific about finding the title (patch)
25
+ * Adds `data-type="slug"` to `metadata_lement` `children_to_keep` method, updates spec helper `metadata_element` and related spec files(minor)
26
+
27
+ ## [12.0.0] - 2021-09-21
28
+
29
+ * Fixes `BakeStepwise` to skip nested lists (patch)
30
+ * Adds an optional selector to `RemoveSectionTitles` (minor)
31
+ * Patches `BakeFreeResponse` to only delete the first h3, not all h3s (patch)
32
+ * Lets `BakeExample` not count titles in lists as commentary titles (minor)
33
+ * Renames `BakePageAbstracts` to `BakeLearningObjectives` and adds optional parameter for titles in `v2` (major)
34
+ * Gets rid of extraneous titles in `BakeAutoTitledNotes` when subtitles are off (minor)
35
+ * Adds `BakeAutotitledExercise` direction and the option to `bake_unclassified_exercises` within `BakeAutotitledNotes`
36
+ * Adds optional numbering for `BakeReferences.v1` (minor)
37
+ * Patches`BakeNumberedNotes.v3` to suppress solutions outside examples when suppress_solutions is true (minor)
38
+
39
+ ## [11.2.0] - 2021-09-10
40
+
41
+ * Adds `BakeAccessibilityFixes` direction for (minor)
42
+ * Remove deprecation warning from `BakeChapterIntroductions.v1` and adapted to be used like `.v2` (minor)
43
+ * Small class fix for `BakeFootnotes.v1` (patch)
44
+ * Fix `BakeNumberedNotes` to find related example better (minor)
45
+ * Small fix for parameter in `bake_note` definition (minor)
46
+ * Small fixes to return when no elements are found and not add an empty wrapper in `BakeChapterReferences` and
47
+ `BakeFreeResponse` (minor)
48
+ * Adding class `os-timeline-table-container` to numbered tables when required (minor)
49
+ * Fix `BakeExample` to catch the multiple solutions to one exercise (patch)
50
+
9
51
  ## [11.1.0] - 2021-08-30
10
52
 
11
53
  * Update injected questions to synthesize ids during baking (minor)
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- openstax_kitchen (11.1.0)
4
+ openstax_kitchen (12.2.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)
14
+ activesupport (6.1.4.1)
15
15
  concurrent-ruby (~> 1.0, >= 1.0.2)
16
16
  i18n (>= 1.6, < 2)
17
17
  minitest (>= 5.1)
@@ -68,5 +68,12 @@ module Kitchen
68
68
  search('div[data-type="abstract"]')
69
69
  end
70
70
 
71
+ # Returns an enumerator for the learning objectives
72
+ #
73
+ # @return [ElementEnumerator]
74
+ #
75
+ def learning_objectives
76
+ search('section.learning-objectives')
77
+ end
71
78
  end
72
79
  end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kitchen
4
+ module Directions
5
+ module BakeAccessibilityFixes
6
+ def self.v1(section:)
7
+ section.search('ol[data-number-style="lower-alpha"]').each { |ol| ol['type'] = 'a' }
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kitchen
4
+ module Directions
5
+ module BakeAutotitledExercise
6
+ def self.v1(exercise:, number: nil)
7
+ V1.new.bake(exercise: exercise, number: number)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kitchen::Directions::BakeAutotitledExercise
4
+ class V1
5
+ def bake(exercise:, number:)
6
+ exercise.add_class('unnumbered') unless number
7
+
8
+ # bake problem
9
+ exercise.problem.wrap_children('div', class: 'os-problem-container')
10
+ exercise.problem.prepend(child:
11
+ <<~HTML
12
+ <h4 class="exercise-title" data-type="title">#{exercise.autogenerated_title}</h4>
13
+ HTML
14
+ )
15
+ return unless exercise.solution
16
+
17
+ # bake solution in place
18
+ exercise.solution.wrap_children('div', class: 'os-solution-container')
19
+ exercise.solution.prepend(child:
20
+ <<~HTML
21
+ <h4 class="solution-title" data-type="title">
22
+ <span class="os-text">#{I18n.t(:"exercises.solution")}</span>
23
+ </h4>
24
+ HTML
25
+ )
26
+ end
27
+ end
28
+ end
@@ -18,6 +18,8 @@ module Kitchen::Directions::BakeChapterIntroductions
18
18
  def bake_as_note(chapter:)
19
19
  chapter_objectives_note = chapter.notes('$.chapter-objectives').first
20
20
 
21
+ return unless chapter_objectives_note.present?
22
+
21
23
  # trash existing title
22
24
  chapter_objectives_note.titles.first&.trash
23
25
  Kitchen::Directions::BakeAutotitledNotes.v1(
@@ -3,54 +3,14 @@
3
3
  module Kitchen::Directions::BakeChapterIntroductions
4
4
  class V1
5
5
  def bake(book:)
6
- # <b>DEPRECATED:</b> Please use <tt>v2</tt> instead.
7
- warn '[DEPRECATION] `BakeChapterIntroductions.v1` is deprecated. Please use `v2` instead.'
8
-
9
- book.chapters.each do |chapter|
10
- introduction_page = chapter.introduction_page
11
-
12
- introduction_page.search("div[data-type='description']").trash
13
- introduction_page.search("div[data-type='abstract']").trash
14
-
15
- title = introduction_page.title.cut
16
- title.name = 'h2'
17
- Kitchen::Directions::MoveTitleTextIntoSpan.v1(title: title)
18
-
19
- intro_content = introduction_page.search(
20
- "> :not([data-type='metadata']):not(.splash):not(.has-splash)"
21
- ).cut
22
-
23
- chapter_objectives_html = chapter.non_introduction_pages.map do |page|
24
- <<~HTML
25
- <div class="os-chapter-objective">
26
- <a class="os-chapter-objective" href="##{page.title[:id]}">
27
- <span class="os-number">#{chapter.count_in(:book)}.#{page.count_in(:chapter)}</span>
28
- <span class="os-divider"> </span>
29
- <span data-type="" itemprop="" class="os-text">#{page.title.children[0].text}</span>
30
- </a>
31
- </div>
32
- HTML
33
- end.join('')
34
-
35
- chapter_outline =
36
- Kitchen::Directions::BakeChapterIntroductions.bake_chapter_outline(
37
- chapter_objectives_html: chapter_objectives_html
38
- )
39
-
40
- introduction_page.append(child:
41
- <<~HTML
42
- <div class="intro-body">
43
- #{chapter_outline}
44
- <div class="intro-text">
45
- #{title.paste}
46
- #{intro_content.paste}
47
- </div>
48
- </div>
49
- HTML
50
- )
51
- end
52
-
53
- Kitchen::Directions::BakeChapterIntroductions.v1_update_selectors(book)
6
+ Kitchen::Directions::BakeChapterIntroductions.v2(
7
+ book: book,
8
+ strategy_options: {
9
+ strategy: :add_objectives,
10
+ bake_chapter_outline: true,
11
+ introduction_order: :v1
12
+ }
13
+ )
54
14
  end
55
15
  end
56
16
  end
@@ -7,6 +7,8 @@ module Kitchen::Directions::BakeChapterReferences
7
7
  bake_page_references(page: page)
8
8
  end
9
9
 
10
+ return if chapter.pages.references.none?
11
+
10
12
  content = chapter.pages.references.cut.paste
11
13
 
12
14
  Kitchen::Directions::EocCompositePageContainer.v1(
@@ -3,8 +3,8 @@
3
3
  module Kitchen
4
4
  module Directions
5
5
  module RemoveSectionTitle
6
- def self.v1(section:)
7
- section.first('[data-type="title"]')&.trash
6
+ def self.v1(section:, selector: '')
7
+ section.first("#{selector}[data-type=\"title\"]")&.trash
8
8
  end
9
9
  end
10
10
  end
@@ -30,9 +30,9 @@ module Kitchen
30
30
  problem.wrap_children(class: 'os-problem-container')
31
31
  end
32
32
 
33
- if (solution = exercise.solution)
33
+ exercise.solutions.each do |solution|
34
34
  solution_number = if numbered_solutions
35
- "<span class=\"os-number\">#{exercise.count_in(:example)}</span>"
35
+ "<span class=\"os-number\">#{solution.count_in(:example)}</span>"
36
36
  else
37
37
  ''
38
38
  end
@@ -53,7 +53,7 @@ module Kitchen
53
53
  next unless commentary.present?
54
54
 
55
55
  commentary_title = commentary.titles.first
56
- next unless commentary_title.present?
56
+ next unless commentary_title.present? && commentary_title.parent['data-type'] != 'list'
57
57
 
58
58
  commentary_title.name = 'h4'
59
59
  commentary_title['data-type'] = 'commentary-title'
@@ -4,10 +4,11 @@ module Kitchen
4
4
  module Directions
5
5
  module BakeFigure
6
6
  def self.v1(figure:, number:, cases: false)
7
- return if figure.has_class?('unnumbered') && !figure.has_class?('splash')
7
+ return if figure.has_class?('unnumbered') && !figure.has_class?('splash') && !figure.caption
8
8
 
9
9
  figure.wrap(%(<div class="os-figure#{' has-splash' if figure.has_class?('splash')}">))
10
- if figure.has_class?('unnumbered') && figure.has_class?('splash')
10
+
11
+ if figure.has_class?('unnumbered') && (figure.caption || figure.has_class?('splash'))
11
12
  caption = figure.caption&.cut
12
13
  figure.append(sibling:
13
14
  <<~HTML
@@ -28,7 +28,11 @@ module Kitchen::Directions::BakeFootnotes
28
28
  anchor.replace_children(with: footnote_number)
29
29
  aside_id = anchor[:href][1..-1]
30
30
  aside_id_to_footnote_number[aside_id] = footnote_number
31
- anchor.parent.add_class('has-noteref') if anchor.parent.name == 'p'
31
+ if anchor.parent.name == 'p'
32
+ anchor.parent.add_class('has-noteref')
33
+ elsif anchor.parent.name == 'em' && anchor.parent.parent.name == 'p'
34
+ anchor.parent.parent.add_class('has-noteref')
35
+ end
32
36
  end
33
37
 
34
38
  container.search('aside').each do |aside|
@@ -12,7 +12,7 @@ module Kitchen::Directions::BakeFreeResponse
12
12
  free_response_questions = page.free_response
13
13
  next if free_response_questions.none?
14
14
 
15
- free_response_questions.search('h3').trash
15
+ free_response_questions.search('h3')&.first&.trash
16
16
  title = Kitchen::Directions::EocSectionTitleLinkSnippet.v1(page: page)
17
17
  free_response_questions.each do |free_response_question|
18
18
  free_response_question.prepend(child: title)
@@ -20,6 +20,8 @@ module Kitchen::Directions::BakeFreeResponse
20
20
  end
21
21
  end
22
22
 
23
+ return if @free_response_clipboard.none?
24
+
23
25
  append_to_element = append_to || chapter
24
26
  @title_tag = append_to ? 'h3' : 'h2'
25
27
 
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kitchen
4
+ module Directions
5
+ # Ids should be added before exercises are moved to EOC,
6
+ # since they're using part of the ancestor page id.
7
+ #
8
+ # In some books exercises are numbered after moving.
9
+ # That's why this step has to be separated from BakeInjectedExerciseQuestion
10
+ module AddInjectedExerciseId
11
+ def self.v1(book:)
12
+ book.pages.injected_questions.each(&:id)
13
+ end
14
+ end
15
+ end
16
+ end
@@ -2,17 +2,22 @@
2
2
 
3
3
  module Kitchen
4
4
  module Directions
5
- # Adds learning objectives header to abstracts
6
- module BakePageAbstracts
5
+ module BakeLearningObjectives
7
6
  def self.v1(chapter:)
8
7
  chapter.abstracts.each do |abstract|
9
8
  abstract.prepend(child: "<h3 data-type='title'>#{I18n.t(:learning_objectives)}</h3>")
10
9
  end
11
10
  end
12
11
 
13
- def self.v2(chapter:)
14
- chapter.abstracts.each do |abstract|
15
- abstract.prepend(child: "<h3 data-type='title'>#{I18n.t(:learning_objectives)}</h3>")
12
+ def self.v2(chapter:, add_title: true)
13
+ learning_objectives =
14
+ chapter.abstracts.any? ? chapter.abstracts : chapter.learning_objectives
15
+
16
+ learning_objectives.each do |abstract|
17
+ if add_title
18
+ abstract.prepend(child: "<h3 data-type='title'>#{I18n.t(:learning_objectives)}</h3>")
19
+ end
20
+
16
21
  ul = abstract.first!('ul')
17
22
  ul.add_class('os-abstract')
18
23
  ul.search('li').each_with_index do |li, index|
@@ -3,19 +3,24 @@
3
3
  module Kitchen
4
4
  module Directions
5
5
  module BakeAutotitledNotes
6
- def self.v1(book:, classes:, bake_subtitle: true, cases: false)
6
+ def self.v1(book:, classes:, bake_subtitle: true, cases: false, bake_exercises: false)
7
7
  book.notes.each do |note|
8
8
  next unless (note.classes & classes).any?
9
9
 
10
- bake_note(note: note, bake_subtitle: bake_subtitle, cases: cases)
10
+ bake_note(
11
+ note: note, bake_subtitle: bake_subtitle, cases: cases, bake_exercises: bake_exercises)
11
12
  end
12
13
  end
13
14
 
14
- def self.bake_note(note:, bake_subtitle:, cases: false)
15
+ def self.bake_note(note:, bake_subtitle:, cases:, bake_exercises:)
15
16
  Kitchen::Directions::BakeIframes.v1(outer_element: note)
16
17
  note.wrap_children(class: 'os-note-body')
17
18
 
18
- BakeNoteSubtitle.v1(note: note, cases: cases) if bake_subtitle
19
+ if bake_subtitle
20
+ BakeNoteSubtitle.v1(note: note, cases: cases)
21
+ else
22
+ note.title&.trash
23
+ end
19
24
 
20
25
  note.prepend(child:
21
26
  <<~HTML
@@ -24,6 +29,33 @@ module Kitchen
24
29
  </h3>
25
30
  HTML
26
31
  )
32
+
33
+ bake_unclassified_exercises(note: note) if bake_exercises
34
+ end
35
+
36
+ def self.bake_unclassified_exercises(note:)
37
+ note.exercises.each do |exercise|
38
+ exercise.problem.wrap_children('div', class: 'os-problem-container')
39
+
40
+ unless exercise.has_class?('unnumbered')
41
+ exercise.problem.prepend(child:
42
+ <<~HTML
43
+ <span class="os-title-label">#{I18n.t(:"exercises.exercise")} </span>
44
+ <span class="os-number">#{exercise.count_in(:note)}</span>
45
+ HTML
46
+ )
47
+ end
48
+
49
+ next unless exercise.solution
50
+
51
+ exercise.solution.wrap_children('div', class: 'os-solution-container')
52
+
53
+ exercise.solution.prepend(child:
54
+ <<~HTML
55
+ <span class="os-title-label">#{I18n.t(:"exercises.solution")}</span>
56
+ HTML
57
+ )
58
+ end
27
59
  end
28
60
  end
29
61
  end
@@ -9,6 +9,9 @@ module Kitchen::Directions
9
9
  book.chapters.pages.notes("$.#{klass}").each do |note|
10
10
  note.wrap_children(class: 'os-note-body')
11
11
  previous_example = note.previous
12
+ until previous_example.nil? || previous_example[:'data-type'] == 'example'
13
+ previous_example = previous_example.previous
14
+ end
12
15
  os_number = previous_example&.first('.os-number')&.children&.to_s
13
16
 
14
17
  note.prepend(child:
@@ -29,6 +32,8 @@ module Kitchen::Directions
29
32
  note.injected_questions.each do |question|
30
33
  BakeNumberedNotes.bake_note_injected_question(note: note, question: question)
31
34
  end
35
+
36
+ note.search("div[data-type='solution']").each&.trash if suppress_solution
32
37
  end
33
38
  end
34
39
  end
@@ -62,6 +62,9 @@ module Kitchen
62
62
  elsif table.column_header?
63
63
  custom_table = CustomBody.new(table: table, klass: 'column-header')
64
64
  custom_table.modify_body(has_fake_title: false)
65
+ elsif table.text_heavy?
66
+ custom_table = CustomBody.new(table: table, klass: 'text-heavy')
67
+ custom_table.modify_body(has_fake_title: false)
65
68
  end
66
69
  end
67
70
  end
@@ -38,6 +38,8 @@ module Kitchen::Directions::BakeNumberedTable
38
38
  </div>
39
39
  HTML
40
40
  )
41
+
42
+ table.parent.add_class('os-timeline-table-container') if table.has_class?('timeline-table')
41
43
  end
42
44
  end
43
45
  end
@@ -26,6 +26,8 @@ module Kitchen::Directions::BakeNumberedTable
26
26
  </div>
27
27
  HTML
28
28
  )
29
+
30
+ table.parent.add_class('os-timeline-table-container') if table.has_class?('timeline-table')
29
31
  end
30
32
  end
31
33
  end
@@ -5,10 +5,11 @@ module Kitchen
5
5
  # Bake directions for EOB references
6
6
  #
7
7
  module BakeReferences
8
- def self.v1(book:, metadata_source:)
8
+ def self.v1(book:, metadata_source:, numbered_title: false)
9
9
  V1.new.bake(
10
10
  book: book,
11
- metadata_source: metadata_source
11
+ metadata_source: metadata_source,
12
+ numbered_title: numbered_title
12
13
  )
13
14
  end
14
15
 
@@ -4,7 +4,7 @@ module Kitchen::Directions::BakeReferences
4
4
  class V1
5
5
  renderable
6
6
 
7
- def bake(book:, metadata_source:)
7
+ def bake(book:, metadata_source:, numbered_title:)
8
8
  @metadata = metadata_source.children_to_keep.copy
9
9
  @klass = 'reference'
10
10
  @uuid_prefix = '.'
@@ -28,12 +28,17 @@ module Kitchen::Directions::BakeReferences
28
28
  end
29
29
 
30
30
  chapter_references = chapter.pages.references.cut
31
- chapter_title_no_num = chapter.title.search('.os-text')
31
+
32
+ chapter_title = if numbered_title
33
+ chapter.title.search('.os-number, .os-divider, .os-text')
34
+ else
35
+ chapter.title.search('.os-text')
36
+ end
32
37
 
33
38
  chapter.append(child:
34
39
  <<~HTML
35
40
  <div class="os-chapter-area">
36
- <h2 data-type="document-title">#{chapter_title_no_num}</h2>
41
+ <h2 data-type="document-title">#{chapter_title}</h2>
37
42
  #{chapter_references.paste}
38
43
  </div>
39
44
  HTML
@@ -8,7 +8,7 @@ module Kitchen
8
8
  ol.remove_class('stepwise')
9
9
  ol.add_class('os-stepwise')
10
10
 
11
- ol.search('li').each_with_index do |li, ii|
11
+ ol.search('> li').each_with_index do |li, ii|
12
12
  li.wrap_children('span', class: 'os-stepwise-content')
13
13
  li.prepend(child:
14
14
  <<~HTML
@@ -9,6 +9,7 @@ module Kitchen
9
9
  table.remove_attribute('summary')
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
+ table.parent.add_class('os-top-titled-container') if table.top_titled?
12
13
  end
13
14
  end
14
15
  end
@@ -38,7 +38,9 @@ module Kitchen::Directions::MoveSolutionsToAnswerKey
38
38
  </div>
39
39
  HTML
40
40
  )
41
- strategy.bake(chapter: chapter, append_to: append_to.first("[data-uuid-key='#{uuid_key}']"))
41
+ strategy.bake(
42
+ chapter: chapter, append_to: append_to.first("div[data-uuid-key='#{uuid_key}']")
43
+ )
42
44
  end
43
45
  # rubocop:enable Metrics/ParameterLists
44
46
  end
@@ -211,6 +211,14 @@ module Kitchen
211
211
  self[:id] = value
212
212
  end
213
213
 
214
+ # Returns the element's data-type
215
+ #
216
+ # @return [String]
217
+ #
218
+ def data_type
219
+ self[:'data-type']
220
+ end
221
+
214
222
  # A way to set values and chain them
215
223
  #
216
224
  # @param property [String, Symbol] the name of the property to set
@@ -419,23 +427,6 @@ module Kitchen
419
427
  )
420
428
  end
421
429
 
422
- # Searches for elements handled by a list of enumerator classes. All element that
423
- # matches one of those enumerator classes are iterated over.
424
- #
425
- # @param enumerator_classes [Array<ElementEnumeratorBase>]
426
- # @return [TypeCastingElementEnumerator]
427
- #
428
- def search_with(*enumerator_classes)
429
- block_error_if(block_given?)
430
- raise 'must supply at least one enumerator class' if enumerator_classes.empty?
431
-
432
- factory = enumerator_classes[0].factory
433
- enumerator_classes[1..-1].each do |enumerator_class|
434
- factory = factory.or_with(enumerator_class.factory)
435
- end
436
- factory.build_within(self)
437
- end
438
-
439
430
  # Removes the element from its parent and places it on the specified clipboard
440
431
  #
441
432
  # @param to [Symbol, String, Clipboard, nil] the name of the clipboard (or a Clipboard
@@ -762,7 +753,8 @@ module Kitchen
762
753
  # Returns a pages enumerator
763
754
  def_delegators :as_enumerator, :pages, :chapters, :terms, :figures, :notes, :tables, :examples,
764
755
  :metadatas, :non_introduction_pages, :units, :titles, :exercises, :references,
765
- :composite_pages, :composite_chapters, :solutions, :injected_questions
756
+ :composite_pages, :composite_chapters, :solutions, :injected_questions,
757
+ :search_with
766
758
 
767
759
  # Returns this element as an enumerator (over only one element, itself)
768
760
  #
@@ -327,6 +327,23 @@ module Kitchen
327
327
  chain_to(ElementEnumerator, css_or_xpath: css_or_xpath, only: only, except: except)
328
328
  end
329
329
 
330
+ # Searches for elements handled by a list of enumerator classes. All element that
331
+ # matches one of those enumerator classes are iterated over.
332
+ #
333
+ # @param enumerator_classes [Array<ElementEnumeratorBase>]
334
+ # @return [TypeCastingElementEnumerator]
335
+ #
336
+ def search_with(*enumerator_classes)
337
+ block_error_if(block_given?)
338
+ raise 'must supply at least one enumerator class' if enumerator_classes.empty?
339
+
340
+ factory = enumerator_classes[0].factory
341
+ enumerator_classes[1..-1].each do |enumerator_class|
342
+ factory = factory.or_with(enumerator_class.factory)
343
+ end
344
+ factory.build_within(self)
345
+ end
346
+
330
347
  # Returns an enumerator that iterates through elements within the scope of this enumerator
331
348
  #
332
349
  # @param enumerator_class [ElementEnumeratorBase] the enumerator to use for the iteration
@@ -33,7 +33,8 @@ module Kitchen
33
33
  title.parent.has_class?('os-caption-container') || \
34
34
  title.parent.has_class?('os-caption') || \
35
35
  title.parent.name == 'caption' || \
36
- title.parent[:'data-type'] == 'note'
36
+ title.parent.data_type == 'note' || \
37
+ title.parent.data_type == 'exercise'
37
38
  end
38
39
  )
39
40
  end
@@ -39,6 +39,14 @@ module Kitchen
39
39
  first("div[data-type='solution']")
40
40
  end
41
41
 
42
+ # Returns the enumerator for solutions
43
+ #
44
+ # @return [ElementEnumerator]
45
+ #
46
+ def solutions
47
+ search("div[data-type='solution']")
48
+ end
49
+
42
50
  # Returns whether the exercise has been baked
43
51
  #
44
52
  # @return [Boolean]
@@ -46,5 +54,39 @@ module Kitchen
46
54
  def baked?
47
55
  search('div.os-problem-container').any?
48
56
  end
57
+
58
+ # Returns true if the exercise's title is autogenerated
59
+ #
60
+ # @return [Boolean]
61
+ #
62
+ def indicates_autogenerated_title?
63
+ detected_exercise_title_key != 0 && detected_exercise_title_key.present?
64
+ end
65
+
66
+ # Get the autogenerated title for this note
67
+ #
68
+ # @return [String]
69
+ #
70
+ def autogenerated_title
71
+ if indicates_autogenerated_title?
72
+ I18n.t(:"exercises.#{detected_exercise_title_key}")
73
+ else
74
+ "unknown title for exercise with classes #{classes}"
75
+ end
76
+ end
77
+
78
+ def detected_exercise_title_key
79
+ @detected_exercise_title_key ||= begin
80
+ return 0 if classes.empty? || !I18n.t('.').key?(:exercises)
81
+
82
+ possible_keys = I18n.t(:exercises).keys.map(&:to_s)
83
+ keys = possible_keys & classes
84
+
85
+ raise("too many translation keys: #{keys.join(', ')}") if keys.many?
86
+ return 0 if keys.empty?
87
+
88
+ keys.first
89
+ end
90
+ end
49
91
  end
50
92
  end
@@ -53,7 +53,7 @@ module Kitchen
53
53
  # @return [Boolean]
54
54
  #
55
55
  def figure_to_bake?
56
- return false if subfigure? || (has_class?('unnumbered') && !has_class?('splash'))
56
+ return false if subfigure? || (has_class?('unnumbered') && !has_class?('splash') && !caption)
57
57
 
58
58
  true
59
59
  end
@@ -27,7 +27,12 @@ module Kitchen
27
27
  # @return [ElementEnumerator]
28
28
  #
29
29
  def children_to_keep
30
- search(%w(span[data-type='revised'] div.authors div.publishers div.print-style div.permissions
30
+ search(%w(span[data-type='revised']
31
+ span[data-type='slug']
32
+ div.authors
33
+ div.publishers
34
+ div.print-style
35
+ div.permissions
31
36
  div[data-type='subject']))
32
37
  end
33
38
  end
@@ -29,16 +29,30 @@ module Kitchen
29
29
  #
30
30
  def title
31
31
  block_error_if(block_given?)
32
- note_body = first('div.os-note-body')
33
- first_child = note_body ? note_body.element_children[0] : element_children[0]
32
+ # An element with data-type="title" is ambiguous; it is only the note's title if:
33
+ # 1. it is the note body's first child
34
+ # 2. it is the first child's first child and the first child is a paragraph
35
+ first_child = first_note_body_child
36
+ return unless first_child
37
+
38
+ first_grandchild = get_first_grandchild_for_title(first_child)
34
39
 
35
- if first_child[:'data-type'] == 'title'
40
+ if first_child.data_type == 'title'
36
41
  first_child
37
- elsif first_child&.element_children&.[](0)&.[](:'data-type') == 'title'
38
- first_child.element_children[0]
42
+ elsif first_grandchild&.data_type == 'title'
43
+ first_grandchild
39
44
  end
40
45
  end
41
46
 
47
+ def first_note_body_child
48
+ note_body = first('div.os-note-body')
49
+ note_body ? note_body.element_children[0] : element_children[0]
50
+ end
51
+
52
+ def get_first_grandchild_for_title(first_child)
53
+ first_child&.element_children&.[](0) if first_child.name == 'p'
54
+ end
55
+
42
56
  # Returns true if the note's title is autogenerated
43
57
  #
44
58
  # @return [Boolean]
@@ -97,6 +97,14 @@ module Kitchen
97
97
  has_class?('column-header')
98
98
  end
99
99
 
100
+ # Returns true if the table is text heavy
101
+ #
102
+ # @return [Boolean]
103
+ #
104
+ def text_heavy?
105
+ has_class?('text-heavy')
106
+ end
107
+
100
108
  # Returns an element for the table caption, if present
101
109
  #
102
110
  # @return [Element, nil]
@@ -3,5 +3,5 @@
3
3
  # A library for modifying the structure of OpenStax book XML.
4
4
  #
5
5
  module Kitchen
6
- VERSION = '11.1.0'
6
+ VERSION = '12.2.0'
7
7
  end
data/lib/locales/en.yml CHANGED
@@ -27,6 +27,7 @@ en:
27
27
  section_exercises: ! 'Section %{number} Exercises'
28
28
  iframe_link_text: Click to view content
29
29
  handbook_outline_title: Outline
30
+ context_lead_text: 'Refer to '
30
31
  eoc:
31
32
  glossary: Key Terms
32
33
  key-equations: Key Equations
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: 11.1.0
4
+ version: 12.2.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-08-30 00:00:00.000000000 Z
11
+ date: 2021-10-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -243,9 +243,12 @@ files:
243
243
  - lib/kitchen/counter.rb
244
244
  - lib/kitchen/debug/print_recipe_error.rb
245
245
  - lib/kitchen/directions/.rubocop.yml
246
+ - lib/kitchen/directions/bake_accessibility_fixes.rb
246
247
  - lib/kitchen/directions/bake_annotation_classes/main.rb
247
248
  - lib/kitchen/directions/bake_annotation_classes/v1.rb
248
249
  - lib/kitchen/directions/bake_appendix.rb
250
+ - lib/kitchen/directions/bake_autotitled_exercise/main.rb
251
+ - lib/kitchen/directions/bake_autotitled_exercise/v1.rb
249
252
  - lib/kitchen/directions/bake_chapter_glossary/main.rb
250
253
  - lib/kitchen/directions/bake_chapter_glossary/v1.rb
251
254
  - lib/kitchen/directions/bake_chapter_introductions/bake_chapter_objectives.rb
@@ -291,8 +294,10 @@ files:
291
294
  - lib/kitchen/directions/bake_index/v1.rb
292
295
  - lib/kitchen/directions/bake_index/v1.xhtml.erb
293
296
  - lib/kitchen/directions/bake_injected_exercise.rb
294
- - lib/kitchen/directions/bake_injected_exercise_question.rb
297
+ - lib/kitchen/directions/bake_injected_exercise/add_injected_exercise_id.rb
298
+ - lib/kitchen/directions/bake_injected_exercise/bake_injected_exercise_question.rb
295
299
  - lib/kitchen/directions/bake_inline_lists.rb
300
+ - lib/kitchen/directions/bake_learning_objectives.rb
296
301
  - lib/kitchen/directions/bake_link_placeholders.rb
297
302
  - lib/kitchen/directions/bake_lists_with_para.rb
298
303
  - lib/kitchen/directions/bake_math_in_paragraph.rb
@@ -310,7 +315,6 @@ files:
310
315
  - lib/kitchen/directions/bake_numbered_table/main.rb
311
316
  - lib/kitchen/directions/bake_numbered_table/v1.rb
312
317
  - lib/kitchen/directions/bake_numbered_table/v2.rb
313
- - lib/kitchen/directions/bake_page_abstracts.rb
314
318
  - lib/kitchen/directions/bake_preface/main.rb
315
319
  - lib/kitchen/directions/bake_preface/v1.rb
316
320
  - lib/kitchen/directions/bake_references/main.rb