openstax_kitchen 3.0.0 → 3.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +21 -3
  3. data/Gemfile.lock +1 -1
  4. data/README.md +7 -0
  5. data/lib/kitchen/directions/bake_book_answer_key/eob_solutions_container.xhtml.erb +9 -0
  6. data/lib/kitchen/directions/bake_book_answer_key/main.rb +11 -0
  7. data/lib/kitchen/directions/bake_book_answer_key/v1.rb +13 -0
  8. data/lib/kitchen/directions/bake_chapter_answer_key/main.rb +14 -0
  9. data/lib/kitchen/directions/bake_chapter_answer_key/strategies/calculus.rb +41 -0
  10. data/lib/kitchen/directions/bake_chapter_answer_key/strategies/uphysics.rb +61 -0
  11. data/lib/kitchen/directions/bake_chapter_answer_key/v1.rb +32 -0
  12. data/lib/kitchen/directions/bake_chapter_glossary.rb +4 -2
  13. data/lib/kitchen/directions/bake_chapter_key_concepts/key_concepts.xhtml.erb +16 -0
  14. data/lib/kitchen/directions/bake_chapter_key_concepts/main.rb +11 -0
  15. data/lib/kitchen/directions/bake_chapter_key_concepts/v1.rb +30 -0
  16. data/lib/kitchen/directions/bake_chapter_key_equations.rb +4 -2
  17. data/lib/kitchen/directions/bake_chapter_review/chapter_review.xhtml.erb +9 -0
  18. data/lib/kitchen/directions/bake_chapter_review/main.rb +11 -0
  19. data/lib/kitchen/directions/bake_chapter_review/v1.rb +13 -0
  20. data/lib/kitchen/directions/bake_chapter_review_exercises/main.rb +15 -0
  21. data/lib/kitchen/directions/bake_chapter_review_exercises/review_exercises.xhtml.erb +10 -0
  22. data/lib/kitchen/directions/bake_chapter_review_exercises/v1.rb +38 -0
  23. data/lib/kitchen/directions/bake_chapter_review_exercises/v2.rb +50 -0
  24. data/lib/kitchen/directions/bake_chapter_section_exercises/main.rb +11 -0
  25. data/lib/kitchen/directions/bake_chapter_section_exercises/v1.rb +28 -0
  26. data/lib/kitchen/directions/bake_chapter_summary.rb +1 -1
  27. data/lib/kitchen/directions/bake_checkpoint.rb +44 -0
  28. data/lib/kitchen/directions/bake_composite_chapters.rb +14 -0
  29. data/lib/kitchen/directions/bake_equations.rb +27 -0
  30. data/lib/kitchen/directions/bake_example.rb +29 -7
  31. data/lib/kitchen/directions/bake_exercises/main.rb +1 -0
  32. data/lib/kitchen/directions/bake_exercises/v1.rb +34 -31
  33. data/lib/kitchen/directions/bake_non_introduction_pages.rb +26 -0
  34. data/lib/kitchen/directions/bake_notes/bake_autotitled_notes.rb +29 -0
  35. data/lib/kitchen/directions/bake_notes/bake_note_subtitle.rb +18 -0
  36. data/lib/kitchen/directions/{bake_notes.rb → bake_notes/bake_notes.rb} +6 -16
  37. data/lib/kitchen/directions/bake_notes/bake_numbered_notes.rb +63 -0
  38. data/lib/kitchen/directions/bake_notes/bake_unclassified_notes.rb +30 -0
  39. data/lib/kitchen/directions/bake_numbered_exercise/main.rb +11 -0
  40. data/lib/kitchen/directions/bake_numbered_exercise/v1.rb +34 -0
  41. data/lib/kitchen/directions/bake_numbered_table/main.rb +2 -2
  42. data/lib/kitchen/directions/bake_numbered_table/v1.rb +17 -3
  43. data/lib/kitchen/directions/bake_page_abstracts.rb +16 -0
  44. data/lib/kitchen/directions/bake_problem_first_elements.rb +16 -0
  45. data/lib/kitchen/directions/bake_stepwise.rb +1 -5
  46. data/lib/kitchen/directions/bake_theorem/main.rb +11 -0
  47. data/lib/kitchen/directions/bake_theorem/v1.rb +28 -0
  48. data/lib/kitchen/directions/bake_toc.rb +6 -0
  49. data/lib/kitchen/directions/eoc_section_title_link_snippet.rb +20 -0
  50. data/lib/kitchen/element_base.rb +40 -1
  51. data/lib/kitchen/element_enumerator_base.rb +87 -8
  52. data/lib/kitchen/exercise_element.rb +45 -0
  53. data/lib/kitchen/exercise_element_enumerator.rb +21 -0
  54. data/lib/kitchen/note_element.rb +17 -16
  55. data/lib/kitchen/page_element.rb +8 -0
  56. data/lib/kitchen/recipe.rb +35 -2
  57. data/lib/kitchen/version.rb +1 -1
  58. data/lib/locales/en.yml +9 -7
  59. data/lib/locales/pl.yml +24 -0
  60. data/lib/openstax_kitchen.rb +1 -1
  61. metadata +39 -3
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kitchen
4
+ module Directions
5
+ module BakeNoteSubtitle
6
+ def self.v1(note:)
7
+ title = note.title&.cut
8
+
9
+ return unless title
10
+
11
+ title.name = 'h4'
12
+ title.add_class('os-subtitle')
13
+ title.wrap_children('span', class: 'os-subtitle-label')
14
+ note.first!('.os-note-body').prepend(child: title.to_s)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -4,14 +4,12 @@ module Kitchen
4
4
  module Directions
