Class: RMThemeGen::ThemeTextmate

Inherits:
RMThemeParent show all
Defined in:
lib/rmthemegen/rmtg_textmate.rb,
lib/rmthemegen/plist_to_tokenlist.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods inherited from RMThemeParent

#clean_colorsets, #clear_colorsets, #handle_rand_seed, #randcolor, #randfilename, #randthemename, #reset_colorsets

Constructor Details

#initializeThemeTextmate

Returns a new instance of ThemeTextmate.



25
26
27
28
29
30
31
32
33
34
35
36
37
# File 'lib/rmthemegen/rmtg_textmate.rb', line 25

def initialize
   super
   @bold_chance = 0.4
   @underline_chance = 0.3
   @italic_candidates = []
   @bold_candidates = []
# with code inspections we don't color the text, we just put a line or something under it .
   @code_inspections = []
   @cross_out = [ ]
   
   @underline_candidates = []
   @italic_chance = 0.2
end

Instance Attribute Details

#color_2_groupObject

Returns the value of attribute color_2_group.



17
18
19
# File 'lib/rmthemegen/plist_to_tokenlist.rb', line 17

def color_2_group
  @color_2_group
end

#for_tm_outputObject (readonly)

Returns the value of attribute for_tm_output.



16
17
18
# File 'lib/rmthemegen/plist_to_tokenlist.rb', line 16

def for_tm_output
  @for_tm_output
end

#group_2_colorObject

Returns the value of attribute group_2_color.



17
18
19
# File 'lib/rmthemegen/plist_to_tokenlist.rb', line 17

def group_2_color
  @group_2_color
end

#repository_namesObject (readonly)

Returns the value of attribute repository_names.



16
17
18
# File 'lib/rmthemegen/plist_to_tokenlist.rb', line 16

def repository_names
  @repository_names
end

#scope_2_groupObject

Returns the value of attribute scope_2_group.



17
18
19
# File 'lib/rmthemegen/plist_to_tokenlist.rb', line 17

def scope_2_group
  @scope_2_group
end

#scopes_foundObject (readonly)

Returns the value of attribute scopes_found.



16
17
18
# File 'lib/rmthemegen/plist_to_tokenlist.rb', line 16

def scopes_found
  @scopes_found
end

#scopes_found_countObject

Returns the value of attribute scopes_found_count.



17
18
19
# File 'lib/rmthemegen/plist_to_tokenlist.rb', line 17

def scopes_found_count
  @scopes_found_count
end

#themenameObject (readonly)

Returns the value of attribute themename.



17
18
19
# File 'lib/rmthemegen/rmtg_textmate.rb', line 17

def themename
  @themename
end

#under_patternsObject (readonly)

Returns the value of attribute under_patterns.



16
17
18
# File 'lib/rmthemegen/plist_to_tokenlist.rb', line 16

def under_patterns
  @under_patterns
end

#uses_same_foregroundObject

Returns the value of attribute uses_same_foreground.



17
18
19
# File 'lib/rmthemegen/plist_to_tokenlist.rb', line 17

def uses_same_foreground
  @uses_same_foreground
end

Instance Method Details

#before_create(outputdir, bg_color_style, colorsets, rand_seed) ⇒ Object

subclass



59
60
61
# File 'lib/rmthemegen/rmtg_textmate.rb', line 59

def before_create(outputdir, bg_color_style, colorsets, rand_seed)
   super(outputdir, bg_color_style, colorsets, rand_seed)
end

#create_textmate_theme(bg_color_style = :dark, colorsets = [], rand_seed = nil, opts_hash = {}) ⇒ Object



86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
# File 'lib/rmthemegen/rmtg_textmate.rb', line 86

