1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
|
# frozen_string_literal: true
module Slim
class InvalidAttributeNameError < StandardError; end
module Splat
# @api private
class Builder
# https://html.spec.whatwg.org/multipage/syntax.html#attributes-2
INVALID_ATTRIBUTE_NAME_REGEX = /[ \0"'>\/=]/
def initialize(options)
@options = options
@attrs = {}
end
def code_attr(name, escape, value)
if delim = @options[:merge_attrs][name]
value = Array === value ? value.join(delim) : value.to_s
attr(name, escape_html(escape, value)) unless value.empty?
elsif @options[:hyphen_attrs].include?(name) && Hash === value
hyphen_attr(name, escape, value)
elsif value != false && value != nil
attr(name, escape_html(escape, value))
end
end
def splat_attrs(splat)
splat.each do |name, value|
code_attr(name.to_s, true, value)
end
end
def attr(name, value)
if name =~ INVALID_ATTRIBUTE_NAME_REGEX
raise InvalidAttributeNameError, "Invalid attribute name '#{name}' was rendered"
end
if @attrs[name]
if delim = @options[:merge_attrs][name]
@attrs[name] = @attrs[name].to_s + delim + value.to_s
else
raise("Multiple #{name} attributes specified")
end
else
@attrs[name] = value
end
end
def build_tag(&block)
tag = @attrs.delete('tag').to_s
tag = @options[:default_tag] if tag.empty?
if block
# This is a bit of a hack to get a universal capturing.
#
# TODO: Add this as a helper somewhere to solve these capturing issues
# once and for all.
#
# If we have Slim capturing disabled and the scope defines the method `capture` (i.e. Rails)
# we use this method to capture the content.
#
# otherwise we just use normal Slim capturing (yield).
#
# See https://github.com/slim-template/slim/issues/591
# https://github.com/slim-template/slim#helpers-capturing-and-includes
#
content =
if @options[:disable_capture] && (scope = block.binding.eval('self')).respond_to?(:capture)
scope.capture(&block)
else
yield
end
"<#{tag}#{build_attrs}>#{content}</#{tag}>"
else
"<#{tag}#{build_attrs} />"
end
end
def build_attrs
attrs = @options[:sort_attrs] ? @attrs.sort_by(&:first) : @attrs
attrs.map do |k, v|
if v == true
if @options[:format] == :xhtml
" #{k}=#{@options[:attr_quote]}#{@options[:attr_quote]}"
else
" #{k}"
end
else
" #{k}=#{@options[:attr_quote]}#{v}#{@options[:attr_quote]}"
end
end.join
end
private
def hyphen_attr(name, escape, value)
if Hash === value
if @options[:hyphen_underscore_attrs]
value.each do |n, v|
hyphen_attr("#{name}-#{n.to_s.tr('_', '-')}", escape, v)
end
else
value.each do |n, v|
hyphen_attr("#{name}-#{n}", escape, v)
end
end
else
attr(name, escape_html(escape, value))
end
end
def escape_html(escape, value)
return value if !escape || value == true
@options[:use_html_safe] ? Temple::Utils.escape_html_safe(value) : Temple::Utils.escape_html(value)
end
end
end
end
|