5
5
  module BakeNotes
6
6
  def self.v1(book:)
7
- book.notes.each do |note|
7
+ warn 'WARNING! deprecated direction used: BakeNotes'
8
+
9
+ book.notes('$:not(.checkpoint):not(.theorem)').each do |note|
8
10
  title = note.title&.cut
9
11
 
10
- note.replace_children(with:
11
- <<~HTML
12
- <div class="os-note-body">#{note.children}</div>
13
- HTML
14
- )
12
+ note.wrap_children(class: 'os-note-body')
15
13
 
16
14
  if title
17
15
  if note.indicates_autogenerated_title?
@@ -25,20 +23,12 @@ module Kitchen
25
23
 
26
24
  title.name = 'h4'
27
25
  title.add_class('os-subtitle')
28
- title.replace_children(with:
29
- <<~HTML
30
- <span class="os-subtitle-label">#{title.children}</span>
31
- HTML
32
- )
26
+ title.wrap_children('span', class: 'os-subtitle-label')
33
27
  note.first!('.os-note-body').prepend(child: title.raw)
34
28
  else
35
29
  title.name = 'h3'
36
30
  title.add_class('os-title')
37
- title.replace_children(with:
38
- <<~HTML
39
- <span data-type="" id="#{title.id}" class="os-title-label">#{title.children}</span>
40
- HTML
41
- )
31
+ title.wrap_children('span', data_type: '', id: title.id, class: 'os-title-label')
42
32
  title.remove_attribute('id')
43
33
  note.prepend(child: title.raw)
44
34
  end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kitchen
