openstax_kitchen 14.0.0 → 17.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +47 -3
- data/Gemfile.lock +4 -4
- data/codecov.yaml +4 -0
- data/lib/kitchen/directions/bake_all_chapter_solutions_types.rb +30 -0
- data/lib/kitchen/directions/bake_all_numbered_exercise_types.rb +26 -0
- data/lib/kitchen/directions/bake_annotation_classes/v1.rb +11 -2
- data/lib/kitchen/directions/bake_autotitled_exercise/main.rb +4 -0
- data/lib/kitchen/directions/bake_autotitled_exercise/v3.rb +35 -0
- data/lib/kitchen/directions/bake_chapter_references/main.rb +15 -0
- data/lib/kitchen/directions/bake_chapter_references/v2.rb +53 -0
- data/lib/kitchen/directions/bake_chapter_references/v3.rb +45 -0
- data/lib/kitchen/directions/bake_chapter_summary.rb +5 -4
- data/lib/kitchen/directions/bake_example.rb +1 -1
- data/lib/kitchen/directions/bake_exercise_prefixes/main.rb +14 -0
- data/lib/kitchen/directions/bake_exercise_prefixes/v1.rb +22 -0
- data/lib/kitchen/directions/bake_footnotes/v1.rb +1 -1
- data/lib/kitchen/directions/bake_index/v1.rb +13 -8
- data/lib/kitchen/directions/bake_index/v1.xhtml.erb +6 -10
- data/lib/kitchen/directions/{bake_injected_exercise.rb → bake_injected_exercise/bake_injected_exercise.rb} +10 -1
- data/lib/kitchen/directions/bake_injected_exercise/bake_injected_exercise_question.rb +4 -0
- data/lib/kitchen/directions/bake_link_placeholders.rb +1 -1
- data/lib/kitchen/directions/bake_lo_link_labels.rb +19 -0
- data/lib/kitchen/directions/bake_non_introduction_pages.rb +3 -1
- data/lib/kitchen/directions/bake_numbered_exercise/main.rb +8 -3
- data/lib/kitchen/directions/bake_numbered_exercise/v1.rb +48 -7
- data/lib/kitchen/directions/bake_numbered_table/bake_table_body.rb +5 -1
- data/lib/kitchen/directions/bake_numbered_table/v2.rb +2 -1
- data/lib/kitchen/directions/bake_references/v1.rb +13 -0
- data/lib/kitchen/directions/bake_screenreader_spans.rb +22 -0
- data/lib/kitchen/directions/bake_unnumbered_exercise.rb +16 -0
- data/lib/kitchen/directions/book_answer_key_container/eob_answer_key_container.xhtml.erb +9 -0
- data/lib/kitchen/directions/book_answer_key_container/v1.rb +6 -2
- data/lib/kitchen/directions/default_strategy_for_answer_key_solutions.rb +35 -0
- data/lib/kitchen/directions/move_solutions_to_answer_key/answer_key_inner_container.rb +29 -0
- data/lib/kitchen/element_base.rb +25 -5
- data/lib/kitchen/element_enumerator_base.rb +18 -0
- data/lib/kitchen/injected_exercise_element.rb +41 -0
- data/lib/kitchen/injected_exercise_element_enumerator.rb +21 -0
- data/lib/kitchen/injected_question_element.rb +7 -0
- data/lib/kitchen/patches/i18n.rb +4 -0
- data/lib/kitchen/patches/integer.rb +32 -3
- data/lib/kitchen/patches/nokogiri.rb +5 -4
- data/lib/kitchen/patches/renderable.rb +1 -1
- data/lib/kitchen/selectors/base.rb +3 -0
- data/lib/kitchen/selectors/standard_1.rb +1 -0
- data/lib/kitchen/version.rb +1 -1
- data/lib/locales/en.yml +16 -0
- data/lib/locales/es.yml +6 -1
- metadata +18 -11
- data/lib/kitchen/directions/book_answer_key_container/eob_answer_key_outer_container.xhtml.erb +0 -9
- data/lib/kitchen/directions/move_solutions_to_answer_key/main.rb +0 -18
- data/lib/kitchen/directions/move_solutions_to_answer_key/strategies/calculus.rb +0 -41
- data/lib/kitchen/directions/move_solutions_to_answer_key/strategies/contemporary_math.rb +0 -40
- data/lib/kitchen/directions/move_solutions_to_answer_key/strategies/default.rb +0 -27
- data/lib/kitchen/directions/move_solutions_to_answer_key/strategies/precalculus.rb +0 -63
- data/lib/kitchen/directions/move_solutions_to_answer_key/strategies/uphysics.rb +0 -21
- data/lib/kitchen/directions/move_solutions_to_answer_key/v1.rb +0 -47
@@ -3,14 +3,19 @@
|
|
3
3
|
module Kitchen
|
4
4
|
module Directions
|
5
5
|
module BakeNumberedExercise
|
6
|
+
# rubocop:disable Metrics/ParameterLists
|
7
|
+
# :/
|
6
8
|
def self.v1(exercise:, number:, suppress_solution_if: false,
|
7
|
-
note_suppressed_solutions: false, cases: false)
|
9
|
+
note_suppressed_solutions: false, cases: false, solution_stays_put: false)
|
8
10
|
V1.new.bake(exercise: exercise, number: number, suppress_solution_if: suppress_solution_if,
|
9
|
-
note_suppressed_solutions: note_suppressed_solutions, cases: cases
|
11
|
+
note_suppressed_solutions: note_suppressed_solutions, cases: cases,
|
12
|
+
solution_stays_put: solution_stays_put)
|
10
13
|
end
|
14
|
+
# rubocop:enable Metrics/ParameterLists
|
11
15
|
|
12
16
|
def self.bake_solution_v1(exercise:, number:, divider: '. ')
|
13
|
-
V1.new.bake_solution(exercise: exercise, number: number, divider: divider
|
17
|
+
V1.new.bake_solution(exercise: exercise, number: number, divider: divider,
|
18
|
+
solution_stays_put: false)
|
14
19
|
end
|
15
20
|
end
|
16
21
|
end
|
@@ -2,13 +2,25 @@
|
|
2
2
|
|
3
3
|
module Kitchen::Directions::BakeNumberedExercise
|
4
4
|
class V1
|
5
|
-
|
6
|
-
|
5
|
+
# rubocop:disable Metrics/ParameterLists
|
6
|
+
def bake(exercise:, number:, suppress_solution_if:,
|
7
|
+
note_suppressed_solutions:, cases:, solution_stays_put:)
|
7
8
|
problem = exercise.problem
|
8
9
|
solution = exercise.solution
|
9
10
|
|
11
|
+
in_appendix = exercise.has_ancestor?(:page) && exercise.ancestor(:page).has_class?('appendix')
|
12
|
+
|
10
13
|
# Store label information
|
11
|
-
|
14
|
+
if in_appendix
|
15
|
+
label_number = number
|
16
|
+
title_label = "<span class=\"os-title-label\">#{I18n.t('exercise')}</span>"
|
17
|
+
problem_divider = ''
|
18
|
+
else
|
19
|
+
label_number = "#{exercise.ancestor(:chapter).count_in(:book)}.#{number}"
|
20
|
+
title_label = ''
|
21
|
+
problem_divider = "<span class='os-divider'>. </span>"
|
22
|
+
end
|
23
|
+
|
12
24
|
exercise.target_label(label_text: 'exercise', custom_content: label_number, cases: cases)
|
13
25
|
|
14
26
|
problem_number = "<span class='os-number'>#{number}</span>"
|
@@ -26,25 +38,54 @@ module Kitchen::Directions::BakeNumberedExercise
|
|
26
38
|
solution.trash
|
27
39
|
exercise.add_class('os-hasSolution-trashed') if note_suppressed_solutions
|
28
40
|
else
|
29
|
-
problem_number =
|
30
|
-
|
41
|
+
problem_number = \
|
42
|
+
if solution_stays_put || in_appendix
|
43
|
+
"<span class='os-number'>#{number}</span>"
|
44
|
+
else
|
45
|
+
"<a class='os-number' href='##{exercise.id}-solution'>#{number}</a>"
|
46
|
+
end
|
47
|
+
bake_solution(
|
48
|
+
exercise: exercise,
|
49
|
+
number: number,
|
50
|
+
solution_stays_put: solution_stays_put,
|
51
|
+
in_appendix: in_appendix
|
52
|
+
)
|
31
53
|
end
|
32
54
|
end
|
33
55
|
|
34
56
|
problem.replace_children(with:
|
35
57
|
<<~HTML
|
58
|
+
#{title_label}
|
36
59
|
#{problem_number}
|
37
|
-
|
60
|
+
#{problem_divider}
|
38
61
|
<div class="os-problem-container">#{problem.children}</div>
|
39
62
|
HTML
|
40
63
|
)
|
41
64
|
end
|
65
|
+
# rubocop:enable Metrics/ParameterLists
|
42
66
|
|
43
|
-
def bake_solution(exercise:, number:, divider: '. ')
|
67
|
+
def bake_solution(exercise:, number:, solution_stays_put:, divider: '. ', in_appendix: false)
|
44
68
|
solution = exercise.solution
|
69
|
+
if solution_stays_put
|
70
|
+
solution.wrap_children(class: 'os-solution-container')
|
71
|
+
solution.prepend(child:
|
72
|
+
<<~HTML
|
73
|
+
<h4 class="solution-title" data-type="title">
|
74
|
+
<span class="os-text">#{I18n.t(:solution)}</span>
|
75
|
+
</h4>
|
76
|
+
HTML
|
77
|
+
)
|
78
|
+
return
|
79
|
+
end
|
80
|
+
|
45
81
|
solution.id = "#{exercise.id}-solution"
|
46
82
|
exercise.add_class('os-hasSolution')
|
47
83
|
|
84
|
+
if in_appendix
|
85
|
+
solution.wrap_children(class: 'os-solution-container')
|
86
|
+
return
|
87
|
+
end
|
88
|
+
|
48
89
|
solution.replace_children(with:
|
49
90
|
<<~HTML
|
50
91
|
<a class='os-number' href='##{exercise.id}'>#{number}</a>
|
@@ -44,8 +44,9 @@ module Kitchen
|
|
44
44
|
table.target_label(label_text: 'table', custom_content: number, cases: cases)
|
45
45
|
|
46
46
|
if table.top_titled?
|
47
|
+
klass = table.text_heavy? ? 'text-heavy-top-titled' : 'top-titled'
|
47
48
|
custom_table = CustomBody.new(table: table,
|
48
|
-
klass:
|
49
|
+
klass: klass,
|
49
50
|
fake_title_class: 'os-table-title',
|
50
51
|
fake_title: table.title,
|
51
52
|
to_trash: table.title_row)
|
@@ -65,6 +66,9 @@ module Kitchen
|
|
65
66
|
elsif table.text_heavy?
|
66
67
|
custom_table = CustomBody.new(table: table, klass: 'text-heavy')
|
67
68
|
custom_table.modify_body(has_fake_title: false)
|
69
|
+
elsif table.unstyled?
|
70
|
+
custom_table = CustomBody.new(table: table, klass: 'unstyled')
|
71
|
+
custom_table.modify_body(has_fake_title: false)
|
68
72
|
end
|
69
73
|
end
|
70
74
|
end
|
@@ -9,7 +9,8 @@ module Kitchen::Directions::BakeNumberedTable
|
|
9
9
|
Kitchen::Directions::BakeTableBody::V1.new.bake(table: table, number: number, cases: cases)
|
10
10
|
|
11
11
|
caption = ''
|
12
|
-
if table&.caption&.first("span[data-type='title']")
|
12
|
+
if (table&.caption&.first("span[data-type='title']") || table&.caption) \
|
13
|
+
&& !table.top_captioned?
|
13
14
|
caption_el = table.caption
|
14
15
|
caption_el.add_class('os-caption')
|
15
16
|
caption_el.name = 'span'
|
@@ -12,6 +12,19 @@ module Kitchen::Directions::BakeReferences
|
|
12
12
|
<sup class="os-citation-number">#{link.count_in(:chapter)}</sup>
|
13
13
|
HTML
|
14
14
|
)
|
15
|
+
|
16
|
+
next if link.nil?
|
17
|
+
|
18
|
+
link_sibling = link.previous unless link.preceded_by_text?
|
19
|
+
next if link_sibling.nil?
|
20
|
+
|
21
|
+
next unless link_sibling&.raw&.attr('data-type') == 'cite'
|
22
|
+
|
23
|
+
link.prepend(sibling:
|
24
|
+
<<~HTML
|
25
|
+
<span class="os-reference-link-separator">, </span>
|
26
|
+
HTML
|
27
|
+
)
|
15
28
|
end
|
16
29
|
|
17
30
|
chapter.references.each do |reference|
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kitchen
|
4
|
+
module Directions
|
5
|
+
module BakeScreenreaderSpans
|
6
|
+
# Add text for accessibility.
|
7
|
+
# Additional screenreader spans can be added below.
|
8
|
+
def self.v1(book:)
|
9
|
+
book.search('u[data-effect="double-underline"]').each do |element|
|
10
|
+
element.add_previous_sibling(
|
11
|
+
'<span data-screenreader-only="true">double underline</span>'
|
12
|
+
)
|
13
|
+
end
|
14
|
+
book.search('u[data-effect="underline"]').each do |element|
|
15
|
+
element.add_previous_sibling(
|
16
|
+
'<span data-screenreader-only="true">underline</span>'
|
17
|
+
)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kitchen
|
4
|
+
module Directions
|
5
|
+
module BakeUnnumberedExercise
|
6
|
+
def self.v1(exercise:)
|
7
|
+
exercise.problem.wrap_children(class: 'os-problem-container')
|
8
|
+
return unless exercise.solution
|
9
|
+
|
10
|
+
exercise.solutions.each do |solution|
|
11
|
+
solution.wrap_children(class: 'os-solution-container')
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
<div class="os-eob os-<%= @solutions_or_solution %>-container" data-type="<%= @composite_element %>" data-uuid-key=".<%= @uuid_key %>">
|
2
|
+
<<%= @main_title_tag %> data-type="document-title">
|
3
|
+
<span class="os-text"><%= @title %></span>
|
4
|
+
</<%= @main_title_tag %>>
|
5
|
+
<div data-type="metadata" style="display: none;">
|
6
|
+
<h1 data-type="document-title" itemprop="name"><%= @title %></h1>
|
7
|
+
<%= @metadata.paste %>
|
8
|
+
</div>
|
9
|
+
</div>
|
@@ -4,10 +4,14 @@ module Kitchen::Directions::BookAnswerKeyContainer
|
|
4
4
|
class V1
|
5
5
|
renderable
|
6
6
|
|
7
|
-
def bake(book:, solutions_plural:
|
7
|
+
def bake(book:, solutions_plural:)
|
8
|
+
@composite_element = 'composite-chapter'
|
8
9
|
@metadata = book.metadata.children_to_keep.copy
|
9
10
|
@solutions_or_solution = solutions_plural ? 'solutions' : 'solution'
|
10
|
-
|
11
|
+
@title = I18n.t(:answer_key_title)
|
12
|
+
@main_title_tag = 'h1'
|
13
|
+
@uuid_key = @solutions_or_solution
|
14
|
+
book.body.append(child: render(file: 'eob_answer_key_container.xhtml.erb'))
|
11
15
|
book.body.first("div.os-eob.os-#{@solutions_or_solution}-container")
|
12
16
|
end
|
13
17
|
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kitchen::Directions::DefaultStrategyForAnswerKeySolutions
|
4
|
+
def self.v1(strategy_options:, chapter:, append_to:)
|
5
|
+
V1.new(
|
6
|
+
strategy_options
|
7
|
+
).bake(
|
8
|
+
chapter: chapter,
|
9
|
+
append_to: append_to
|
10
|
+
)
|
11
|
+
end
|
12
|
+
|
13
|
+
class V1
|
14
|
+
def bake(chapter:, append_to:)
|
15
|
+
bake_section(chapter: chapter, append_to: append_to)
|
16
|
+
end
|
17
|
+
|
18
|
+
protected
|
19
|
+
|
20
|
+
def bake_section(chapter:, append_to:)
|
21
|
+
@selectors.each do |selector|
|
22
|
+
chapter.search("#{selector} div[data-type='solution']").each do |solution|
|
23
|
+
append_to.append(child: solution.cut.to_s) unless @condition.present? && !@condition
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# This method helps to obtain more strategy-specific params through
|
29
|
+
# "strategy_options: {blah1: 1, blah2: 2}"
|
30
|
+
def initialize(strategy_options)
|
31
|
+
@selectors = strategy_options[:selectors] || (raise 'missing selectors for strategy')
|
32
|
+
@condition = strategy_options[:condition]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kitchen::Directions::AnswerKeyInnerContainer
|
4
|
+
def self.v1(chapter:, metadata_source:, append_to:, solutions_plural: true)
|
5
|
+
V1.new.bake(
|
6
|
+
chapter: chapter,
|
7
|
+
metadata_source: metadata_source,
|
8
|
+
append_to: append_to,
|
9
|
+
solutions_plural: solutions_plural
|
10
|
+
)
|
11
|
+
end
|
12
|
+
|
13
|
+
class V1
|
14
|
+
renderable
|
15
|
+
|
16
|
+
def bake(chapter:, metadata_source:, append_to:, solutions_plural:)
|
17
|
+
@solutions_or_solution = solutions_plural ? 'solutions' : 'solution'
|
18
|
+
@uuid_key = "#{@solutions_or_solution}#{chapter.count_in(:book)}"
|
19
|
+
@metadata = metadata_source.children_to_keep.copy
|
20
|
+
@composite_element = 'composite-page'
|
21
|
+
@title = "#{I18n.t(:chapter)} #{chapter.count_in(:book)}"
|
22
|
+
@main_title_tag = 'h2'
|
23
|
+
|
24
|
+
append_to.append(
|
25
|
+
child: render(file: '../book_answer_key_container/eob_answer_key_container.xhtml.erb')
|
26
|
+
).first("div[data-uuid-key='.#{@uuid_key}']")
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/kitchen/element_base.rb
CHANGED
@@ -82,9 +82,13 @@ module Kitchen
|
|
82
82
|
# Set the inner HTML for this element
|
83
83
|
# @see https://www.rubydoc.info/github/sparklemotion/nokogiri/Nokogiri/XML/Node#inner_html=-instance_method Nokogiri::XML::Node#inner_html=
|
84
84
|
# @return Object
|
85
|
+
# @!method preceded_by_text
|
86
|
+
# Returns true if the immediately preceding sibling is text
|
87
|
+
# @return Boolean
|
85
88
|
def_delegators :@node, :name=, :name, :[], :[]=, :add_class, :remove_class,
|
86
89
|
:text, :wrap, :children, :to_html, :remove_attribute,
|
87
|
-
:key?, :classes, :path, :inner_html
|
90
|
+
:key?, :classes, :path, :inner_html=, :add_previous_sibling,
|
91
|
+
:preceded_by_text?
|
88
92
|
|
89
93
|
# @!method config
|
90
94
|
# Get the config for this element's document
|
@@ -219,6 +223,22 @@ module Kitchen
|
|
219
223
|
self[:'data-type']
|
220
224
|
end
|
221
225
|
|
226
|
+
# Returns the element's href
|
227
|
+
#
|
228
|
+
# @return [String]
|
229
|
+
#
|
230
|
+
def href
|
231
|
+
self[:href]
|
232
|
+
end
|
233
|
+
|
234
|
+
# Sets the element's href
|
235
|
+
#
|
236
|
+
# @param value [String] the new value for the href
|
237
|
+
#
|
238
|
+
def href=(value)
|
239
|
+
self[:href] = value
|
240
|
+
end
|
241
|
+
|
222
242
|
# A way to set values and chain them
|
223
243
|
#
|
224
244
|
# @param property [String, Symbol] the name of the property to set
|
@@ -502,12 +522,12 @@ module Kitchen
|
|
502
522
|
Element.new(node: raw.parent, document: document, short_type: "parent(#{short_type})")
|
503
523
|
end
|
504
524
|
|
505
|
-
# returns previous element
|
506
|
-
#
|
525
|
+
# returns previous element sibling
|
526
|
+
# (only returns elements or nil)
|
507
527
|
# nil if there's no previous sibling
|
508
528
|
#
|
509
529
|
def previous
|
510
|
-
prev = raw.
|
530
|
+
prev = raw.previous_element
|
511
531
|
return prev if prev.nil?
|
512
532
|
|
513
533
|
Element.new(
|
@@ -754,7 +774,7 @@ module Kitchen
|
|
754
774
|
def_delegators :as_enumerator, :pages, :chapters, :terms, :figures, :notes, :tables, :examples,
|
755
775
|
:metadatas, :non_introduction_pages, :units, :titles, :exercises, :references,
|
756
776
|
:composite_pages, :composite_chapters, :solutions, :injected_questions,
|
757
|
-
:search_with, :sections
|
777
|
+
:search_with, :sections, :injected_exercises
|
758
778
|
|
759
779
|
# Returns this element as an enumerator (over only one element, itself)
|
760
780
|
#
|
@@ -336,6 +336,24 @@ module Kitchen
|
|
336
336
|
css_or_xpath: css_or_xpath, only: only, except: except)
|
337
337
|
end
|
338
338
|
|
339
|
+
# Returns an enumerator that iterates through injected exercises within the scope of this enumerator
|
340
|
+
#
|
341
|
+
# @param css_or_xpath [String] additional selectors to further narrow the element iterated over;
|
342
|
+
# a "$" in this argument will be replaced with the default selector for the element being
|
343
|
+
# iterated over.
|
344
|
+
# @param only [Symbol, Callable] the name of a method to call on an element or a
|
345
|
+
# lambda or proc that accepts an element; elements will only be included in the
|
346
|
+
# search results if the method or callable returns true
|
347
|
+
# @param except [Symbol, Callable] the name of a method to call on an element or a
|
348
|
+
# lambda or proc that accepts an element; elements will not be included in the
|
349
|
+
# search results if the method or callable returns false
|
350
|
+
#
|
351
|
+
def injected_exercises(css_or_xpath=nil, only: nil, except: nil)
|
352
|
+
block_error_if(block_given?)
|
353
|
+
chain_to(InjectedExerciseElementEnumerator,
|
354
|
+
css_or_xpath: css_or_xpath, only: only, except: except)
|
355
|
+
end
|
356
|
+
|
339
357
|
# Returns an enumerator that iterates within the scope of this enumerator
|
340
358
|
#
|
341
359
|
# @param css_or_xpath [String] additional selectors to further narrow the element iterated over
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kitchen
|
4
|
+
# An element for an example
|
5
|
+
#
|
6
|
+
class InjectedExerciseElement < ElementBase
|
7
|
+
|
8
|
+
# Creates a new +InjectedQuestionElement+
|
9
|
+
#
|
10
|
+
# @param node [Nokogiri::XML::Node] the node this element wraps
|
11
|
+
# @param document [Document] this element's document
|
12
|
+
#
|
13
|
+
def initialize(node:, document: nil)
|
14
|
+
super(node: node,
|
15
|
+
document: document,
|
16
|
+
enumerator_class: InjectedExerciseElementEnumerator)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Returns the short type
|
20
|
+
# @return [Symbol]
|
21
|
+
#
|
22
|
+
def self.short_type
|
23
|
+
:injected_exercise
|
24
|
+
end
|
25
|
+
|
26
|
+
# Returns the exercise context element.
|
27
|
+
# @return [Element]
|
28
|
+
#
|
29
|
+
def exercise_context
|
30
|
+
first("div[data-type='exercise-context']")
|
31
|
+
end
|
32
|
+
|
33
|
+
# Returns the exercise question element.
|
34
|
+
# @return [Element]
|
35
|
+
#
|
36
|
+
def exercise_question
|
37
|
+
first("div[data-type='exercise-question']")
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kitchen
|
4
|
+
# An enumerator for example elements
|
5
|
+
#
|
6
|
+
class InjectedExerciseElementEnumerator < ElementEnumeratorBase
|
7
|
+
|
8
|
+
# Returns a factory for this enumerator
|
9
|
+
#
|
10
|
+
# @return [ElementEnumeratorFactory]
|
11
|
+
#
|
12
|
+
def self.factory
|
13
|
+
ElementEnumeratorFactory.new(
|
14
|
+
default_css_or_xpath: Selector.named(:injected_exercise),
|
15
|
+
sub_element_class: InjectedExerciseElement,
|
16
|
+
enumerator_class: self
|
17
|
+
)
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
@@ -55,6 +55,13 @@ module Kitchen
|
|
55
55
|
first("div[data-type='question-solution']")
|
56
56
|
end
|
57
57
|
|
58
|
+
# Returns the exercise context element.
|
59
|
+
# @return [Element]
|
60
|
+
#
|
61
|
+
def exercise_context_in_question
|
62
|
+
first("div[data-type='exercise-context']")
|
63
|
+
end
|
64
|
+
|
58
65
|
# Returns the answer correctness given an alphabet
|
59
66
|
#
|
60
67
|
# @return [Array]
|
data/lib/kitchen/patches/i18n.rb
CHANGED
@@ -4,6 +4,10 @@ require 'twitter_cldr'
|
|
4
4
|
|
5
5
|
# rubocop:disable Style/Documentation
|
6
6
|
module I18n
|
7
|
+
def self.character_to_group(character)
|
8
|
+
I18n.locale == :pl ? character : I18n.transliterate(character)
|
9
|
+
end
|
10
|
+
|
7
11
|
def self.sort_strings(first, second)
|
8
12
|
string_sorter.compare(first, second)
|
9
13
|
end
|
@@ -3,7 +3,22 @@
|
|
3
3
|
# Monkey patches for +Integer+
|
4
4
|
#
|
5
5
|
class Integer
|
6
|
-
|
6
|
+
|
7
|
+
@roman_numerals = {
|
8
|
+
100 => 'c',
|
9
|
+
90 => 'xc',
|
10
|
+
50 => 'l',
|
11
|
+
40 => 'xl',
|
12
|
+
10 => 'x',
|
13
|
+
9 => 'ix',
|
14
|
+
5 => 'v',
|
15
|
+
4 => 'iv',
|
16
|
+
1 => 'i'
|
17
|
+
}
|
18
|
+
|
19
|
+
class << self
|
20
|
+
attr_accessor :roman_numerals
|
21
|
+
end
|
7
22
|
|
8
23
|
# Formats as different types of integers, including roman numerals.
|
9
24
|
#
|
@@ -14,11 +29,25 @@ class Integer
|
|
14
29
|
when :arabic
|
15
30
|
to_s
|
16
31
|
when :roman
|
17
|
-
raise 'Unknown conversion to Roman numerals' if self
|
32
|
+
raise 'Unknown conversion to Roman numerals' if self > self.class.roman_numerals.keys.first
|
18
33
|
|
19
|
-
|
34
|
+
to_roman
|
20
35
|
else
|
21
36
|
raise 'Unknown integer format'
|
22
37
|
end
|
23
38
|
end
|
39
|
+
|
40
|
+
def to_roman
|
41
|
+
return 0 if zero?
|
42
|
+
|
43
|
+
roman = ''
|
44
|
+
integer = self
|
45
|
+
self.class.roman_numerals.each do |number, letter|
|
46
|
+
until integer < number
|
47
|
+
roman += letter
|
48
|
+
integer -= number
|
49
|
+
end
|
50
|
+
end
|
51
|
+
roman
|
52
|
+
end
|
24
53
|
end
|
@@ -67,11 +67,12 @@ module Nokogiri
|
|
67
67
|
self[:class]&.split || []
|
68
68
|
end
|
69
69
|
|
70
|
-
def
|
71
|
-
prev =
|
72
|
-
|
70
|
+
def preceded_by_text?
|
71
|
+
prev = previous_sibling
|
72
|
+
while !prev.nil? && prev.blank? do prev = prev.previous_sibling end
|
73
|
+
return false if prev.nil?
|
73
74
|
|
74
|
-
prev.text?
|
75
|
+
prev.text?
|
75
76
|
end
|
76
77
|
|
77
78
|
def self.selector_to_css_nodes(selector)
|
data/lib/kitchen/version.rb
CHANGED
data/lib/locales/en.yml
CHANGED
@@ -28,6 +28,7 @@ en:
|
|
28
28
|
iframe_link_text: Click to view content
|
29
29
|
handbook_outline_title: Outline
|
30
30
|
context_lead_text: 'Refer to '
|
31
|
+
lo_label_text: 'LO '
|
31
32
|
eoc:
|
32
33
|
glossary: Key Terms
|
33
34
|
key-equations: Key Equations
|
@@ -45,3 +46,18 @@ en:
|
|
45
46
|
reference: References
|
46
47
|
index:
|
47
48
|
main: Index
|
49
|
+
annotation_icons:
|
50
|
+
linguistic-icon:
|
51
|
+
title: Language lens icon
|
52
|
+
culture-icon:
|
53
|
+
title: Culture lens icon
|
54
|
+
dreaming-icon:
|
55
|
+
title: Generating and capturing ideas icon
|
56
|
+
visual-icon:
|
57
|
+
title: Visual learning style icon
|
58
|
+
speech-icon:
|
59
|
+
title: Voice to text icon
|
60
|
+
auditory-icon:
|
61
|
+
title: Auditory learning style icon
|
62
|
+
kinesthetic-icon:
|
63
|
+
title: Kinesthetic learning style icon
|
data/lib/locales/es.yml
CHANGED
@@ -21,17 +21,22 @@ es:
|
|
21
21
|
eoc_exercises_title: Ejercicios
|
22
22
|
eoc_composite_metadata_title: Revisión Del Capítulo
|
23
23
|
eoc_solutions_title: Soluciones
|
24
|
-
eoc_key_concepts: Conceptos Clave
|
25
24
|
eoc_suggested_reading: Sugerencias Para Estudios Adicionales
|
26
25
|
eob_index_symbols_group: Símbolos
|
27
26
|
review_exercises: Ejercicios De Repaso
|
28
27
|
section_exercises: ! 'Sección %{number} Ejercicios'
|
28
|
+
iframe_link_text: Haga Clic Para Ver El Contenido
|
29
|
+
handbook_outline_title: Esquema
|
30
|
+
context_lead_text: 'Referirse a '
|
29
31
|
eoc:
|
30
32
|
glossary: Términos Clave
|
31
33
|
key-equations: Ecuaciones Clave
|
32
34
|
summary: Resumen
|
33
35
|
further-research: Investigación Adicional
|
36
|
+
key-concepts: Conceptos Clave
|
34
37
|
references: Referencias
|
38
|
+
solutions: Soluciones
|
39
|
+
section-exercises: ! 'Sección %{number} Ejercicios'
|
35
40
|
folio:
|
36
41
|
preface: Prefacio
|
37
42
|
access_for_free: Acceso gratis en openstax.org
|