module WWW_App::TO
Constants
- COMMA
- GEM_PATH
- INVALID_SCRIPT_TYPE_CHARS
- JS_FILE_PATHS
- KEY_REQUIRED
- NOTHING
- SPACE
- VERSION
Public Instance Methods
to_html(*args)
click to toggle source
# File lib/www_app/TO.rb, line 168 def to_html *args return @mustache.render(*args) if instance_variable_defined?(:@mustache) instance_eval(&@blok) if @tags.empty? final = "" indent = 0 todo = @tags.dup last = nil stacks = {:js=>[], :script_tags=>[]} last_open = nil script_libs_added = false doc = [ doc_type = {:tag_name=>:doc_type , :text => "<!DOCTYPE html>"}, html = {:tag_name=>:html , :children=>[ head = {:tag_name=>:head , :lang=>'en', :children=>[]}, body = {:tag_name=>:body , :children=>[]} ]} ] style_tags = {:tag_name => :style_tags, :children => []} tags = @tags.dup while (t = tags.shift) t_name = t[:tag_name] parent = t[:parent] case # ============== when t_name == :title && !parent fail "Title already set." if head[:children].detect { |c| c[:tag_name] == :title } head[:children] << t when t_name == :meta head[:children] << t when t_name == :style style_tags[:children] << t when t_name == :link head[:children] << t when t_name == :_ && !parent body[:css] = (body[:css] || {}).merge(t[:css]) if t[:css] body[:class] = (body[:class] || []).concat(t[:class]) if t[:class] if t[:id] fail ":body already has id: #{body[:id].inspect}, #{t[:id]}" if body[:id] body[:id] = t[:id] end if t[:children] body[:children].concat t[:children] tags = t[:children].dup.concat(tags) end else # ============== if !parent body[:children] << t end if t[:css] style_tags[:children] << t end if t[:children] tags = t[:children].dup.concat(tags) end if t_name == :script stacks[:script_tags] << t end if t_name == :js stacks[:js].concat [css_selector(t[:parent])].concat(t[:value]) end end # === case ======== end # === while if body[:css] && !body[:css].empty? style_tags[:children] << body end is_fragment = stacks[:script_tags].empty? && stacks[:js].empty? && style_tags[:children].empty? && head[:children].empty? && body.values_at(:css, :id, :class).compact.empty? if is_fragment doc = body[:children] else # is doc head[:children] << style_tags content_type = head[:children].detect { |t| t[:tag_name] == :meta && t[:http_equiv] && t[:http_equiv].downcase=='Content-Type'.downcase } if !content_type head[:children].unshift( {:tag_name=>:meta, :http_equiv=>'Content-Type', :content=>"text/html; charset=UTF-8"} ) end end # if is_fragment todo = doc.dup while (tag = todo.shift) t_name = tag.is_a?(Hash) && tag[:tag_name] case when tag == :new_line final << NEW_LINE when tag == :open attributes = stacks.delete :attributes tag_sym = todo.shift if [:script].include?(tag_sym) || (todo.first != :close && !indent.zero? && !HTML::NO_NEW_LINES.include?(last_open)) final << NEW_LINE << SPACES(indent) end if HTML::SELF_CLOSING_TAGS.include?(tag_sym) final << ( attributes ? "<#{tag_sym} #{attributes} />\n" : "<#{tag_sym} />\n" ) if todo.first == :close && todo[1] == tag_sym todo.shift todo.shift end else # === has closing tag if attributes final << "<#{tag_sym} #{attributes}>" else final << "<#{tag_sym}>" end end # === if HTML last = indent indent += 2 last_open = tag_sym when tag == :close indent -= 2 if last != indent final << SPACES(indent) end last = indent final << "</#{todo.shift}>" when tag == :clean_attrs attributes = todo.shift target = todo.shift tag_name = target[:tag_name] attributes.each { |attr, val| attributes[attr] = case when attr == :src && tag_name == :script fail ::ArgumentError, "Invalid type: #{val.inspect}" unless val.is_a?(String) Clean.relative_href val when attr == :type && tag_name == :script clean = val.gsub(INVALID_SCRIPT_TYPE_CHARS, '') clean = 'text/unknown' if clean.empty? clean when attr == :type && val == :hidden 'hidden' when attr == :href && tag_name == :a if val.is_a? Symbol Clean.mustache :href, val else Clean.href val end when [:action, :src, :href].include?(attr) Clean.relative_href(val) when attr == :id Clean.html_id(val.to_s) when attr == :class val.map { |name| Clean.css_class_name(name.to_s) }.join(" ".freeze) when tag_name == :style && attr == :type 'text/css' when tag_name == :label && attr == :for && val.is_a?(::Symbol) Clean.html(val.to_s) when ::WWW_App::HTML::TAGS_TO_ATTRIBUTES[tag_name].include?(attr) Clean.html(val) else fail "Invalid attr: #{attr.inspect}" end # case attr } # === each attr stacks[:attributes] = attributes.inject([]) { |memo, (k,v)| memo << "#{k}=\"#{v}\"" memo }.join " ".freeze when t_name == :doc_type if tag[:text] == "<!DOCTYPE html>" final << tag[:text] final << NEW_LINE else fail "Unknown doc type: #{tag[:text].inspect}" end when t_name == :text final.<<( tag[:skip_escape] ? tag[:value] : Clean.html(tag[:value]) ) when t_name == :link final << ( %^#{SPACES(indent)}<link type="text/css" rel="stylesheet" href="#{::Escape_Escape_Escape.relative_href tag[:href]}" />^ ) when t_name == :meta case when tag[:http_equiv] key_name = "http-equiv" key_content = tag[:http_equiv].gsub(/[^a-zA-Z\/\;\ 0-9\=\-]+/, '') content = tag[:content].gsub(/[^a-zA-Z\/\;\ 0-9\=\-]+/, '') else fail ArgumentError, tag.keys.inspect end final << ( %^#{SPACES(indent)}<meta #{key_name}="#{key_content}" content="#{content}" />\n^ ) when t_name == :html # === :html tag ================ todo = [ :clean_attrs, {:lang=>(tag[:lang] || 'en')}, tag, :open, :html ].concat(tag[:children]).concat([:new_line, :close, :html]).concat(todo) when t_name == :head # === :head tag ================= todo = [ :open, :head, :new_line ]. concat(tag[:children] || []). concat([:new_line, :close, :head]). concat(todo) when t_name == :title && !parent(tag) todo = [ :open, :title ].concat(tag[:children]).concat([:close, :title]).concat(todo) when t_name == :_ # =============== :_ tag ======== nil # do nothing when t_name == :js next when t_name == :script # =============== :script tag === attrs = tag.select { |k, v| k == :src || k == :type || k == :class } new_todo = [] if attrs[:src] && !script_libs_added new_todo << {:tag_name=>:js_to_script_tag} script_libs_added = true end new_todo.concat [ :clean_attrs, attrs, tag, :open, :script, ] new_todo.concat(tag[:children]) if tag[:children] if tag[:children] && !tag[:children].empty? && tag[:children].first[:tag_name] != :text && tag[:children].last[:tag_name] != :text new_todo << :new_line end new_todo.concat [ :close, :script ].concat(todo) todo = new_todo when t_name == :js_to_script_tag next if stacks[:js].empty? && stacks[:script_tags].empty? stacks[:clean_text] ||= lambda { |raw_x| x = case raw_x when ::Symbol, ::String Clean.html(raw_x.to_s) when ::Array raw_x.map { |x| stacks[:clean_text].call x } when ::Numeric x else fail "Unknown type for json: #{raw_x.inspect}" end } script_tag = {:tag_name=>:script}.freeze new_todo = [] JS_FILE_PATHS.each { |path| new_todo.concat [ :clean_attrs, {:src=>path}, script_tag, :open, :script, :close, :script ] } if !stacks[:js].empty? clean_vals = stacks[:js].map { |raw_x| stacks[:clean_text].call(raw_x) } content = <<-EOF \n#{SPACES(indent)}WWW_App.run( #{::Escape_Escape_Escape.json_encode(code: clean_vals)} ); EOF new_todo.concat [ :clean_attrs, {:type=>'application/javascript'}, script_tag, :open, :script, {:tag_name=>:text, :skip_escape=>true, :value=> content }, :close, :script ] end todo = new_todo.concat(todo) when tag == :javascript vals = todo.shift when tag == :to_json vals = todo.shift ::Escape_Escape_Escape.json_encode(to_clean_text(:javascript, vals)) when t_name == :style next when t_name == :style_tags # =============== <style ..> TAG ================= next if tag[:children].empty? style_and_other_tags = tag[:children].dup flatten = [] # === flatten groups # style # div, span { # a:link, a:visited { # ---> # style # div a:link, div a:visited, span a:link, span a:visited { # prev = nil while e = style_and_other_tags.shift case when e[:tag_name] == :style style_and_other_tags = e[:children].dup.concat(style_and_other_tags) when e[:tag_name] == :group style_and_other_tags = e[:children].dup.concat(style_and_other_tags) prev = nil flatten << e when parent?(e, :group) if e[:__] e[:__children] = [] end if prev && prev[:__] prev[:__children] << e e[:__parent] = prev end prev = e else flatten << e end # === case end # === while todo = [ :clean_attrs, {:type=>'text/css'}, {:tag_name=>:style}, :open, :style, :flat_style_groups, flatten, :close, :style ].concat(todo) when tag == :flat_style_groups indent += 2 css_final = "" flatten = todo.shift # # Each produces: # # selectors { # escaped/sanitized css; # } # flatten.each { |style| next if !style[:css] || style[:css].empty? css_final << "\n" << SPACES(indent) << css_selector(style, :full) << " {\n".freeze the_css = style[:css] || (parent?(style, :group) && style[:parent][:css]) if the_css indent += 2 the_css.each { |raw_k, raw_val| name = begin clean_k = ::WWW_App::Clean.css_attr(raw_k.to_s.gsub('_','-')) fail("Invalid name for css property name: #{raw_k.inspect}") if !clean_k || clean_k.empty? clean_k end raw_val = raw_val.is_a?(Array) ? raw_val.join(COMMA) : raw_val.to_s v = case when name[IMAGE_AT_END] case raw_val when 'inherit', 'none' raw_val else "url(#{Clean.href(raw_val)})" end when ::WWW_App::CSS::PROPERTIES.include?(raw_k) Clean.css raw_k, raw_val else fail "Invalid css attr: #{name.inspect}" end # === case css_final << SPACES(indent) << "#{name}: #{v};\n" } # === each style indent -= 2 end # === if style[:css] css_final << SPACES(indent) << "}\n".freeze << SPACES(indent - 2) } indent -= 2 todo = [ {:tag_name=>:text, :skip_escape=>true, :value=>css_final} ].concat(todo) when tag == :script # ============ h = vals if h[:tag] == :script && h[:content] && !h[:content].empty? return <<-EOF <script type="text/css"> WWW_App.run( #{to_clean_text :to_json, h[:content]} ); </script> EOF end html = h[:childs].map { |tag_index| to_clean_text(:html, @tag_arr[tag_index]) }.join(NEW_LINE).strip return unless h[:render?] if html.empty? && h[:text] html = if h[:text].is_a?(::Symbol) h[:text].to_mustache(:html) else if h[:text].is_a?(::Hash) if h[:text][:escape] == false h[:text][:value] else Clean.html(h[:text][:value].strip) end else Clean.html(h[:text].strip) end end end # === if html.empty? (html = nil) if html.empty? case when h[:tag] == :render_if key = h[:attrs][:key] open = "{{# coll.#{key} }}" close = "{{/ coll.#{key} }}" when h[:tag] == :render_unless key = h[:attrs][:key] open = "{{^ coll.#{key} }}" close = "{{/ coll.#{key} }}" when Methods[:elements].include?(h[:tag]) open = "<#{h[:tag]}#{to_clean_text(:attrs, h)}" if NO_END_TAGS.include?(h[:tag]) open += ' />' close = nil else open += '>' close = "</#{h[:tag]}>" end else fail "Unknown html tag: #{h[:tag].inspect}" end # === case h[:tag] if h[:tag] [open, html, close].compact.join else html end # === if when t_name && ::WWW_App::HTML::TAGS.include?(t_name) # === HTML tags ===== # ================================ # === Save this for last to allow # certain tags # to be over-riddent, # like :script # ================================ attrs = {} attrs.default KEY_REQUIRED new_todo = [] t2a = ::WWW_App::HTML::TAGS_TO_ATTRIBUTES tag.each { |attr_name, v| if t2a[:all].include?(attr_name) || (t2a[tag[:tag_name]] && t2a[tag[:tag_name]].include?(attr_name)) attrs[attr_name] = v end } if !attrs.empty? new_todo.concat [:clean_attrs, attrs, tag] end new_todo.concat [:open, tag[:tag_name]] if tag[:children] && !tag[:children].empty? new_todo.concat tag[:children] if tag[:children].detect { |t| HTML::TAGS.include?(t[:tag_name]) } new_todo << :new_line end end new_todo.concat [:close, tag[:tag_name]] todo = new_todo.concat(todo) else fail "Unknown: #{tag.inspect[0,30]}" end # === case end # === while final @mustache ||= begin mustache = ::Mustache.new mustache.template = Clean.clean_utf8(final) mustache end to_html(*args) end
to_raw_text()
click to toggle source
# File lib/www_app/TO.rb, line 146 def to_raw_text instance_eval(&@blok) if @tags.empty? str = "" indent = 0 print_tag = lambda { |t| info = t.select { |n| [:id, :class, :closed, :pseudo].include?( n ) } info[:parent] = t[:parent] && t[:parent][:tag_name] str += "#{" " * indent}#{t[:tag_name].inspect} -- #{info.inspect.gsub(/\A\{|\}\Z/, '')}\n" indent += 1 if t[:children] t[:children].each { |c| str << print_tag.call(c) } end indent -= 1 } @tags.each { |t| print_tag.call(t) } str end