4
+ module Directions
5
+ module BakeNumberedNotes
6
+ def self.v1(book:, classes:)
7
+ classes.each do |klass|
8
+ book.chapters.notes("$.#{klass}").each do |note|
9
+ bake_note(note: note)
10
+ bake_note_exercise(note: note)
11
+ end
12
+ end
13
+ end
14
+
15
+ def self.bake_note(note:)
16
+ note.wrap_children(class: 'os-note-body')
17
+
18
+ chapter_count = note.ancestor(:chapter).count_in(:book)
19
+ note_count = note.count_in(:chapter)
20
+ note.prepend(child:
21
+ <<~HTML
22
+ <div class="os-title">
23
+ <span class="os-title-label">#{note.autogenerated_title}</span>
24
+ <span class="os-number">#{chapter_count}.#{note_count}</span>
25
+ <span class="os-divider"> </span>
26
+ </div>
27
+ HTML
28
+ )
29
+
30
+ return unless note['use-subtitle']
31
+
32
+ BakeNoteSubtitle.v1(note: note)
33
+ end
34
+
35
+ def self.bake_note_exercise(note:)
36
+ exercise = note.exercises.first
37
+ return unless exercise
38
+
39
+ exercise.add_class('unnumbered')
40
+ # bake problem
41
+ exercise.problem.wrap_children('div', class: 'os-problem-container')
42
+ exercise.problem.first('strong')&.trash
43
+ exercise.search('[data-type="commentary"]').each(&:trash)
44
+ solution = exercise.solution
45
+ return unless solution
46
+
47
+ # bake solution in place
48
+ exercise.add_class('os-hasSolution')
49
+ solution[:id] = "#{exercise[:id]}-solution"
50
+ solution_number = note.first('.os-number').text
51
+ solution.replace_children(with:
52
+ <<~HTML
53
+ <a class="os-number" href="##{exercise[:id]}">#{solution_number}</a>
54
+ <span class="os-divider"> </span>
55
+ <div class="os-solution-container">
56
+ #{solution.children}
57
+ </div>
58
+ HTML
59
+ )
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kitchen
4
+ module Directions
5
+ module BakeUnclassifiedNotes
6
+ def self.v1(book:)
7
+ book.notes.each do |note|
8
+ next unless note.classes.empty?
9
+
10
+ bake_note(note: note)
11
+ end
12
+ end
13
+
14
+ def self.bake_note(note:)
15
+ note.wrap_children(class: 'os-note-body')
16
+
17
+ title = note.title&.cut
18
+ return unless title
19
+
20
+ note.prepend(child:
21
+ <<~HTML
22
+ <h3 class="os-title" data-type="title">
23
+ <span class="os-title-label" data-type="" id="#{title[:id]}">#{title.children}</span>
24
+ </h3>
25
+ HTML
26
+ )
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kitchen
4
+ module Directions
5
+ module BakeNumberedExercise
6
+ def self.v1(exercise:, number:)
7
+ V1.new.bake(exercise: exercise, number: number)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kitchen::Directions::BakeNumberedExercise
4
+ class V1
5
+ def bake(exercise:, number:)
6
+ problem = exercise.problem
7
+ solution = exercise.solution
8
+
9
+ problem_number = "<span class='os-number'>#{number}</span>"
10
+
11
+ if solution.present?
12
+ solution.id = "#{exercise.id}-solution"
13
+ exercise.add_class('os-hasSolution')
14
+ problem_number = "<a class='os-number' href='##{exercise.id}-solution'>#{number}</a>"
15
+
16
+ solution.replace_children(with:
17
+ <<~HTML
18
+ <a class='os-number' href='##{exercise.id}'>#{number}</a>
19
+ <span class='os-divider'>. </span>
20
+ <div class="os-solution-container">#{solution.children}</div>
21
+ HTML
22
+ )
23
+ end
24
+
25
+ problem.replace_children(with:
26
+ <<~HTML
27
+ #{problem_number}
28
+ <span class='os-divider'>. </span>
29
+ <div class="os-problem-container">#{problem.children}</div>
30
+ HTML
31
+ )
32
+ end
33
+ end
34
+ end
@@ -3,8 +3,8 @@
3
3
  module Kitchen
4
4
  module Directions
5
5
  module BakeNumberedTable
6
- def self.v1(table:, number:)
7
- V1.new.bake(table: table, number: number)
6
+ def self.v1(table:, number:, always_caption: false)
7
+ V1.new.bake(table: table, number: number, always_caption: always_caption)
8
8
  end
9
9
  end
10
10
  end
@@ -3,7 +3,7 @@
3
3
  module Kitchen::Directions::BakeNumberedTable
4
4
  class V1
5
5
 
6
- def bake(table:, number:)
6
+ def bake(table:, number:, always_caption: false)
7
7
  table.wrap(%(<div class="os-table">))
8
8
 
9
9
  table_label = "#{I18n.t(:table_label)} #{number}"
@@ -22,14 +22,28 @@ module Kitchen::Directions::BakeNumberedTable
22
22
  table.parent.add_class('os-column-header-container') if table.column_header?
23
23
 
24
24
  # TODO: extra spaces added here to match legacy implementation, but probably not meaningful?
25
- new_summary = "#{table_label} "
25
+ new_summary = "#{table_label} "
26
26
  new_caption = ''
