Class: Kitchen::ElementBase
Overview
Abstract base class for all elements. If you are looking for a simple concrete element class, use ‘Element`.
Direct Known Subclasses
BookElement, ChapterElement, CompositeChapterElement, CompositePageElement, Element, ExampleElement, ExerciseElement, FigureElement, ImageElement, InjectedExerciseElement, InjectedQuestionElement, MetadataElement, NoteElement, PageElement, ReferenceElement, SectionElement, TableElement, TermElement, UnitElement
Instance Attribute Summary collapse
-
#ancestors ⇒ Array<Ancestor>
readonly
Returns the element’s ancestors.
-
#document(: clipboard) ⇒ Clipboard
readonly
Access the clipboard for this element’s document.
-
#enumerator_class ⇒ Class
readonly
The enumerator class for this element.
-
#search_query_that_found_me ⇒ SearchQuery
The search query that located this element in the DOM.
-
#short_type ⇒ Symbol, String
readonly
The element’s type, e.g.
Class Method Summary collapse
-
.descendant(type) ⇒ Class
Returns ElementBase descendent type or nil if none found.
-
.descendant!(type) ⇒ Class
Returns ElementBase descendent type or Error if none found.
-
.is_the_element_class_for?(node, config:) ⇒ Boolean
Returns true if this class represents the element for the given node.
Instance Method Summary collapse
-
#[] ⇒ String
Get an element attribute.
-
#[]= ⇒ Object
Set an element attribute.
-
#add_ancestor(ancestor) ⇒ Object
Adds one ancestor, incrementing its descendant counts for this element type.
-
#add_ancestors(*args) ⇒ Object
Adds ancestors to this element, for each incrementing descendant counts for this type.
-
#add_class ⇒ Object
Add a class to the element.
- #add_platform_media(format) ⇒ Object
-
#ancestor(type) ⇒ Ancestor
Returns this element’s ancestor of the given type.
-
#ancestor_elements ⇒ Array<ElementBase>
Return the elements in all of the ancestors.
-
#append(child: nil, sibling: nil) ⇒ Object
If child argument given, appends it after the element’s current children.
-
#as_enumerator ⇒ ElementEnumeratorBase
Returns this element as an enumerator (over only one element, itself).
-
#children ⇒ Nokogiri::XML::NodeSet
Get the element’s children.
-
#classes ⇒ Array<String>
Gets the element’s classes.
-
#clone ⇒ Object
Returns a clone of this object.
-
#config ⇒ Config
Get the config for this element’s document.
-
#contains?(*selector_or_xpath_args) ⇒ Boolean
Returns true if this element has a child matching the provided selector.
-
#contains_blockish? ⇒ Boolean
Returns true if the current element contains blockish descendants.
-
#content(*selector_or_xpath_args) ⇒ String
Get the content of children matching the provided selector.
-
#copy(to: nil) ⇒ Element
Makes a copy of the element and places it on the specified clipboard.
-
#count_in(ancestor_type) ⇒ Object
Returns the count of this element’s type in the given ancestor type.
-
#custom_target_label_for_modules(custom_title_content: nil, custom_number_content: nil) ⇒ Pantry
Creates labels for links to modules (used only in accounting where LO link labels are also present and utilizes only the number).
-
#cut(to: nil) ⇒ Element
Removes the element from its parent and places it on the specified clipboard.
-
#data_sm ⇒ String
Returns the element’s data-sm.
-
#data_sm_formatted ⇒ String
function to translate phil’s notation (modules/m53650/index.cnxml:6:3) (6 Line, 3 col) to M123:L456:C789.
-
#data_source ⇒ String
Returns the element’s data source map in M123:L456:C789 style and whether this is self or the nearest parent’s sm.
-
#data_type ⇒ String
Returns the element’s data-type.
-
#element_children ⇒ TypeCastingElementEnumerator
Returns an enumerator over the direct child elements of this element, with the specific type (e.g.
TermElement
) if such type is available. -
#first(*selector_or_xpath_args, reload: false) {|the| ... } ⇒ Element?
(also: #at)
Yields and returns the first child element that matches the provided selector or XPath arguments.
-
#first!(*selector_or_xpath_args, reload: false) {|the| ... } ⇒ Element
Yields and returns the first child element that matches the provided selector or XPath arguments.
-
#has_ancestor?(type) ⇒ Boolean
Returns true iff this element has an ancestor of the given type.
-
#has_class?(klass) ⇒ Boolean
Returns true if this element has the given class.
-
#href ⇒ String
Returns the element’s href.
-
#href=(value) ⇒ Object
Sets the element’s href.
-
#id ⇒ String
Returns the element’s ID.
-
#id=(value) ⇒ Object
Sets the element’s ID.
-
#initialize(node:, document:, enumerator_class:, short_type: nil) ⇒ ElementBase
constructor
Creates a new instance.
-
#inner_html= ⇒ Object
Set the inner HTML for this element.
-
#inspect ⇒ String
Returns a string version of this element.
-
#is?(type) ⇒ Boolean
Returns true if this element is the given type.
-
#key?(attribute) ⇒ Object
Returns true if attribute is set.
-
#mark_as_current_location! ⇒ Object
Mark the location so that if there’s an error we can show the developer where.
-
#name ⇒ String
Get the element name (the tag).
-
#name= ⇒ Object
Set the element name (the tag).
-
#pages ⇒ Object
Returns a pages enumerator.
-
#pantry ⇒ Pantry
Access the pantry for this element’s document.
- #parent ⇒ Object
-
#paste ⇒ Object
When an element is cut or copied, use this method to get the element’s content; keeps IDs unique.
-
#path ⇒ String
Get the path for this element.
-
#preceded_by_text ⇒ Object
Returns true if the immediately preceding sibling is text.
-
#prepend(child: nil, sibling: nil) ⇒ Object
If child argument given, prepends it before the element’s current children.
-
#previous ⇒ Object
returns previous element sibling (only returns elements or nil) nil if there’s no previous sibling.
-
#raw ⇒ Nokogiri::XML::Node
Returns the underlying Nokogiri object.
- #raw_search(*selector_or_xpath_args, reload: false) ⇒ Object
-
#remember_that_a_sub_element_was_counted(search_query, type) ⇒ Object
Track that a sub element found by the given query has been counted.
-
#remove_attribute ⇒ Object
Removes an attribute from the element.
-
#remove_class ⇒ Object
Remove a class from the element.
-
#replace_children(with:) ⇒ Object
Replaces this element’s children.
- #rex_link ⇒ Object
-
#say_source_or_nil ⇒ String
Gives Error-Ready Data Source Map message or nil if there’s no data_source.
-
#search(*selector_or_xpath_args, only: nil, except: nil, reload: false) ⇒ ElementEnumerator
Returns an ElementEnumerator that iterates over the provided selector or xpath queries.
-
#search_history ⇒ SearchHistory
Returns the search history that found this element.
-
#selectors ⇒ Selectors::Base
Get the selectors for this element’s document.
-
#set(property, value) ⇒ Object
A way to set values and chain them.
-
#sub_header_name ⇒ String
Returns the header tag name that is one level under the first header tag in this element, e.g.
-
#target_label(label_text: nil, custom_content: nil, cases: false, label_class: nil) ⇒ Pantry
Creates labels for links to inside elements like Figures, Tables, Equations, Exercises, Notes, Appendices.
-
#text ⇒ String
Get the element text.
-
#to_html ⇒ String
Get the element as HTML.
-
#to_s ⇒ String
Returns a string version of this element.
-
#to_xhtml ⇒ String
Returns a string version of this element as XHTML.
-
#to_xml ⇒ String
Returns a string version of this element as XML.
-
#trash ⇒ Object
Delete the element.
-
#uncount(search_query) ⇒ Object
Undo the counts from a prior search query (so that they can be counted again).
-
#wrap ⇒ Nokogiri::XML::Node
Add HTML around this element.
-
#wrap_children(name = 'div', attributes = {}) {|the| ... } ⇒ Element
Wraps the element’s children in a new element.
Methods included from Mixins::BlockErrorIf
Constructor Details
#initialize(node:, document:, enumerator_class:, short_type: nil) ⇒ ElementBase
Creates a new instance
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 |
# File 'lib/kitchen/element_base.rb', line 120 def initialize(node:, document:, enumerator_class:, short_type: nil) raise(ArgumentError, 'node cannot be nil') if node.nil? @node = node raise(ArgumentError, 'enumerator_class cannot be nil') if enumerator_class.nil? @enumerator_class = enumerator_class @short_type = short_type || self.class.try(:short_type) || "unknown_type_#{SecureRandom.hex(4)}" @document = case document when Kitchen::Document document else raise(ArgumentError, '`document` is not a known document type') end @ancestors = HashWithIndifferentAccess.new @search_query_matches_that_have_been_counted = {} @is_a_clone = false @search_cache = {} end |
Instance Attribute Details
#ancestors ⇒ Array<Ancestor> (readonly)
Returns the element’s ancestors
331 332 333 |
# File 'lib/kitchen/element_base.rb', line 331 def ancestors @ancestors end |
#document(: clipboard) ⇒ Clipboard (readonly)
Access the clipboard for this element’s document
17 18 19 |
# File 'lib/kitchen/element_base.rb', line 17 def document @document end |
#enumerator_class ⇒ Class (readonly)
The enumerator class for this element
25 26 27 |
# File 'lib/kitchen/element_base.rb', line 25 def enumerator_class @enumerator_class end |
#search_query_that_found_me ⇒ SearchQuery
The search query that located this element in the DOM
29 30 31 |
# File 'lib/kitchen/element_base.rb', line 29 def search_query_that_found_me @search_query_that_found_me end |
#short_type ⇒ Symbol, String (readonly)
The element’s type, e.g. :page
21 22 23 |
# File 'lib/kitchen/element_base.rb', line 21 def short_type @short_type end |
Class Method Details
.descendant(type) ⇒ Class
Returns ElementBase descendent type or nil if none found
152 153 154 155 156 157 158 159 160 161 |
# File 'lib/kitchen/element_base.rb', line 152 def self.descendant(type) @types_to_descendants ||= descendants.each_with_object({}) do |descendant, hash| next unless descendant.try(:short_type) hash[descendant.short_type] = descendant end @types_to_descendants[type] end |
.descendant!(type) ⇒ Class
Returns ElementBase descendent type or Error if none found
169 170 171 |
# File 'lib/kitchen/element_base.rb', line 169 def self.descendant!(type) descendant(type) || raise("Unknown ElementBase descendant type '#{type}'") end |
Instance Method Details
#[] ⇒ String
Get an element attribute
88 89 90 91 |
# File 'lib/kitchen/element_base.rb', line 88 def_delegators :@node, :name=, :name, :[], :[]=, :add_class, :remove_class, :text, :wrap, :children, :to_html, :remove_attribute, :key?, :classes, :path, :inner_html=, :add_previous_sibling, :preceded_by_text? |
#[]= ⇒ Object
Set an element attribute
88 89 90 91 |
# File 'lib/kitchen/element_base.rb', line 88 def_delegators :@node, :name=, :name, :[], :[]=, :add_class, :remove_class, :text, :wrap, :children, :to_html, :remove_attribute, :key?, :classes, :path, :inner_html=, :add_previous_sibling, :preceded_by_text? |
#add_ancestor(ancestor) ⇒ Object
Adds one ancestor, incrementing its descendant counts for this element type
359 360 361 362 363 364 365 366 367 368 |
# File 'lib/kitchen/element_base.rb', line 359 def add_ancestor(ancestor) if @ancestors[ancestor.type].present? raise "Trying to add an ancestor of type '#{ancestor.type}' but one of that " \ "type is already present" \ "#{say_source_or_nil}" end ancestor.increment_descendant_count(short_type) @ancestors[ancestor.type] = ancestor end |
#add_ancestors(*args) ⇒ Object
Adds ancestors to this element, for each incrementing descendant counts for this type
339 340 341 342 343 344 345 346 347 348 349 350 351 352 |
# File 'lib/kitchen/element_base.rb', line 339 def add_ancestors(*args) args.each do |arg| case arg when Hash add_ancestors(*arg.values) when Ancestor add_ancestor(arg) when Element, Document add_ancestor(Ancestor.new(arg)) else raise "Unsupported ancestor type `#{arg.class}`" end end end |
#add_class ⇒ Object
Add a class to the element
88 89 90 91 |
# File 'lib/kitchen/element_base.rb', line 88 def_delegators :@node, :name=, :name, :[], :[]=, :add_class, :remove_class, :text, :wrap, :children, :to_html, :remove_attribute, :key?, :classes, :path, :inner_html=, :add_previous_sibling, :preceded_by_text? |
#add_platform_media(format) ⇒ Object
951 952 953 954 955 956 957 958 959 960 961 |
# File 'lib/kitchen/element_base.rb', line 951 def add_platform_media(format) valid_formats = %w[screen print screenreader] raise "Media format invalid; valid formats are #{valid_formats}" unless valid_formats.include?(format) self[:'data-media'] = \ if self[:'data-media'] self[:'data-media'].split.to_set.add(format).to_a.join(' ') else format end end |
#ancestor(type) ⇒ Ancestor
Returns this element’s ancestor of the given type
313 314 315 316 |
# File 'lib/kitchen/element_base.rb', line 313 def ancestor(type) @ancestors[type.to_sym]&.element || raise("No ancestor of type '#{type}'" \ "#{say_source_or_nil}") end |
#ancestor_elements ⇒ Array<ElementBase>
Return the elements in all of the ancestors
374 375 376 |
# File 'lib/kitchen/element_base.rb', line 374 def ancestor_elements @ancestors.values.map(&:element) end |
#append(child: nil, sibling: nil) ⇒ Object
If child argument given, appends it after the element’s current children. If sibling is given, appends it as a sibling to this element.
660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 |
# File 'lib/kitchen/element_base.rb', line 660 def append(child: nil, sibling: nil) require_one_of_child_or_sibling(child, sibling) if child if node.children.empty? node.children = child.to_s else node.add_child(child) end else node.next = sibling end self end |
#as_enumerator ⇒ ElementEnumeratorBase
Returns this element as an enumerator (over only one element, itself)
914 915 916 |
# File 'lib/kitchen/element_base.rb', line 914 def as_enumerator enumerator_class.new(search_query: search_query_that_found_me) { |block| block.yield(self) } end |
#children ⇒ Nokogiri::XML::NodeSet
Get the element’s children
88 89 90 91 |
# File 'lib/kitchen/element_base.rb', line 88 def_delegators :@node, :name=, :name, :[], :[]=, :add_class, :remove_class, :text, :wrap, :children, :to_html, :remove_attribute, :key?, :classes, :path, :inner_html=, :add_previous_sibling, :preceded_by_text? |
#classes ⇒ Array<String>
Gets the element’s classes
88 89 90 91 |
# File 'lib/kitchen/element_base.rb', line 88 def_delegators :@node, :name=, :name, :[], :[]=, :add_class, :remove_class, :text, :wrap, :children, :to_html, :remove_attribute, :key?, :classes, :path, :inner_html=, :add_previous_sibling, :preceded_by_text? |
#clone ⇒ Object
Returns a clone of this object
801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 |
# File 'lib/kitchen/element_base.rb', line 801 def clone super.tap do |element| # Define all namespaces that are used inside the node being cloned. # # When document is created, all namespaces definitions from the book # are collected and added to the html tag. # In the moment when clipboard is pasted and converted to string # we are loosing information about namespaces defined on html ancestor. # # It's important to add namespace definition directly to the node that has a namespace. # Adding it just to the node being cloned is breaking elements that have # namespaces without prefix (e.g <math xmlns="http://www.w3.org/1998/Math/MathML"/>) raw.traverse do |node| node.attribute_nodes.each do |attr| next unless attr.namespace unless node.has_namespace_defined_on_ancestor?(attribute: attr) node.add_namespace(attr.namespace.prefix, attr.namespace.href) end end next unless node.namespace unless node.has_namespace_defined_on_ancestor? node.add_namespace(node.namespace.prefix, node.namespace.href) end end # When we call dup, the dup gets a bunch of default namespace stuff that # the original doesn't have. Why? Unclear, but hard to get rid of nicely. # So here we mark that the element is a clone and then all of the `to_s`-like # methods gsub out the default namespace gunk. Clones are mostly used for # clipboards and are accessed using `paste` methods, so modifying the `to_s` # behavior works for us. If we end up using `clone` in a way that doesn't # eventually get converted to string, we may have to investigate other # options. # # An alternative is to remove the `xmlns` attribute in the `html` tag before # the input file is parse into a Nokogiri document and then to add it back # in when the baked file is written out. # # Nokogiri::XML::Document.remove_namespaces! is not an option because that blows # away our MathML namespace. # # I may not fully understand why the extra default namespace stuff is happening # FWIW :-) element.node = node.dup element.is_a_clone = true end end |
#config ⇒ Config
Get the config for this element’s document
96 |
# File 'lib/kitchen/element_base.rb', line 96 def_delegators :document, :config |
#contains?(*selector_or_xpath_args) ⇒ Boolean
Returns true if this element has a child matching the provided selector
731 732 733 |
# File 'lib/kitchen/element_base.rb', line 731 def contains?(*selector_or_xpath_args) !node.at(*selector_or_xpath_args).nil? end |
#contains_blockish? ⇒ Boolean
Returns true if the current element contains blockish descendants
458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 |
# File 'lib/kitchen/element_base.rb', line 458 def contains_blockish? # Block-level elements: https://developer.mozilla.org/en-US/docs/Web/HTML/Block-level_elements block_level = %w[ address article aside blockquote details dialog dd div dl dt fieldset figcaption figure footer form h1 h2 h3 h4 h5 h6 header hgroup hr li main nav ol p pre section table ul ] search(block_level.join(',')).to_a.length.positive? end |
#content(*selector_or_xpath_args) ⇒ String
Get the content of children matching the provided selector. Mostly useful when there is one child with text you want to extract.
722 723 724 |
# File 'lib/kitchen/element_base.rb', line 722 def content(*selector_or_xpath_args) node.search(*selector_or_xpath_args).children.to_s end |
#copy(to: nil) ⇒ Element
Makes a copy of the element and places it on the specified clipboard.
571 572 573 574 575 576 577 578 579 580 581 582 583 |
# File 'lib/kitchen/element_base.rb', line 571 def copy(to: nil) # See `clone` method for a note about namespaces block_error_if(block_given?) the_copy = clone the_copy.raw.traverse do |node| next if node.text? || node.document? id_tracker.record_id_copied(node[:id]) end get_clipboard(to).add(the_copy) if to.present? the_copy end |
#count_in(ancestor_type) ⇒ Object
Returns the count of this element’s type in the given ancestor type
382 383 384 385 386 |
# File 'lib/kitchen/element_base.rb', line 382 def count_in(ancestor_type) @ancestors[ancestor_type]&.get_descendant_count(short_type) || raise("No ancestor of type '#{ancestor_type}'#{say_source_or_nil}" ) end |
#custom_target_label_for_modules(custom_title_content: nil, custom_number_content: nil) ⇒ Pantry
Creates labels for links to modules (used only in accounting where LO link labels are also present and utilizes only the number)
895 896 897 898 899 900 901 |
# File 'lib/kitchen/element_base.rb', line 895 def custom_target_label_for_modules(custom_title_content: nil, custom_number_content: nil) element_label = <<~HTML.chomp <span class="label-counter">#{custom_number_content}</span><span class="title-label-text">#{custom_title_content}</span> HTML pantry(name: :link_text).store element_label, label: id if id end |
#cut(to: nil) ⇒ Element
Removes the element from its parent and places it on the specified clipboard
551 552 553 554 555 556 557 558 559 560 561 562 |
# File 'lib/kitchen/element_base.rb', line 551 def cut(to: nil) block_error_if(block_given?) raw.traverse do |node| next if node.text? || node.document? id_tracker.record_id_cut(node[:id]) end node.remove get_clipboard(to).add(self) if to.present? self end |
#data_sm ⇒ String
Returns the element’s data-sm
246 247 248 |
# File 'lib/kitchen/element_base.rb', line 246 def data_sm self[:'data-sm'] end |
#data_sm_formatted ⇒ String
function to translate phil’s notation (modules/m53650/index.cnxml:6:3) (6 Line, 3 col) to M123:L456:C789
254 255 256 257 258 259 260 |
# File 'lib/kitchen/element_base.rb', line 254 def data_sm_formatted format_match = /\/m(\d+)\/[^:]+:(\d+):(\d+)/ module_line_column = data_sm.match(format_match).captures "(self) M#{module_line_column[0]}:L#{module_line_column[1]}:C#{module_line_column[2]}" end |
#data_source ⇒ String
Returns the element’s data source map in M123:L456:C789 style and whether this is self or the nearest parent’s sm
266 267 268 269 270 271 272 273 274 275 276 277 278 279 |
# File 'lib/kitchen/element_base.rb', line 266 def data_source return nil if !parent&.name || parent&.name == 'html' if data_sm.nil? && parent&.name != 'html' parent_source = parent.data_source return nil if parent_source.nil? "(nearest parent) #{parent_source.split(' ')[1]}" unless parent_source.match(/\(nearest parent\)/) else data_sm_formatted end end |
#data_type ⇒ String
Returns the element’s data-type
222 223 224 |
# File 'lib/kitchen/element_base.rb', line 222 def data_type self[:'data-type'] end |
#element_children ⇒ TypeCastingElementEnumerator
Returns an enumerator over the direct child elements of this element, with the specific type (e.g. TermElement
) if such type is available.
536 537 538 539 540 541 542 |
# File 'lib/kitchen/element_base.rb', line 536 def element_children block_error_if(block_given?) TypeCastingElementEnumerator.factory.build_within( self, search_query: SearchQuery.new(css_or_xpath: './*') ) end |
#first(*selector_or_xpath_args, reload: false) {|the| ... } ⇒ Element? Also known as: at
Yields and returns the first child element that matches the provided selector or XPath arguments.
506 507 508 509 510 |
# File 'lib/kitchen/element_base.rb', line 506 def first(*selector_or_xpath_args, reload: false) search(*selector_or_xpath_args, reload: reload).first.tap do |element| yield(element) if block_given? end end |
#first!(*selector_or_xpath_args, reload: false) {|the| ... } ⇒ Element
Yields and returns the first child element that matches the provided selector or XPath arguments.
521 522 523 524 525 |
# File 'lib/kitchen/element_base.rb', line 521 def first!(*selector_or_xpath_args, reload: false) search(*selector_or_xpath_args, reload: reload).first!.tap do |element| yield(element) if block_given? end end |
#has_ancestor?(type) ⇒ Boolean
Returns true iff this element has an ancestor of the given type
323 324 325 |
# File 'lib/kitchen/element_base.rb', line 323 def has_ancestor?(type) @ancestors[type.to_sym].present? end |
#has_class?(klass) ⇒ Boolean
Returns true if this element has the given class
198 199 200 |
# File 'lib/kitchen/element_base.rb', line 198 def has_class?(klass) (self[:class] || '').include?(klass) end |
#href ⇒ String
Returns the element’s href
230 231 232 |
# File 'lib/kitchen/element_base.rb', line 230 def href self[:href] end |
#href=(value) ⇒ Object
Sets the element’s href
238 239 240 |
# File 'lib/kitchen/element_base.rb', line 238 def href=(value) self[:href] = value end |
#id ⇒ String
Returns the element’s ID
206 207 208 |
# File 'lib/kitchen/element_base.rb', line 206 def id self[:id] end |
#id=(value) ⇒ Object
Sets the element’s ID
214 215 216 |
# File 'lib/kitchen/element_base.rb', line 214 def id=(value) self[:id] = value end |
#inner_html= ⇒ Object
Set the inner HTML for this element
88 89 90 91 |
# File 'lib/kitchen/element_base.rb', line 88 def_delegators :@node, :name=, :name, :[], :[]=, :add_class, :remove_class, :text, :wrap, :children, :to_html, :remove_attribute, :key?, :classes, :path, :inner_html=, :add_previous_sibling, :preceded_by_text? |
#inspect ⇒ String
Returns a string version of this element
771 772 773 |
# File 'lib/kitchen/element_base.rb', line 771 def inspect to_s end |
#is?(type) ⇒ Boolean
Returns true if this element is the given type
179 180 181 |
# File 'lib/kitchen/element_base.rb', line 179 def is?(type) ElementBase.descendant!(type).is_the_element_class_for?(raw, config: config) end |
#key?(attribute) ⇒ Object
Returns true if attribute is set
88 89 90 91 |
# File 'lib/kitchen/element_base.rb', line 88 def_delegators :@node, :name=, :name, :[], :[]=, :add_class, :remove_class, :text, :wrap, :children, :to_html, :remove_attribute, :key?, :classes, :path, :inner_html=, :add_previous_sibling, :preceded_by_text? |
#mark_as_current_location! ⇒ Object
Mark the location so that if there’s an error we can show the developer where.
755 756 757 |
# File 'lib/kitchen/element_base.rb', line 755 def mark_as_current_location! document.location = self end |
#name ⇒ String
Get the element name (the tag)
88 89 90 91 |
# File 'lib/kitchen/element_base.rb', line 88 def_delegators :@node, :name=, :name, :[], :[]=, :add_class, :remove_class, :text, :wrap, :children, :to_html, :remove_attribute, :key?, :classes, :path, :inner_html=, :add_previous_sibling, :preceded_by_text? |
#name= ⇒ Object
Set the element name (the tag)
88 89 90 91 |
# File 'lib/kitchen/element_base.rb', line 88 def_delegators :@node, :name=, :name, :[], :[]=, :add_class, :remove_class, :text, :wrap, :children, :to_html, :remove_attribute, :key?, :classes, :path, :inner_html=, :add_previous_sibling, :preceded_by_text? |
#pages ⇒ Object
Returns a pages enumerator
905 906 907 908 |
# File 'lib/kitchen/element_base.rb', line 905 def_delegators :as_enumerator, :pages, :chapters, :terms, :figures, :notes, :tables, :examples, :metadatas, :non_introduction_pages, :units, :titles, :exercises, :references, :composite_pages, :composite_chapters, :solutions, :injected_questions, :search_with, :sections, :injected_exercises, :images |
#pantry ⇒ Pantry
Access the pantry for this element’s document
109 |
# File 'lib/kitchen/element_base.rb', line 109 def_delegators :document, :pantry, :clipboard |
#parent ⇒ Object
609 610 611 |
# File 'lib/kitchen/element_base.rb', line 609 def parent Element.new(node: raw.parent, document: document, short_type: "parent(#{short_type})") end |
#paste ⇒ Object
When an element is cut or copied, use this method to get the element’s content; keeps IDs unique
587 588 589 590 591 592 593 594 595 596 597 598 599 600 |
# File 'lib/kitchen/element_base.rb', line 587 def paste # See `clone` method for a note about namespaces block_error_if(block_given?) temp_copy = clone temp_copy.raw.traverse do |node| next if node.text? || node.document? if node[:id].present? id_tracker.record_id_pasted(node[:id]) node.delete('id') unless id_tracker.first_id?(node[:id]) end end temp_copy.to_s end |
#path ⇒ String
Get the path for this element
88 89 90 91 |
# File 'lib/kitchen/element_base.rb', line 88 def_delegators :@node, :name=, :name, :[], :[]=, :add_class, :remove_class, :text, :wrap, :children, :to_html, :remove_attribute, :key?, :classes, :path, :inner_html=, :add_previous_sibling, :preceded_by_text? |
#preceded_by_text ⇒ Object
Returns true if the immediately preceding sibling is text
88 89 90 91 |
# File 'lib/kitchen/element_base.rb', line 88 def_delegators :@node, :name=, :name, :[], :[]=, :add_class, :remove_class, :text, :wrap, :children, :to_html, :remove_attribute, :key?, :classes, :path, :inner_html=, :add_previous_sibling, :preceded_by_text? |
#prepend(child: nil, sibling: nil) ⇒ Object
If child argument given, prepends it before the element’s current children. If sibling is given, prepends it as a sibling to this element.
637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 |
# File 'lib/kitchen/element_base.rb', line 637 def prepend(child: nil, sibling: nil) require_one_of_child_or_sibling(child, sibling) if child if node.children.empty? node.children = child.to_s else node.children.first.add_previous_sibling(child) end else node.add_previous_sibling(sibling) end self end |
#previous ⇒ Object
returns previous element sibling (only returns elements or nil) nil if there’s no previous sibling
617 618 619 620 621 622 623 624 625 626 |
# File 'lib/kitchen/element_base.rb', line 617 def previous prev = raw.previous_element return prev if prev.nil? Element.new( node: prev, document: document, short_type: "previous(#{short_type})" ) end |
#raw ⇒ Nokogiri::XML::Node
Returns the underlying Nokogiri object
763 764 765 |
# File 'lib/kitchen/element_base.rb', line 763 def raw node end |
#raw_search(*selector_or_xpath_args, reload: false) ⇒ Object
446 447 448 449 450 451 452 |
# File 'lib/kitchen/element_base.rb', line 446 def raw_search(*selector_or_xpath_args, reload: false) key = selector_or_xpath_args @search_cache[key] = nil if reload || !config.enable_search_cache # cache nil search results with a fake -1 value @search_cache[key] ||= raw.search(*selector_or_xpath_args) || -1 @search_cache[key] == -1 ? nil : @search_cache[key] end |
#remember_that_a_sub_element_was_counted(search_query, type) ⇒ Object
Track that a sub element found by the given query has been counted
393 394 395 396 |
# File 'lib/kitchen/element_base.rb', line 393 def remember_that_a_sub_element_was_counted(search_query, type) @search_query_matches_that_have_been_counted[search_query.to_s] ||= Hash.new(0) @search_query_matches_that_have_been_counted[search_query.to_s][type] += 1 end |
#remove_attribute ⇒ Object
Removes an attribute from the element
88 89 90 91 |
# File 'lib/kitchen/element_base.rb', line 88 def_delegators :@node, :name=, :name, :[], :[]=, :add_class, :remove_class, :text, :wrap, :children, :to_html, :remove_attribute, :key?, :classes, :path, :inner_html=, :add_previous_sibling, :preceded_by_text? |
#remove_class ⇒ Object
Remove a class from the element
88 89 90 91 |
# File 'lib/kitchen/element_base.rb', line 88 def_delegators :@node, :name=, :name, :[], :[]=, :add_class, :remove_class, :text, :wrap, :children, :to_html, :remove_attribute, :key?, :classes, :path, :inner_html=, :add_previous_sibling, :preceded_by_text? |
#replace_children(with:) ⇒ Object
Replaces this element’s children
680 681 682 683 |
# File 'lib/kitchen/element_base.rb', line 680 def replace_children(with:) node.children = with self end |
#rex_link ⇒ Object
918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 |
# File 'lib/kitchen/element_base.rb', line 918 def rex_link self[:'data-is-for-rex-linking'] = 'true' element_with_ancestors = document.book.chapters.search_with( Kitchen::PageElementEnumerator, Kitchen::CompositePageElementEnumerator ).search('[data-is-for-rex-linking="true"]').first remove_attribute('data-is-for-rex-linking') unless element_with_ancestors raise("Cannot create rex link to element #{self} - needs ancestors of both types chapter & page/composite_page" \ "#{say_source_or_nil}" ) end book_slug = document.slug chapter_count = element_with_ancestors.ancestor(:chapter).count_in(:book) page_string = '' page_title = '' page = element_with_ancestors.ancestor(:page) if element_with_ancestors.has_ancestor?(:page) if page&.is_introduction? page_title = page.first('[data-type="document-title"]').text.kebab_case elsif page page_string = "#{page.count_in(:chapter) - 1}-" page_title = page.title_text.kebab_case else page = element_with_ancestors.ancestor(:composite_page) page_title = page.title.text.kebab_case end "https://openstax.org/books/#{book_slug}/pages/#{chapter_count}-#{page_string}#{page_title}" end |
#say_source_or_nil ⇒ String
Gives Error-Ready Data Source Map message or nil if there’s no data_source
285 286 287 |
# File 'lib/kitchen/element_base.rb', line 285 def say_source_or_nil "#{data_source ? "\nCNXML SOURCE: " : nil}#{data_source}" end |
#search(*selector_or_xpath_args, only: nil, except: nil, reload: false) ⇒ ElementEnumerator
Returns an ElementEnumerator that iterates over the provided selector or xpath queries
432 433 434 435 436 437 438 439 440 441 442 443 444 |
# File 'lib/kitchen/element_base.rb', line 432 def search(*selector_or_xpath_args, only: nil, except: nil, reload: false) block_error_if(block_given?) ElementEnumerator.factory.build_within( self, search_query: SearchQuery.new( css_or_xpath: selector_or_xpath_args, only: only, except: except ), reload: reload ) end |
#search_history ⇒ SearchHistory
Returns the search history that found this element
414 415 416 417 418 419 |
# File 'lib/kitchen/element_base.rb', line 414 def search_history SearchHistory.new( ancestor_elements.last&.search_history || SearchHistory.empty, search_query_that_found_me ) end |
#selectors ⇒ Selectors::Base
Get the selectors for this element’s document
101 |
# File 'lib/kitchen/element_base.rb', line 101 def_delegators :config, :selectors |
#set(property, value) ⇒ Object
A way to set values and chain them
297 298 299 300 301 302 303 304 305 |
# File 'lib/kitchen/element_base.rb', line 297 def set(property, value) case property.to_sym when :name self.name = value else self[property.to_sym] = value end self end |
#sub_header_name ⇒ String
Returns the header tag name that is one level under the first header tag in this element, e.g. if this element is a “div” whose first header is “h1”, this will return “h2”
TODO this method may not be needed.
743 744 745 746 747 748 749 750 751 |
# File 'lib/kitchen/element_base.rb', line 743 def sub_header_name first_header = node.search('h1, h2, h3, h4, h5, h6').first if first_header.nil? 'h1' else first_header.name.gsub(/\d/) { |num| (num.to_i + 1).to_s } end end |
#target_label(label_text: nil, custom_content: nil, cases: false, label_class: nil) ⇒ Pantry
Creates labels for links to inside elements like Figures, Tables, Equations, Exercises, Notes, Appendices.
863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 |
# File 'lib/kitchen/element_base.rb', line 863 def target_label(label_text: nil, custom_content: nil, cases: false, label_class: nil) if cases cases = %w[nominative genitive dative accusative instrumental locative vocative] element_labels = {} cases.each do |label_case| element_labels[label_case] = "#{I18n.t("#{label_text}.#{label_case}")} #{custom_content}" element_label_case = element_labels[label_case] pantry(name: "#{label_case}_link_text").store element_label_case, label: id if id end else element_label = if label_text "#{I18n.t(label_text.to_s)} #{custom_content}" else custom_content end pantry(name: :link_text).store element_label, label: id if id end return unless label_class pantry(name: :link_type).store label_class, label: id if id end |
#text ⇒ String
Get the element text
88 89 90 91 |
# File 'lib/kitchen/element_base.rb', line 88 def_delegators :@node, :name=, :name, :[], :[]=, :add_class, :remove_class, :text, :wrap, :children, :to_html, :remove_attribute, :key?, :classes, :path, :inner_html=, :add_previous_sibling, :preceded_by_text? |
#to_html ⇒ String
Get the element as HTML
88 89 90 91 |
# File 'lib/kitchen/element_base.rb', line 88 def_delegators :@node, :name=, :name, :[], :[]=, :add_class, :remove_class, :text, :wrap, :children, :to_html, :remove_attribute, :key?, :classes, :path, :inner_html=, :add_previous_sibling, :preceded_by_text? |
#to_s ⇒ String
Returns a string version of this element
779 780 781 |
# File 'lib/kitchen/element_base.rb', line 779 def to_s remove_default_namespaces_if_clone(node.to_s) end |
#to_xhtml ⇒ String
Returns a string version of this element as XHTML
795 796 797 |
# File 'lib/kitchen/element_base.rb', line 795 def to_xhtml remove_default_namespaces_if_clone(node.to_xhtml) end |
#to_xml ⇒ String
Returns a string version of this element as XML
787 788 789 |
# File 'lib/kitchen/element_base.rb', line 787 def to_xml remove_default_namespaces_if_clone(node.to_xml) end |
#trash ⇒ Object
Delete the element
604 605 606 607 |
# File 'lib/kitchen/element_base.rb', line 604 def trash node.remove self end |
#uncount(search_query) ⇒ Object
Undo the counts from a prior search query (so that they can be counted again)
402 403 404 405 406 407 408 |
# File 'lib/kitchen/element_base.rb', line 402 def uncount(search_query) @search_query_matches_that_have_been_counted.delete(search_query.to_s)&.each do |type, count| ancestors.each_value do |ancestor| ancestor.decrement_descendant_count(type, by: count) end end end |
#wrap ⇒ Nokogiri::XML::Node
Add HTML around this element
88 89 90 91 |
# File 'lib/kitchen/element_base.rb', line 88 def_delegators :@node, :name=, :name, :[], :[]=, :add_class, :remove_class, :text, :wrap, :children, :to_html, :remove_attribute, :key?, :classes, :path, :inner_html=, :add_previous_sibling, :preceded_by_text? |
#wrap_children(name = 'div', attributes = {}) {|the| ... } ⇒ Element
Wraps the element’s children in a new element. Yields the new wrapper element to a block, if provided.
696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 |
# File 'lib/kitchen/element_base.rb', line 696 def wrap_children(name='div', attributes={}) if name.is_a?(Hash) attributes = name name = 'div' end node.children = node.document.create_element(name) do |new_node| # For some reason passing attributes to create_element doesn't work, so doing here attributes.each do |k, v| new_node[k.to_s.gsub(/([^_])_([^_])/, '\1-\2').gsub('__', '_')] = v end new_node.children = children yield Element.new(node: new_node, document: document, short_type: nil) if block_given? end self end |