require 'volt/server/html_parser/attribute_scope' class ViewScope include AttributeScope attr_reader :html, :bindings attr_accessor :path, :binding_number def initialize(handler, path) @handler = handler @path = path @html = '' @bindings = {} @binding_number = 0 end def <<(html) @html << html end def add_binding(content) case content[0] when '#' command, *content = content.split(/ /) content = content.join(' ') case command when '#if' add_if(content) when '#elsif' add_else(content) when '#else' if content.blank? add_else(nil) else raise "#else does not take a conditional #{content} was provided." end when '#each' add_each(content) when '#template' add_template(content) end when '/' # close binding close_scope else # content add_content_binding(content) end end def add_content_binding(content) @handler.html << "<!-- $#{@binding_number} --><!-- $/#{@binding_number} -->" save_binding(@binding_number, "lambda { |__p, __t, __c, __id| ContentBinding.new(__p, __t, __c, __id, Proc.new { #{content} }) }") @binding_number += 1 end def add_if(content) # Add with path for if group. @handler.scope << IfViewScope.new(@handler, @path + "/__ifg#{@binding_number}", content) @binding_number += 1 end def add_else(content) raise "#else can only be added inside of an if block" end def add_each(content) @handler.scope << EachScope.new(@handler, @path, content) end def add_template(content) @handler.html << "<!-- $#{@binding_number} --><!-- $/#{@binding_number} -->" save_binding(@binding_number, "lambda { |__p, __t, __c, __id| TemplateBinding.new(__p, __t, __c, __id, #{@path.inspect}, Proc.new { [#{content}] }) }") @binding_number += 1 end def add_component(tag_name, attributes, unary) component_name = tag_name[1..-1].gsub(':', '/') @handler.html << "<!-- $#{@binding_number} --><!-- $/#{@binding_number} -->" data_hash = [] attributes.each_pair do |name, value| parts = value.split(/(\{[^\}]+\})/).reject(&:blank?) binding_count = parts.count {|p| p[0] == '{' && p[-1] == '}'} # if this attribute has bindings if binding_count > 0 if binding_count > 1 # Multiple bindings elsif parts.size == 1 && binding_count == 1 # A single binding data_hash << "#{name.inspect} => Proc.new { #{value[1..-2]} }" end else # String data_hash << "#{name.inspect} => #{value.inspect}" end end arguments = "#{component_name.inspect}, { #{data_hash.join(',')} }" save_binding(@binding_number, "lambda { |__p, __t, __c, __id| ComponentBinding.new(__p, __t, __c, __id, #{@path.inspect}, Proc.new { [#{arguments}] }) }") @binding_number += 1 end def add_textarea(tag_name, attributes, unary) @handler.scope << TextareaScope.new(@handler, @path + "/__txtarea#{@binding_number}", attributes) @binding_number += 1 # close right away if unary @handler.last.close_scope if unary end # Called when this scope should be closed out def close_scope(pop=true) if pop scope = @handler.scope.pop else scope = @handler.last end raise "template path already exists: #{scope.path}" if @handler.templates[scope.path] template = { 'html' => scope.html } if scope.bindings.size > 0 # Add the bindings if there are any template['bindings'] = scope.bindings end @handler.templates[scope.path] = template end def save_binding(binding_number, code) @bindings[binding_number] ||= [] @bindings[binding_number] << code end end