27
+ caption_title = ''
28
+
29
+ if (title = table.first("span[data-type='title']")&.cut)
30
+ new_summary += title.text
31
+ caption_title = <<~HTML
32
+ \n<span class="os-title" data-type="title">#{title.children}</span>
33
+ HTML
34
+ end
35
+
36
+ new_summary += ' '
27
37
 
28
38
  if (caption = table.caption&.cut)
29
39
  new_summary += caption.text
30
40
  new_caption = <<~HTML
31
41
  \n<span class="os-caption">#{caption.children}</span>
32
42
  HTML
43
+ elsif always_caption
44
+ new_caption = <<~HTML
45
+ \n<span class="os-caption"></span>
46
+ HTML
33
47
  end
34
48
 
35
49
  table[:summary] = new_summary
@@ -41,7 +55,7 @@ module Kitchen::Directions::BakeNumberedTable
41
55
  <div class="os-caption-container">
42
56
  <span class="os-title-label">#{I18n.t(:table_label)} </span>
43
57
  <span class="os-number">#{number}</span>
44
- <span class="os-divider"> </span>
58
+ <span class="os-divider"> </span>#{caption_title}
45
59
  <span class="os-divider"> </span>#{new_caption}
46
60
  </div>
47
61
  HTML
@@ -9,6 +9,22 @@ module Kitchen
9
9
  abstract.prepend(child: "<h3 data-type='title'>#{I18n.t(:learning_objectives)}</h3>")
10
10
  end
11
11
  end
12
+
13
+ def self.v2(chapter:)
14
+ chapter.abstracts.each do |abstract|
15
+ abstract.prepend(child: "<h3 data-type='title'>#{I18n.t(:learning_objectives)}</h3>")
16
+ ul = abstract.first!('ul')
17
+ ul.add_class('os-abstract')
18
+ ul.search('li').each_with_index do |li, index|
19
+ li.replace_children(with:
20
+ <<~HTML
21
+ <span class="os-abstract-token">#{chapter.count_in(:book)}.#{abstract.count_in(:chapter)}.#{index + 1}</span>
22
+ <span class="os-abstract-content">#{li.text}</span>
23
+ HTML
24
+ )
25
+ end
26
+ end
27
+ end
12
28
  end
13
29
  end
14
30
  end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kitchen
4
+ module Directions
5
+ module BakeProblemFirstElements
6
+ def self.v1(within:, selectors:)
7
+ selectors.each do |selector|
8
+ within.search(".os-problem-container > #{selector}:first-child").each do |problem|
9
+ problem.add_class('first-element')
10
+ problem.parent.add_class('has-first-element')
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -9,11 +9,7 @@ module Kitchen
9
9
  ol.add_class('os-stepwise')
10
10
 
11
11
  ol.search('li').each_with_index do |li, ii|
12
- li.replace_children(with:
13
- <<~HTML
14
- <span class="os-stepwise-content">#{li.children}</span>
15
- HTML
16
- )
12
+ li.wrap_children('span', class: 'os-stepwise-content')
17
13
  li.prepend(child:
18
14
  <<~HTML
19
15
  <span class="os-stepwise-token">#{I18n.t(:stepwise_step_label)} #{ii + 1}. </span>
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kitchen
4
+ module Directions
5
+ module BakeTheorem
6
+ def self.v1(theorem:, number:)
7
+ V1.new.bake(theorem: theorem, number: number)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kitchen::Directions::BakeTheorem
4
+ class V1
5
+ def bake(theorem:, number:)
6
+ theorem['use-subtitle'] = true
7
+ new_subtitle = theorem.title.cut
8
+
9
+ theorem.wrap_children(class: 'os-note-body')
10
+ note_body = theorem.first('.os-note-body')
11
+
12
+ note_body.prepend(sibling:
13
+ <<~HTML
14
+ <div class="os-title">
15
+ <span class="os-title-label">#{I18n.t(:theorem)} </span>
16
+ <span class="os-number">#{number}</span>
17
+ <span class="os-divider"> </span>
18
+ </div>
19
+ HTML
20
+ )
21
+
22
+ new_subtitle.name = 'h4'
23
+ new_subtitle.add_class('os-subtitle')
24
+ new_subtitle.children.wrap('<span class="os-subtitle-label">')
25
+ note_body.prepend(child: new_subtitle.paste)
26
+ end
27
+ end
28
+ end
@@ -59,6 +59,7 @@ module Kitchen
59
59
 
60
60
  def self.li_for_chapter(chapter)
61
61
  pages = chapter.element_children.only(PageElement, CompositePageElement)
62
+ inner_composite_chapters = chapter.element_children.only(CompositeChapterElement)
62
63
 
63
64
  <<~HTML
64
65
  <li class="os-toc-chapter" cnx-archive-shortid="" cnx-archive-uri="">
@@ -69,6 +70,11 @@ module Kitchen
69
70
  </a>
70
71
  <ol class="os-chapter">
71
72
  #{pages.map { |page| li_for_page(page) }.join("\n")}
73
+ #{
74
+ inner_composite_chapters.map do |composite_chapter|
75
+ li_for_composite_chapter(composite_chapter)
76
+ end.join("\n")
77
+ }
72
78
  </ol>