def create_textmate_theme(bg_color_style=:dark, colorsets=[], rand_seed=nil, opts_hash={})
  opts_hash[:punctuation_bold] = 0.2 
  opts_hash[:backgrounds_colored] = 0.05

  #returns string of a plist xml file that should work in textmate.  
  handle_rand_seed(rand_seed)
  before_create( '.',bg_color_style, colorsets, rand_seed) 
  #it will save the theme file ".tmTheme" in the same directory as other themes
  #it will return the full name and path of that theme file.

  @theme_successfully_created=false
    rexmlout = REXML::Document.new
    rexmlout << REXML::DocType.new('plist','PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"')
    rexmlout << REXML::XMLDecl.new("1.0","UTF-8",nil)
    plist = REXML::Element.new "plist"
    plist.add_attributes( "version"=>"1.0")
    dict = REXML::Element.new( "dict", plist) #causes plist to be the parent of dict
    dict.add_text(REXML::Element.new("key").add_text("name") )
    dict.add_text(REXML::Element.new("string").add_text( @themename ) )
    dict.add_text(REXML::Element.new("key").add_text("author") )
    dict.add_text(REXML::Element.new("string").add_text("David Heitzman") )
    dict.add_text(REXML::Element.new("key").add_text("settings") )
    main_array = REXML::Element.new("array",dict)
    doc_dict = REXML::Element.new("dict",main_array)
    doc_dict.add_text(REXML::Element.new("key").add_text("settings") )

    set_doc_options(doc_dict)
    
    process_plists()

    scopes_found=get_scopes_from_themefiles()
    co2 =0
    @used_names ={}
    
    #group_2_color.each do |g,c| group_2_color[g] = "#"+randcolor(:bg_rgb=>@backgroundcolor).upcase end   
    
    @new_color_to_group = {} #so this will have a key from each color to its group and also a key for each scope to its color
    @color_2_group.each do |c,g|
      fontStyle=''
      if g.join(' ').upcase.include?( "ITALIC" ) || rand < (@italic_chance/2) 
        fontStyle +="italic "
      end 
      if g.join(' ').upcase.include?( "BOLD" ) || rand < (@bold_chance/2)
        fontStyle += "bold "
      end 
      newcol =  "#"+randcolor(:bg_rgb=>@backgroundcolor).upcase
      @new_color_to_group[newcol] = color_2_group[c]
      
      g.each do |s|
        @new_color_to_group[s] = {:foreground=>newcol,:fontStyle=>fontStyle.chop!}
      end
    end 
    
    scopes_found.each_index do |k|
      v=scopes_found[k]  
      if  self.scopes_found_count[v] > 0
        scope_text = v
        scope_text = scope_text.split(",")[0] if v.include?(",") 
        scope_text = scope_text.split[0].split(".") #this takes the first whole token prior to any spaces, then makes the array out of the substrings separated by periods
        elname = " ~ "+scope_text[0] || " ! "+unique_number.to_s
        co=1 
        while !@used_names[elname].nil?
           if scope_text[co]
              elname += "."+scope_text[co] 
           else
              elname += unique_number.to_s
           end 
        co += 1
        end
#   puts 'rmtg_textmate:131 '+elname 
#   puts 'the scope that kills it: "'+v.to_s 
#            elname = "uniquename"+unique_number.to_s 
        @used_names[elname] = true
        main_array.add_element(
        # so it's  make_name_scope_settings_rand(name,scope,[don't worry about it, but colors you can assign manually]) 
            make_name_scope_settings_rand(elname,v,[])
        )  
        co2 += 1 
      end
    end 

#        variable.other.readwrite.instance.ruby
#        variable.other.readwrite.class.ruby

    uuid_key = REXML::Element.new("key")
    uuid_key.add_text("uuid")
    uuid_element = REXML::Element.new("string")
    uuid_element.add_text( gen_uuid)
    dict.add_element uuid_key
    dict.add_element uuid_element

    rexmlout << plist
    formatter = REXML::Formatters::Pretty.new
    formatter.compact=true
    @output=''
    formatter.write(rexmlout, @output)
    @theme_successfully_created = true
    @output
end

#gen_uuidObject



258
259
260
261
# File 'lib/rmthemegen/rmtg_textmate.rb', line 258

