module Vapid class Template # A wrapper around Nokogiri's node, so we can manipulate using our own methods class Node attr_reader :directives, :clone_index def initialize(node, clone_index = 0) @node = node @directives = parse_directives @clone_index = node["vp-clone"].present? ? node["vp-clone"].to_i : clone_index end def group_expr @directives["group"] end def group? group_expr.present? end def clone(num_clones = 1) num_clones.times do |n| cloned = @node.dup cloned["vp-clone"] = num_clones - n @node.after cloned end end def clone? clone_index > 0 end def cloneable? group? && !clone? end def directives? @directives.any? end def first_child wrap @node.first_element_child end def next_sibling wrap @node.next_element end # TODO: Abstract this, so that directives only need to pass in # html, text, etc, and don't have to know about Nokogiri methods/concepts def modify(obj) return unless obj obj.each do |prop, value| if value.is_a?(String) @node.send "#{prop}=", value elsif prop == :attributes modify_attributes(value) elsif value.is_a?(Hash) modify_properties(prop, value) elsif prop == :placeholder @node["vp-placeholder"] = true end end end private def wrap(node) node && self.class.new(node, clone_index) end def parse_directives @node.attribute_nodes.each_with_object({}) do |attribute, memo| vp_type = attribute.name[/^(?:data-)?vp-(.+)$/, 1] memo[vp_type] = attribute.value if Directives[vp_type] end end def modify_attributes(attributes) attributes.each { |k, v| @node[k.to_s] = v } end def modify_properties(prop, values) property = @node.send(prop) values.each { |k, v| property[k.to_s] = v } @node.send "#{prop}=", property end end end end