73
79
  </li>
74
80
  HTML
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kitchen
4
+ module Directions
5
+ module EocSectionTitleLinkSnippet
6
+ def self.v1(page:)
7
+ chapter = page.ancestor(:chapter)
8
+ <<~HTML
9
+ <a href="##{page.title.id}">
10
+ <h3 data-type="document-title" id="#{page.title.copied_id}">
11
+ <span class="os-number">#{chapter.count_in(:book)}.#{page.count_in(:chapter)}</span>
12
+ <span class="os-divider"> </span>
13
+ <span class="os-text" data-type="" itemprop="">#{page.title.text}</span>
14
+ </h3>
15
+ </a>
16
+ HTML
17
+ end
18
+ end
19
+ end
20
+ end
@@ -423,6 +423,12 @@ module Kitchen
423
423
  temp_copy.to_s
424
424
  end
425
425
 
426
+ # Copy the element's id
427
+ def copied_id
428
+ document.record_id_copied(id)
429
+ document.modified_id_to_paste(id)
430
+ end
431
+
426
432
  # Delete the element
427
433
  #
428
434
  def trash
@@ -491,6 +497,35 @@ module Kitchen
491
497
  self
492
498
  end
493
499
 
500
+ # Wraps the element's children in a new element. Yields the new wrapper element
501
+ # to a block, if provided.
502
+ #
503
+ # @param name [String] the wrapper's tag name, defaults to 'div'.
504
+ # @param attributes [Hash] the wrapper's attributes. XML attributes often use hyphens
505
+ # (e.g. 'data-type') which are hard to put into symbols. Therefore underscores in
506
+ # keys passed to this method will be converted to hyphens. If you really want an
507
+ # underscore you can use a double underscore.
508
+ # @yieldparam [Element] the wrapper Element
509
+ # @return [Element] self
510
+ #
511
+ def wrap_children(name='div', attributes={})
512
+ if name.is_a?(Hash)
513
+ attributes = name
514
+ name = 'div'
515
+ end
516
+
517
+ node.children = node.document.create_element(name) do |new_node|
518
+ # For some reason passing attributes to create_element doesn't work, so doing here
519
+ attributes.each do |k, v|
520
+ new_node[k.to_s.gsub(/([^_])_([^_])/, '\1-\2').gsub('__', '_')] = v
521
+ end
522
+ new_node.children = children.to_s
523
+ yield Element.new(node: new_node, document: document, short_type: nil) if block_given?
524
+ end.to_s
525
+
526
+ self
527
+ end
528
+
494
529
  # TODO: methods like replace_children that take string, either forbid or handle Element/Node args
495
530
 
496
531
  # Get the content of children matching the provided selector. Mostly
@@ -604,10 +639,14 @@ module Kitchen
604
639
  end
605
640
  end
606
641
 
642
+ def last_element
643
+ node.last_element_child
644
+ end
645
+
607
646
  # @!method pages
608
647
  # Returns a pages enumerator
609
648
  def_delegators :as_enumerator, :pages, :chapters, :terms, :figures, :notes, :tables, :examples,
610
- :metadatas, :units
649
+ :metadatas, :non_introduction_pages, :units, :titles, :exercises, :composite_pages
611
650
 
612
651
  # Returns this element as an enumerator (over only one element, itself)
613
652
  #