openstax_kitchen 11.1.0 → 12.2.0

Sign up to get free protection for your applications and to get access to all the features.
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