def gen_uuid
    nn = sprintf("%032X",rand(340282366920938463463374607431768211456).abs)
    nn = nn[0,8]+"-"+nn[12,4]+"-4"+nn[17,3]+"-"+["8","9","A","B"].shuffle[0]+nn[21,3]+"-"+nn[24,12]
end

#get_scopes_from_themefilesObject



148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
# File 'lib/rmthemegen/plist_to_tokenlist.rb', line 148

def get_scopes_from_themefiles
   @scopes_found_count = {}
   @scopes_found = []
   @scopes_found_hash = {}
#      self.group_2_color = {}
   @color_2_group = {} #each key returns an array of strings, each of which is a scope that shares the color of the key 
   @scope_2_group = {} #key is a string for a scope, and the value is an array of strings that are scopes. 
   
   @uses_same_foreground = {} #the key is a color in uppercase hex such as #FFAAB1, etc. it points to an array of scope names that use this color. it is necessary to color certain elements the same, so as to achieve effects, like @a -- an object variable
   #should all be the same color not 1 color for the @ and 1 color for the a  
#    files_look_in = Dir[File.dirname(__FILE__)+"/textmate_themes/*.tmTheme"]
   files_look_in = Dir[File.dirname(__FILE__)+"/textmate_themes/IR_Black.tmTheme"]
#      files_look_in = Dir[File.dirname(__FILE__)+"/textmate_themes/Brilliance\ Black.tmTheme"]
   use_scope_threshhold =0 # a scope will be used only if it appears at least this number of times in the existing themes 

   num_sf =0
   
   files_look_in.each do |f|
     syntax_file = File.open(f,"r")
     indoc = REXML::Document.new( syntax_file )
     visit_all_nodes(indoc.root) { |k|
       begin
         if (k.respond_to?(:local_name) )
           if (k.parent.name=='dict' && (k.previous_element.respond_to?(:local_name)  ) )
             if ( k.previous_element.local_name=='key' && k.previous_element.text=="scope" )
               # the following monkey business allows us to see how many times we've seen a key
               num_sf += 1
               ssk = String.new(k.text.to_s)
               @scopes_found_count[ssk] ||= 0
               @scopes_found_count[ssk] += 1
               @scopes_found << ssk
               curstyle=get_style_here(k)
               @color_2_group[curstyle["foreground"]] ||= [] if curstyle["foreground"]
               #lku = ssk+unique_number.to_s #unique group string 
               if curstyle["foreground"]
                 @color_2_group[curstyle["foreground"]]<<ssk
               else 
                 @color_2_group[curstyle["nocolor"] ] << ssk
               end 
               @scope_2_group[ssk] = curstyle["foreground"] || "nocolor"
             end
           end
         end
       rescue Exception => e
         puts "an exception in process_plists(): "+e.to_s 
       end
     } 
   uses_same_foreground.delete_if do |k,v|  v.size == 1  end 
#     puts "uses same foreground: "+uses_same_foreground.inspect  
#     puts "Found #{@num_sf} scopes in file #{syntax_file.to_s}"    
   syntax_file.close       
   end #files_look_in.each

   self.scopes_found_count.each do |k,v|
     self.scopes_found_count.delete(k) unless v >= use_scope_threshhold
   end 
   
   outf=File.new("scopes_harvested","w")
   outf.printf "%s","["
   scopes_found.each do |k,v|
     outf.printf("%s","'"+k.to_s+"',\n ")
   end 
   outf.printf "]"
   outf.close 
puts "plist_to_tokenlist line 205: harvested #{scopes_found.size} scopes from #{files_look_in.size} files."  
   return scopes_found
end

#get_style_here(element) ⇒ Object



217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
# File 'lib/rmthemegen/plist_to_tokenlist.rb', line 217

def get_style_here(element)
  #this is for getting the style features of a single element, given that we are given an element within a top-level dict
  
  n = element
  fail=0
  while n.local_name != "dict" && fail < 15 
    n = n.next_element
    fail += 1
  end
  return nil if n.local_name != "dict" || !n.has_elements?
  
  h={} 
