templates/helpers.rb in asciidoctor-revealjs-4.0.1 vs templates/helpers.rb in asciidoctor-revealjs-4.1.0.rc2

- old
+ new

@@ -31,15 +31,15 @@ # Ex: a feature is enabled globally but can be disabled using a data- attribute on individual items # :revealjs_previewlinks: True # then link::example.com[Link text, preview=false] # Here the template must have data-preview-link="false" not just no data-preview-link attribute def bool_data_attr val - return false if !attr?(val) + return false unless attr?(val) if attr(val).downcase == 'false' || attr(val) == '0' - return 'false' + 'false' else - return true + true end end # false needs to be verbatim everything else is a string. # Calling side isn't responsible for quoting so we are doing it here @@ -70,38 +70,111 @@ # @param attributes [Hash] (default: {}) # @param content [#to_s] the content; +nil+ to call the block. (default: nil). # @yield The block of Slim/HTML code within the tag (optional). # @return [String] a rendered HTML element. # - def html_tag(name, attributes = {}, content = nil) attrs = attributes.inject([]) do |attrs, (k, v)| - next attrs if !v || v.nil_or_empty? + next attrs unless v && (v == true || !v.nil_or_empty?) v = v.compact.join(' ') if v.is_a? Array attrs << (v == true ? k : %(#{k}="#{v}")) end attrs_str = attrs.empty? ? '' : ' ' + attrs.join(' ') - if VOID_ELEMENTS.include? name.to_s %(<#{name}#{attrs_str}>) else - content ||= yield if block_given? + content ||= (yield if block_given?) %(<#{name}#{attrs_str}>#{content}</#{name}>) end end + # + # Extracts data- attributes from the attributes. + # @param attributes [Hash] (default: {}) + # @return [Hash] a Hash that contains only data- attributes + # + def data_attrs(attributes) + # key can be an Integer (for positional attributes) + attributes.map { |key, value| (key == 'step') ? ['data-fragment-index', value] : [key, value] } + .to_h + .select { |key, _| key.to_s.start_with?('data-') } + end + + + # + # Wrap an inline text in a <span> element if the node contains a role, an id or data- attributes. + # @param content [#to_s] the content; +nil+ to call the block. (default: nil). + # @return [String] the content or the content wrapped in a <span> element as string + # + def inline_text_container(content = nil) + data_attrs = data_attrs(@attributes) + classes = [role, ('fragment' if (option? :step) || (attr? 'step') || (roles.include? 'step'))].compact + if !roles.empty? || !data_attrs.empty? || !@id.nil? + html_tag('span', { :id => @id, :class => classes }.merge(data_attrs), (content || (yield if block_given?))) + else + content || (yield if block_given?) + end + end + + ## # Returns corrected section level. # # @param sec [Asciidoctor::Section] the section node (default: self). # @return [Integer] # def section_level(sec = self) @_section_level ||= (sec.level == 0 && sec.special) ? 1 : sec.level end + ## + # Display footnotes per slide + # + @@slide_footnotes = {} + @@section_footnotes = {} + + def slide_footnote(footnote) + footnote_parent = footnote.parent + # footnotes declared on the section title are processed during the parsing/substitution. + # as a result, we need to store them to display them on the right slide/section + if footnote_parent.instance_of?(::Asciidoctor::Section) + footnote_parent_object_id = footnote_parent.object_id + section_footnotes = (@@section_footnotes[footnote_parent_object_id] || []) + footnote_index = section_footnotes.length + 1 + attributes = footnote.attributes.merge({ 'index' => footnote_index }) + inline_footnote = Asciidoctor::Inline.new(footnote_parent, footnote.context, footnote.text, :attributes => attributes) + section_footnotes << Asciidoctor::Document::Footnote.new(inline_footnote.attr(:index), inline_footnote.id, inline_footnote.text) + @@section_footnotes[footnote_parent_object_id] = section_footnotes + inline_footnote + else + parent = footnote.parent + until parent == nil || parent.instance_of?(::Asciidoctor::Section) + parent = parent.parent + end + # check if there is any footnote attached on the section title + section_footnotes = parent != nil ? @@section_footnotes[parent.object_id] || [] : [] + initial_index = footnote.attr(:index) + # reset the footnote numbering to 1 on each slide + # make sure that if a footnote is used more than once it will use the same index/number + slide_index = (existing_footnote = @@slide_footnotes[initial_index]) ? existing_footnote.index : @@slide_footnotes.length + section_footnotes.length + 1 + attributes = footnote.attributes.merge({ 'index' => slide_index }) + inline_footnote = Asciidoctor::Inline.new(footnote_parent, footnote.context, footnote.text, :attributes => attributes) + @@slide_footnotes[initial_index] = Asciidoctor::Document::Footnote.new(inline_footnote.attr(:index), inline_footnote.id, inline_footnote.text) + inline_footnote + end + end + + def clear_slide_footnotes + @@slide_footnotes = {} + end + + def slide_footnotes(section) + section_object_id = section.object_id + section_footnotes = @@section_footnotes[section_object_id] || [] + section_footnotes + @@slide_footnotes.values + end ## # Returns the captioned section's title, optionally numbered. # # @param sec [Asciidoctor::Section] the section node (default: self).