require 'volt/server/scope' require 'volt/server/if_binding_setup' require 'nokogiri' # TODO: The section_name that we're passing in should probably be # abstracted out. Possibly this whole thing needs a rewrite. class Template attr_accessor :current_scope, :section_name def initialize(template_parser, section_name, template, scope=Scope.new) @binding_number = 0 @template_parser = template_parser @section_name = section_name @template = template @scopes = [scope] @current_scope = @scopes.first end def html if @template.respond_to?(:name) && @template.name[0] == ':' # Don't return the <:section> tags return @template.children.to_html else # if @template.class == Nokogiri::XML::NodeSet # result = '' # @template.each do |node| # result << node.to_html # end # else result = @template.to_html # end result end end def add_binding(node, content) if content[0] == '/' add_close_mustache(node) elsif content[0] == '#' command, *content = content.split(/ /) content = content.join(' ') case command when '#template' return add_template(node, content) when '#each' return add_each_binding(node, content) when '#if' return add_if_binding(node, content) when '#elsif' return add_else_binding(node, content) when '#else' if content.present? # TODO: improve error, include line/file raise "#else should not include a condition, use #elsif instead. #{content} was passed as a condition." end return add_else_binding(node, nil) else # TODO: Handle invalid command raise "Invalid Command" end else # text binding return add_text_binding(content) end end def add_template(node, content, name='Template') html = "" @current_scope.add_binding(@binding_number, "lambda { |target, context, id| #{name}Binding.new(target, context, id, #{@template_parser.template_path.inspect}, Proc.new { [#{content}] }) }") @binding_number += 1 return html end def add_each_binding(node, content) html = "" content, variable_name = content.strip.split(/ as /) template_name = "#{@template_parser.template_path}/#{section_name}/__template/#{@binding_number}" @current_scope.add_binding(@binding_number, "lambda { |target, context, id| EachBinding.new(target, context, id, Proc.new { #{content} }, #{variable_name.inspect}, #{template_name.inspect}) }") # Add the node, the binding number, then store the location where the # bindings for this block starts. @current_scope = Scope.new(@binding_number) @scopes << @current_scope @binding_number += 1 return html end def add_if_binding(node, content) html = "" template_name = "#{@template_parser.template_path}/#{section_name}/__template/#{@binding_number}" if_binding_setup = IfBindingSetup.new if_binding_setup.add_branch(content, template_name) @current_scope.start_if_binding(@binding_number, if_binding_setup) # Add the node, the binding number, then store the location where the # bindings for this block starts. @current_scope = Scope.new(@binding_number) @scopes << @current_scope @binding_number += 1 return html end def add_else_binding(node, content) html = add_close_mustache(node, false) html += "" template_name = "#{@template_parser.template_path}/#{section_name}/__template/#{@binding_number}" @current_scope.current_if_binding[1].add_branch(content, template_name) # Add the node, the binding number, then store the location where the # bindings for this block starts. @current_scope = Scope.new(@binding_number) @scopes << @current_scope @binding_number += 1 return html end def add_close_mustache(node, close_if=true) scope = @scopes.pop @current_scope = @scopes.last # Close an outstanding if binding (if it exists) @current_scope.close_if_binding! if close_if # Track that this scope was closed out @current_scope.add_closed_child_scope(scope) html = "" return html end # When we find a binding, we pass it's content in here and replace it with # the return value def add_text_binding(content) html = "" @current_scope.add_binding(@binding_number, "lambda { |target, context, id| ContentBinding.new(target, context, id, Proc.new { #{content} }) }") @binding_number += 1 return html end def setup_node_id(node) id = node['id'] # First assign this node an id if it doesn't have one unless id id = node['id'] = "id#{@binding_number}" @binding_number += 1 end end # Attribute bindings support multiple handlebar listeners # Exvoltle: #