#       puts "plist_to_tokenlist#get_style_here (line 207) - n.local_name.inspect == "+n.local_name 
  n.elements.each do |kid|
      if kid.local_name == "key"
#puts "key found "+kid.text
        h[kid.text]=kid.next_element.text if kid.next_element
      end 
  end  
#puts "plist_to_tokenlist#get_style_here (line 207) - <dict> == "+h.inspect 
  return h
end

#make_dict(a_hash) ⇒ Object



188
189
190
191
192
193
194
195
196
197
198
199
# File 'lib/rmthemegen/rmtg_textmate.rb', line 188

def make_dict(a_hash)
  new_dict = REXML::Element.new("dict")
  a_hash.each do |k,v| 
    te1 = REXML::Element.new("key")      
    te1.add_text(k.to_s) 
    te2 = REXML::Element.new("string")      
    te2.add_text(v.to_s)
    new_dict.add_element te1
    new_dict.add_element te2
  end
  return new_dict
end

#make_name_scope_settings(ruby_symbol, an_array) ⇒ Object



201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
# File 'lib/rmthemegen/rmtg_textmate.rb', line 201

def make_name_scope_settings(ruby_symbol,an_array)
fontstyles = ["","bold","italic", "bold italic"]
  #the array looks like ["name","scope",{}] . the third element in the array is a hash for "settings"
    new_dict = REXML::Element.new("dict")
    te1 = REXML::Element.new("key")      
    te1.add_text("name") 
    te2 = REXML::Element.new("string")      
    te2.add_text(an_array[0])
    te3 = REXML::Element.new("key")      
    te3.add_text("scope")
    te4 = REXML::Element.new("string")      
    te4.add_text(an_array[1])
    te5 = REXML::Element.new("key")
    te5.add_text("settings")      
    new_dict.add_element te1
    new_dict.add_element te2
    new_dict.add_element te3
    new_dict.add_element te4
    new_dict.add_element te5
    fontStyle = fontstyles[@textmate_hash[ruby_symbol][:FONT_TYPE].to_i ]
    di1 = make_dict(:foreground => "#"+@textmate_hash[ruby_symbol][:FOREGROUND].upcase, :fontStyle=>fontStyle) 
    new_dict.add_element di1
  return new_dict
end

#make_name_scope_settings_rand(name, scope, settings) ⇒ Object



226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
# File 'lib/rmthemegen/rmtg_textmate.rb', line 226

def make_name_scope_settings_rand(name, scope, settings)
fontstyles = ["","bold","italic", "bold italic"]
  #the array looks like ["name","scope",{}] . the third element in the array is a hash for "settings"
    new_dict = REXML::Element.new("dict")
    te1 = REXML::Element.new("key")      
    te1.add_text("name") 
    te2 = REXML::Element.new("string")      
    te2.add_text(name.to_s)
    te3 = REXML::Element.new("key")      
    te3.add_text("scope")
    te4 = REXML::Element.new("string")      
    te4.text =scope.to_s
    te5 = REXML::Element.new("key")
    te5.add_text("settings")      
    new_dict.add_element te1
    new_dict.add_element te2
    new_dict.add_element te3
    new_dict.add_element te4
    new_dict.add_element te5
      
#puts "scope= #{scope} , group= #{@scope_2_group[scope]} , newcolor = "+newcolor.to_s          
    
    if @new_color_to_group[scope] 
puts "scope= #{scope} , group= #{@scope_2_group[scope]} , newcolor = "+@new_color_to_group[scope][:foreground]+ " , fonStyle=#{@new_color_to_group[scope][:fontStyle]}"          
      di1 = make_dict(@new_color_to_group[scope])   
    else
      di1 = make_dict(:fontStyle=>'')
    end 
  new_dict.add_element di1
  return new_dict
end

#make_theme_file(outputdir = , bg_color_style = :dark, colorsets = [], rand_seed = nil, opts_hash = {}) ⇒ Object



