openstax_kitchen 11.0.0 → 11.1.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +21 -0
- data/Gemfile.lock +1 -1
- data/lib/kitchen/composite_page_element.rb +8 -0
- data/lib/kitchen/directions/bake_chapter_introductions/bake_chapter_objectives.rb +46 -0
- data/lib/kitchen/directions/bake_chapter_introductions/bake_chapter_outline.rb +14 -0
- data/lib/kitchen/directions/bake_chapter_introductions/main.rb +43 -0
- data/lib/kitchen/directions/bake_chapter_introductions/v1.rb +56 -0
- data/lib/kitchen/directions/bake_chapter_introductions/v2.rb +91 -0
- data/lib/kitchen/directions/bake_custom_sections/main.rb +14 -0
- data/lib/kitchen/directions/bake_custom_sections/v1.rb +42 -0
- data/lib/kitchen/directions/bake_equations.rb +2 -3
- data/lib/kitchen/directions/bake_example.rb +4 -5
- data/lib/kitchen/directions/bake_figure.rb +5 -3
- data/lib/kitchen/directions/bake_iframes/main.rb +11 -0
- data/lib/kitchen/directions/bake_iframes/v1.rb +25 -0
- data/lib/kitchen/directions/bake_index/main.rb +2 -2
- data/lib/kitchen/directions/bake_index/v1.rb +39 -7
- data/lib/kitchen/directions/bake_index/v1.xhtml.erb +3 -3
- data/lib/kitchen/directions/bake_injected_exercise.rb +18 -0
- data/lib/kitchen/directions/bake_injected_exercise_question.rb +71 -0
- data/lib/kitchen/directions/bake_link_placeholders.rb +15 -2
- data/lib/kitchen/directions/bake_lists_with_para.rb +3 -3
- data/lib/kitchen/directions/bake_notes/bake_autotitled_notes.rb +5 -5
- data/lib/kitchen/directions/bake_notes/bake_note_subtitle.rb +6 -2
- data/lib/kitchen/directions/bake_notes/bake_numbered_notes/main.rb +13 -3
- data/lib/kitchen/directions/bake_notes/bake_numbered_notes/v1.rb +29 -25
- data/lib/kitchen/directions/bake_notes/bake_numbered_notes/v2.rb +22 -17
- data/lib/kitchen/directions/bake_notes/bake_numbered_notes/v3.rb +27 -22
- data/lib/kitchen/directions/bake_numbered_exercise/main.rb +3 -2
- data/lib/kitchen/directions/bake_numbered_exercise/v1.rb +6 -24
- data/lib/kitchen/directions/bake_numbered_table/bake_table_body.rb +3 -3
- data/lib/kitchen/directions/bake_numbered_table/main.rb +4 -4
- data/lib/kitchen/directions/bake_numbered_table/v1.rb +3 -3
- data/lib/kitchen/directions/bake_numbered_table/v2.rb +3 -3
- data/lib/kitchen/directions/bake_toc.rb +1 -1
- data/lib/kitchen/directions/move_solutions_to_answer_key/move_solutions_from_exercise_section.rb +1 -7
- data/lib/kitchen/directions/move_solutions_to_answer_key/move_solutions_from_numbered_note.rb +1 -7
- data/lib/kitchen/directions/move_solutions_to_answer_key/strategies/contemporary_math.rb +17 -0
- data/lib/kitchen/element_base.rb +38 -2
- data/lib/kitchen/element_enumerator_base.rb +35 -0
- data/lib/kitchen/injected_question_element.rb +77 -0
- data/lib/kitchen/injected_question_element_enumerator.rb +21 -0
- data/lib/kitchen/selectors/base.rb +6 -0
- data/lib/kitchen/selectors/standard_1.rb +3 -0
- data/lib/kitchen/solution_element_enumerator.rb +21 -0
- data/lib/kitchen/version.rb +1 -1
- data/lib/locales/en.yml +6 -4
- data/lib/locales/es.yml +5 -4
- data/lib/locales/pl.yml +52 -7
- metadata +17 -6
- data/lib/kitchen/directions/bake_chapter_introductions/chapter_introduction.xhtml.erb +0 -0
- data/lib/kitchen/directions/bake_chapter_introductions.rb +0 -65
- data/lib/kitchen/directions/bake_notes/bake_note_iframes.rb +0 -27
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kitchen::Directions::BakeIframes
|
4
|
+
class V1
|
5
|
+
def bake(outer_element:)
|
6
|
+
iframes = outer_element.search('iframe')
|
7
|
+
return unless iframes.any?
|
8
|
+
|
9
|
+
iframes.each do |iframe|
|
10
|
+
iframe.wrap('<div class="os-has-iframe" data-type="alternatives">')
|
11
|
+
iframe.add_class('os-is-iframe')
|
12
|
+
link_ref = iframe[:src]
|
13
|
+
next unless link_ref
|
14
|
+
|
15
|
+
iframe = iframe.parent
|
16
|
+
iframe.add_class('os-has-link')
|
17
|
+
iframe.prepend(child:
|
18
|
+
<<~HTML
|
19
|
+
<a class="os-is-link" href="#{link_ref}" target="_window">#{I18n.t(:iframe_link_text)}</a>
|
20
|
+
HTML
|
21
|
+
)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -65,7 +65,7 @@ module Kitchen::Directions::BakeIndex
|
|
65
65
|
return -1 if force_first
|
66
66
|
return 1 if other.force_first
|
67
67
|
|
68
|
-
name
|
68
|
+
I18n.sort_strings(name, other.name)
|
69
69
|
end
|
70
70
|
|
71
71
|
protected
|
@@ -112,9 +112,15 @@ module Kitchen::Directions::BakeIndex
|
|
112
112
|
end
|
113
113
|
end
|
114
114
|
|
115
|
-
def bake(book:)
|
115
|
+
def bake(book:, types: %w[main], uuid_prefix: '')
|
116
116
|
@metadata_elements = book.metadata.children_to_keep.copy
|
117
|
-
@
|
117
|
+
@uuid_prefix = uuid_prefix
|
118
|
+
@indexes = types.each.with_object({}) do |type, hash|
|
119
|
+
index_name = type == 'main' ? 'term' : type
|
120
|
+
hash[index_name] = Index.new
|
121
|
+
end
|
122
|
+
|
123
|
+
# Numbering of IDs doesn't depend on term type
|
118
124
|
|
119
125
|
book.pages.terms.each do |term_element|
|
120
126
|
page = term_element.ancestor(:page)
|
@@ -132,18 +138,44 @@ module Kitchen::Directions::BakeIndex
|
|
132
138
|
add_term_to_index(term_element, page_title)
|
133
139
|
end
|
134
140
|
|
135
|
-
|
141
|
+
types.each do |type|
|
142
|
+
@container_class = "os-index#{"-#{type}" unless type == 'main'}-container"
|
143
|
+
@uuid_key = "#{uuid_prefix}index#{"-#{type}" unless type == 'main'}"
|
144
|
+
@title = I18n.t("index.#{type}")
|
145
|
+
|
146
|
+
index_name = type == 'main' ? 'term' : type
|
147
|
+
@index = @indexes[index_name]
|
148
|
+
|
149
|
+
book.first('body').append(child: render(file: 'v1.xhtml.erb'))
|
150
|
+
end
|
136
151
|
end
|
137
152
|
|
138
153
|
def add_term_to_index(term_element, page_title)
|
139
|
-
|
154
|
+
type =
|
155
|
+
if !term_element.key?('index')
|
156
|
+
'term'
|
157
|
+
elsif term_element['cxlxt:index'] == 'name'
|
158
|
+
'name'
|
159
|
+
elsif term_element['cxlxt:index'] == 'foreign'
|
160
|
+
'foreign'
|
161
|
+
end
|
162
|
+
|
163
|
+
if term_element.key?('reference')
|
164
|
+
term_reference = term_element['cmlnle:reference']
|
165
|
+
group_by = term_reference[0]
|
166
|
+
content = term_reference
|
167
|
+
else
|
168
|
+
group_by = I18n.transliterate(term_element.text.strip[0])
|
169
|
+
content = term_element.text
|
170
|
+
end
|
171
|
+
|
140
172
|
group_by = I18n.t(:eob_index_symbols_group) unless group_by.match?(/[[:alpha:]]/)
|
141
173
|
term_element['group-by'] = group_by
|
142
174
|
|
143
175
|
# Add it to our index object
|
144
|
-
@
|
176
|
+
@indexes[type].add_term(
|
145
177
|
Term.new(
|
146
|
-
text:
|
178
|
+
text: content,
|
147
179
|
id: term_element.id,
|
148
180
|
group_by: group_by,
|
149
181
|
page_title: page_title.gsub(/\n/, '')
|
@@ -1,9 +1,9 @@
|
|
1
|
-
<div class="os-eob
|
1
|
+
<div class="os-eob <%= @container_class %>" data-type="composite-page" data-uuid-key="<%= @uuid_key %>">
|
2
2
|
<h1 data-type="document-title">
|
3
|
-
<span class="os-text"><%=
|
3
|
+
<span class="os-text"><%= @title %></span>
|
4
4
|
</h1>
|
5
5
|
<div data-type="metadata" style="display: none;">
|
6
|
-
<h1 data-type="document-title" itemprop="name"><%=
|
6
|
+
<h1 data-type="document-title" itemprop="name"><%= @title %></h1>
|
7
7
|
<%= @metadata_elements.paste %>
|
8
8
|
</div>
|
9
9
|
<% @index.sections.each do |section| %>
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kitchen::Directions::BakeInjectedExercise
|
4
|
+
def self.v1(exercise:)
|
5
|
+
V1.new.bake(exercise: exercise)
|
6
|
+
end
|
7
|
+
|
8
|
+
class V1
|
9
|
+
def bake(exercise:)
|
10
|
+
context = exercise.search('div[data-type="exercise-context"]')&.first
|
11
|
+
return unless context
|
12
|
+
|
13
|
+
# link replacement is done by BakeLinkPlaceholders
|
14
|
+
link = context.first('a').cut
|
15
|
+
context.replace_children(with: "#{I18n.t(:context_lead_text)}#{link.paste}")
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kitchen::Directions::BakeInjectedExerciseQuestion
|
4
|
+
def self.v1(question:, number:, only_number_solution: false)
|
5
|
+
V1.new.bake(question: question, number: number, only_number_solution: only_number_solution)
|
6
|
+
end
|
7
|
+
|
8
|
+
class V1
|
9
|
+
def bake(question:, number:, only_number_solution:)
|
10
|
+
id = question.id
|
11
|
+
|
12
|
+
# Store label in pantry
|
13
|
+
unless only_number_solution
|
14
|
+
label_number = "#{question.ancestor(:chapter).count_in(:book)}.#{number}"
|
15
|
+
question.target_label(label_text: 'exercise', custom_content: label_number)
|
16
|
+
end
|
17
|
+
|
18
|
+
# Synthesize multiple choice solution
|
19
|
+
if question.answers
|
20
|
+
case question.answers[:type]
|
21
|
+
when 'a'
|
22
|
+
alphabet = *('a'..'z')
|
23
|
+
else
|
24
|
+
raise('Unsupported list type for multiple choice options')
|
25
|
+
end
|
26
|
+
letter_answers = question.correct_answer_letters(alphabet)
|
27
|
+
end
|
28
|
+
if letter_answers.present?
|
29
|
+
question.append(child:
|
30
|
+
<<~HTML
|
31
|
+
<div data-type="question-solution">#{letter_answers.join(', ')}</div>
|
32
|
+
HTML
|
33
|
+
)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Bake question
|
37
|
+
unless only_number_solution
|
38
|
+
problem_number = "<span class='os-number'>#{number}</span>"
|
39
|
+
if question.solution
|
40
|
+
problem_number = "<a class='os-number' href='##{id}-solution'>#{number}</a>"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
question.prepend(child:
|
45
|
+
<<~HTML
|
46
|
+
#{problem_number unless only_number_solution}
|
47
|
+
#{"<span class='os-divider'>. </span>" unless only_number_solution}
|
48
|
+
<div class="os-problem-container">
|
49
|
+
#{question.stimulus&.cut&.paste}
|
50
|
+
#{question.stem.cut.paste}
|
51
|
+
#{question.answers&.cut&.paste}
|
52
|
+
</div>
|
53
|
+
HTML
|
54
|
+
)
|
55
|
+
|
56
|
+
# Bake solution
|
57
|
+
solution = question.solution
|
58
|
+
return unless solution
|
59
|
+
|
60
|
+
question.add_class('os-hasSolution')
|
61
|
+
solution.id = "#{id}-solution"
|
62
|
+
solution.replace_children(with:
|
63
|
+
<<~HTML
|
64
|
+
<a class='os-number' href='##{id}'>#{number}</a>
|
65
|
+
<span class='os-divider'>. </span>
|
66
|
+
<div class="os-solution-container">#{solution.children}</div>
|
67
|
+
HTML
|
68
|
+
)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -5,12 +5,25 @@ module Kitchen
|
|
5
5
|
# Bake directions for link placeholders
|
6
6
|
#
|
7
7
|
module BakeLinkPlaceholders
|
8
|
-
def self.v1(book:)
|
8
|
+
def self.v1(book:, cases: false)
|
9
9
|
book.search('a').each do |anchor|
|
10
10
|
next unless anchor.text == '[link]'
|
11
11
|
|
12
|
+
label_case = anchor['cmlnle:case']
|
12
13
|
id = anchor[:href][1..-1]
|
13
|
-
|
14
|
+
|
15
|
+
if cases
|
16
|
+
pantry_name = if anchor.key?('case')
|
17
|
+
"#{label_case}_link_text"
|
18
|
+
else
|
19
|
+
'nominative_link_text'
|
20
|
+
end
|
21
|
+
|
22
|
+
replacement = book.pantry(name: pantry_name).get(id)
|
23
|
+
else
|
24
|
+
replacement = book.pantry(name: :link_text).get(id)
|
25
|
+
end
|
26
|
+
|
14
27
|
if replacement.present?
|
15
28
|
anchor.replace_children(with: replacement)
|
16
29
|
else
|
@@ -3,13 +3,13 @@
|
|
3
3
|
module Kitchen
|
4
4
|
module Directions
|
5
5
|
# Bakes lists with paragraphs
|
6
|
-
# Should be used for books created by Adaptarr to put list
|
6
|
+
# Should be used for books created by Adaptarr to put list content directly to <li>
|
7
7
|
#
|
8
8
|
module BakeListsWithPara
|
9
9
|
def self.v1(book:)
|
10
10
|
book.search('li').each do |item|
|
11
|
-
|
12
|
-
item.replace_children with:
|
11
|
+
item_para = item.first('p')&.cut
|
12
|
+
item.replace_children with: item_para.children unless item_para.nil?
|
13
13
|
end
|
14
14
|
end
|
15
15
|
end
|
@@ -3,19 +3,19 @@
|
|
3
3
|
module Kitchen
|
4
4
|
module Directions
|
5
5
|
module BakeAutotitledNotes
|
6
|
-
def self.v1(book:, classes:)
|
6
|
+
def self.v1(book:, classes:, bake_subtitle: true, cases: false)
|
7
7
|
book.notes.each do |note|
|
8
8
|
next unless (note.classes & classes).any?
|
9
9
|
|
10
|
-
bake_note(note: note)
|
10
|
+
bake_note(note: note, bake_subtitle: bake_subtitle, cases: cases)
|
11
11
|
end
|
12
12
|
end
|
13
13
|
|
14
|
-
def self.bake_note(note:)
|
15
|
-
|
14
|
+
def self.bake_note(note:, bake_subtitle:, cases: false)
|
15
|
+
Kitchen::Directions::BakeIframes.v1(outer_element: note)
|
16
16
|
note.wrap_children(class: 'os-note-body')
|
17
17
|
|
18
|
-
BakeNoteSubtitle.v1(note: note)
|
18
|
+
BakeNoteSubtitle.v1(note: note, cases: cases) if bake_subtitle
|
19
19
|
|
20
20
|
note.prepend(child:
|
21
21
|
<<~HTML
|
@@ -3,14 +3,18 @@
|
|
3
3
|
module Kitchen
|
4
4
|
module Directions
|
5
5
|
module BakeNoteSubtitle
|
6
|
-
def self.v1(note:)
|
6
|
+
def self.v1(note:, cases: false)
|
7
7
|
title = note.title&.cut
|
8
8
|
|
9
9
|
return unless title
|
10
10
|
|
11
11
|
# Store label information
|
12
12
|
note_label = title.children
|
13
|
-
|
13
|
+
if cases
|
14
|
+
note.target_label(label_text: 'note', custom_content: note_label.to_s, cases: cases)
|
15
|
+
else
|
16
|
+
note.target_label(custom_content: note_label.to_s)
|
17
|
+
end
|
14
18
|
|
15
19
|
title.name = 'h4'
|
16
20
|
title.add_class('os-subtitle')
|
@@ -3,8 +3,8 @@
|
|
3
3
|
module Kitchen
|
4
4
|
module Directions
|
5
5
|
module BakeNumberedNotes
|
6
|
-
def self.v1(book:, classes:)
|
7
|
-
V1.new.bake(book: book, classes: classes)
|
6
|
+
def self.v1(book:, classes:, cases: false)
|
7
|
+
V1.new.bake(book: book, classes: classes, cases: cases)
|
8
8
|
end
|
9
9
|
|
10
10
|
def self.v2(book:, classes:)
|
@@ -21,6 +21,8 @@ module Kitchen
|
|
21
21
|
# Used by V1, V2, V3
|
22
22
|
def self.bake_note_exercise(note:, exercise:, divider: ' ', suppress_solution: false)
|
23
23
|
exercise.add_class('unnumbered')
|
24
|
+
number = note.first('.os-number').text.gsub(/#/, '')
|
25
|
+
|
24
26
|
# bake problem
|
25
27
|
exercise.problem.wrap_children('div', class: 'os-problem-container')
|
26
28
|
exercise.search('[data-type="commentary"]').each(&:trash)
|
@@ -33,11 +35,19 @@ module Kitchen
|
|
33
35
|
else
|
34
36
|
BakeNumberedExercise.bake_solution_v1(
|
35
37
|
exercise: exercise,
|
36
|
-
number:
|
38
|
+
number: number,
|
37
39
|
divider: divider
|
38
40
|
)
|
39
41
|
end
|
40
42
|
end
|
43
|
+
|
44
|
+
def self.bake_note_injected_question(note:, question:)
|
45
|
+
question.add_class('unnumbered')
|
46
|
+
number = note.first('.os-number').text.gsub(/#/, '')
|
47
|
+
Kitchen::Directions::BakeInjectedExerciseQuestion.v1(
|
48
|
+
question: question, number: number, only_number_solution: true
|
49
|
+
)
|
50
|
+
end
|
41
51
|
end
|
42
52
|
end
|
43
53
|
end
|
@@ -1,37 +1,41 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
module Kitchen::Directions
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
3
|
+
module Kitchen::Directions
|
4
|
+
module BakeNumberedNotes
|
5
|
+
class V1
|
6
|
+
def bake(book:, classes:, cases: false)
|
7
|
+
classes.each do |klass|
|
8
|
+
book.chapters.pages.notes("$.#{klass}").each do |note|
|
9
|
+
bake_note(note: note, cases: cases)
|
10
|
+
note.exercises.each do |exercise|
|
11
|
+
BakeNumberedNotes.bake_note_exercise(note: note, exercise: exercise)
|
12
|
+
end
|
13
|
+
note.injected_questions.each do |question|
|
14
|
+
BakeNumberedNotes.bake_note_injected_question(note: note, question: question)
|
15
|
+
end
|
12
16
|
end
|
13
17
|
end
|
14
18
|
end
|
15
|
-
end
|
16
19
|
|
17
|
-
|
18
|
-
|
20
|
+
def bake_note(note:, cases: false)
|
21
|
+
note.wrap_children(class: 'os-note-body')
|
19
22
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
23
|
+
chapter_count = note.ancestor(:chapter).count_in(:book)
|
24
|
+
note_count = note.count_in(:chapter)
|
25
|
+
note.prepend(child:
|
26
|
+
<<~HTML
|
27
|
+
<h3 class="os-title">
|
28
|
+
<span class="os-title-label">#{note.autogenerated_title}</span>
|
29
|
+
<span class="os-number">#{chapter_count}.#{note_count}</span>
|
30
|
+
<span class="os-divider"> </span>
|
31
|
+
</h3>
|
32
|
+
HTML
|
33
|
+
)
|
31
34
|
|
32
|
-
|
35
|
+
return unless note['use-subtitle']
|
33
36
|
|
34
|
-
|
37
|
+
BakeNoteSubtitle.v1(note: note, cases: cases)
|
38
|
+
end
|
35
39
|
end
|
36
40
|
end
|
37
41
|
end
|
@@ -1,22 +1,27 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
module Kitchen::Directions
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
<
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
3
|
+
module Kitchen::Directions
|
4
|
+
module BakeNumberedNotes
|
5
|
+
class V2
|
6
|
+
def bake(book:, classes:)
|
7
|
+
classes.each do |klass|
|
8
|
+
book.chapters.pages.notes("$.#{klass}").each do |note|
|
9
|
+
note.wrap_children(class: 'os-note-body')
|
10
|
+
note_count = note.count_in(:page)
|
11
|
+
note.prepend(child:
|
12
|
+
<<~HTML
|
13
|
+
<h3 class="os-title">
|
14
|
+
<span class="os-title-label">#{note.autogenerated_title}</span>
|
15
|
+
<span class="os-number">##{note_count}</span>
|
16
|
+
</h3>
|
17
|
+
HTML
|
18
|
+
)
|
19
|
+
note.exercises.each do |exercise|
|
20
|
+
BakeNumberedNotes.bake_note_exercise(note: note, exercise: exercise, divider: '. ')
|
21
|
+
end
|
22
|
+
note.injected_questions.each do |question|
|
23
|
+
BakeNumberedNotes.bake_note_injected_question(note: note, question: question)
|
24
|
+
end
|
20
25
|
end
|
21
26
|
end
|
22
27
|
end
|