class Phlex::SGML
def __attributes__(attributes, buffer = +"")
def __attributes__(attributes, buffer = +"") attributes.each do |k, v| next unless v name = case k when String then k when Symbol then k.name.tr("_", "-") else raise Phlex::ArgumentError.new("Attribute keys should be Strings or Symbols.") end value = case v when true true when String v.gsub('"', """) when Symbol v.name.tr("_", "-").gsub('"', """) when Integer, Float v.to_s when Hash case k when :style __styles__(v).gsub('"', """) else __nested_attributes__(v, "#{name}-", buffer) end when Array case k when :style __styles__(v).gsub('"', """) else __nested_tokens__(v) end when Set case k when :style __styles__(v).gsub('"', """) else __nested_tokens__(v.to_a) end when Phlex::SGML::SafeObject v.to_s.gsub('"', """) else raise Phlex::ArgumentError.new("Invalid attribute value for #{k}: #{v.inspect}.") end lower_name = name.downcase unless Phlex::SGML::SafeObject === v normalized_name = lower_name.delete("^a-z-") if value != true && REF_ATTRIBUTES.include?(normalized_name) case value when String if value.downcase.delete("^a-z:").start_with?("javascript:") # We just ignore these because they were likely not specified by the developer. next end else raise Phlex::ArgumentError.new("Invalid attribute value for #{k}: #{v.inspect}.") end end if normalized_name.bytesize > 2 && normalized_name.start_with?("on") && !normalized_name.include?("-") raise Phlex::ArgumentError.new("Unsafe attribute name detected: #{k}.") end if UNSAFE_ATTRIBUTES.include?(normalized_name) raise Phlex::ArgumentError.new("Unsafe attribute name detected: #{k}.") end end if name.match?(/[<>&"']/) raise Phlex::ArgumentError.new("Unsafe attribute name detected: #{k}.") end if lower_name.to_sym == :id && k != :id raise Phlex::ArgumentError.new(":id attribute should only be passed as a lowercase symbol.") end case value when true buffer << " " << name when String buffer << " " << name << '="' << value << '"' end end buffer end