77
78
79
80
81
82
83
84
# File 'lib/rmthemegen/rmtg_textmate.rb', line 77

def make_theme_file( outputdir = ENV["PWD"], bg_color_style=:dark, colorsets=[], rand_seed=nil, opts_hash={} )
  outt=create_textmate_theme(bg_color_style , colorsets, rand_seed, opts_hash) #@themename gets set by that there call, so we need it to happen before we use the filename
  @savefile = File.expand_path(outputdir)+"/rmt_"+@themename+".tmTheme"
  File.open(@savefile, "w") do |f|
    f.puts( outt )
  end 
  @savefile
end

#make_theme_from_hashObject



70
71
# File 'lib/rmthemegen/rmtg_textmate.rb', line 70

def make_theme_from_hash
end

#make_theme_text(bg_color_style = :dark, colorsets = [], rand_seed = nil, opts_hash = {}) ⇒ Object



73
74
75
# File 'lib/rmthemegen/rmtg_textmate.rb', line 73

def make_theme_text(bg_color_style=:dark, colorsets=[], rand_seed=nil, opts_hash={})
  create_textmate_theme( bg_color_style = :dark, colorsets=[], rand_seed=nil, opts_hash )
end

#process_plistsObject

process_plists is an attempt to take from teh syntax files that are included as “textmate bundles” and use the tokens described to create syntax-themes. This attempt has so far been unsuccessful. all of the example theme files found in the wild do not use the tokens found in the syntax files, so there is some other way it has of mapping code to symbols.



25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
# File 'lib/rmthemegen/plist_to_tokenlist.rb', line 25

def process_plists
  @for_tm_output = {}
  files_look_in = Dir[File.dirname(__FILE__)+"/syntaxes/*.plist"]
  files_look_in = Dir[File.dirname(__FILE__)+"/syntaxes/Ruby.plist"]
  puts files_look_in.inspect
  
  files_look_in.each do |f|
    puts "opening file "+f.to_s 
    syntax_file = File.open(f,"r")
    indoc = REXML::Document.new( syntax_file )


    visit_all_nodes(indoc.root) { |k|
      begin
        if (k.respond_to?(:local_name) )
          if (k.parent.name=='dict' && (k.previous_element.respond_to?(:local_name)  ) )
            if ( k.previous_element.local_name=='key' && k.previous_element.text=="name" )
            @for_tm_output[k.text.to_s]=''
            end
          end
        end
      rescue => e
        puts "an exception in process_plists(): "+e.to_s 
      end
    } 

  # take the list of tokens. Find all unique word.word prefixes. Each of them becomes a key in a new hash
  # Each key points to a long string made up of a concatenation of all of the tokens from the list whose prefix
  # matches the found prefix.
  # so, given keyword.other.new.php and keyword.other.phpdoc.doc, we would have @hash['keyword.other']="keyword.other.new.php and keyword.other.phpdoc.doc"
  
  token_ary = []
  @for_tm_output.each do |k,v|
    token_ary << k.to_s
  end 
      
  token_ary.sort!
  token_ary.each do |i|
  end 
  
  @nhash2 = {}
  @for_tm_output.each_key do |t|
    tkary=t.to_s.split(".")
    if tkary.size >= 2
      @nhash2[ tkary[0]+"."+tkary[1] ] ||= ''
      @nhash2[ tkary[0]+"."+tkary[1] ] += t+" "
    else
      @nhash2[t]=''
    end
  end
  
  @repository_names = {}

  indoc.root.elements.each("*/dict") do |e|
    #okay, so this does reliably give us the top-level names (each of the ee's is one of them) 
    e.elements.each do |ee|          
      if ee.name == "key" 
        puts " ***** " 
        @repository_names[ee.text.to_s] = ''
        puts ee.to_s 
        puts " ***** " 
        if ee.next_element 
          visit_all_nodes(ee.next_element)  do |eer|
              if eer.name == "string" && eer.previous_element.name == "key" &&  eer.previous_element.text == "name"
                @repository_names[ee.text.to_s] += eer.text.to_s+" " 
              end
          end 
        end 
      end 
    end 
  end 
  
  @under_patterns={}
  indoc.root.elements.each("*/key") do |e|
      main_name=''
      if e.text == "patterns"  
        e.next_element.elements.each do |ee| #this is the main array of patterns -- these should be the dicts who define the patterns caught by the syntax engine
          if ee.name=="dict"
#              puts "<<>>"+ee.inspect+"<<>>"
#              puts "<<again>>"+ee.elements.size.to_s+"<<>>"
            ee.elements.each do |cc|
              if  ["name", "contentName"].include? cc.text 
              main_name = cc.next_element.text   
              puts "main_name"+main_name
              @under_patterns[main_name]=""
              end 
            end
          
            ee.elements.each do |di|
              if di.name="dict"
                di.elements.each do |di_ch|
                  di_ch.elements.each do |leaf|
                    if leaf.previous_element 
                      @under_patterns[main_name] += leaf.text.to_s+", "  if (leaf.name=="string" && leaf.previous_element.text == "name") 
                    end 
                  end 
                end 
              end 
            end
          end 
        end
      end  
  end
#    @under_patterns.each_key do |k| @under_patterns[k] = @under_patterns[k][ 0, @under_patterns[k].size-2 ] end 
  @under_patterns.each do |k,v| @under_patterns[k] = v[ 0, v.size-2 ] end 
  syntax_file.close       
end #files_look_in.each
  
end

#set_doc_colorsObject



63
64
# File 'lib/rmthemegen/rmtg_textmate.rb', line 63

def set_doc_colors
end

#set_doc_options(dd) ⇒ Object



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/rmthemegen/rmtg_textmate.rb', line 39

def set_doc_options(dd)
   @document_globals[:backgroundcolor]=@backgroundcolor
   @document_globals[:caret_color]= randcolor(:bg_rgb=>@backgroundcolor, :min_cont=>0.30,:max_cont=>0.7,:shade_of_grey=>true)
   @document_globals[:text]=randcolor(:bg_rgb=>@backgroundcolor, :min_cont=>0.24,:max_cont=>0.7,:shade_of_grey=>false) #TEXT should hardly ever appears, since every possible color is stipulated
   @document_globals[:caret_row_color] = randcolor(:bg_rgb=>@backgroundcolor,:min_cont=>0.05,:max_cont => 0.08,:shade_of_grey=>false)
   @document_globals[:selection_background] = randcolor(:bg_rgb=>@backgroundcolor,:min_cont=>0.07,:max_cont => 0.09,:shade_of_grey=>false)

   dd.add_element(
      make_dict(
      :background=>"#"+@document_globals[:backgroundcolor].upcase,
      :caret=>"#"+ @document_globals[:caret_color].upcase ,
      :foreground=>"#"+@document_globals[:text].upcase,
      :invisibles=>"#"+@document_globals[:backgroundcolor].upcase,
      :lineHighlight=>"#"+@document_globals[:caret_row_color].upcase,
      :selection=>"#"+@document_globals[:selection_background].upcase
      ) 
   ) 
end

#set_element_colorsObject



66
67
# File 'lib/rmthemegen/rmtg_textmate.rb', line 66

def set_element_colors
end

#unique_number(n = -1)) ⇒ Object



19
20
21
22
23
# File 'lib/rmthemegen/rmtg_textmate.rb', line 19

def unique_number(n=-1)
   @unique_num ||= n
   @unique_num += 1 
   @unique_num
end

#visit_all_nodes(element, &block) ⇒ Object

process_plists



135
136
137
138
139
140
141
142
143
144
145
# File 'lib/rmthemegen/plist_to_tokenlist.rb', line 135

def visit_all_nodes(element, &block)
    if element.is_a?(REXML::Element) 
      if element.has_elements? then 
        element.each do |kkid|
          visit_all_nodes( kkid, &block) 
        end
      else
        yield element
      end           
    end 
end