sleipnir-api 0.2.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/History.txt +32 -0
- data/Manifest.txt +20 -3
- data/Rakefile +11 -1
- data/TODO.txt +4 -10
- data/bin/grepnir +1 -1
- data/examples/close_dup.rb +1 -1
- data/examples/close_err.rb +12 -0
- data/examples/command_list.rb +135 -0
- data/examples/open_last_url_by_firefox.rb +44 -0
- data/examples/open_selected_links.rb +40 -0
- data/helper/rake.rb +3 -57
- data/helper/rake/rake.rb +58 -0
- data/helper/{rake_sh_filter.rb → rake/rake_sh_filter.rb} +0 -0
- data/helper/{util.rb → rake/util.rb} +0 -0
- data/lib/sleipnir_api/cli/grepnir.rb +2 -3
- data/lib/sleipnir_api/command.rb +126 -0
- data/lib/sleipnir_api/commands.rb +264 -0
- data/lib/sleipnir_api/profile.rb +166 -27
- data/lib/sleipnir_api/profile/ini.rb +202 -0
- data/lib/sleipnir_api/profile/key.rb +120 -0
- data/lib/sleipnir_api/profile/section.rb +147 -0
- data/lib/sleipnir_api/profile/util.rb +82 -0
- data/lib/sleipnir_api/searcher.rb +133 -0
- data/lib/sleipnir_api/security.rb +88 -18
- data/lib/sleipnir_api/sleipnir.rb +91 -7
- data/lib/sleipnir_api/tab.rb +68 -2
- data/lib/sleipnir_api/tabbed_ie.rb +33 -0
- data/lib/sleipnir_api/util.rb +25 -0
- data/lib/sleipnir_api/version.rb +2 -2
- data/scripts/gesture2rb.rb +42 -0
- data/scripts/make_gesture.rb +271 -0
- data/scripts/subcategory.csv +253 -0
- data/spec/sleipnir_api/profile_mock_spec.rb +80 -6
- data/spec/sleipnir_api/profile_section_mock_spec.rb +143 -0
- data/spec/sleipnir_api/profile_spec.rb +191 -0
- data/spec/sleipnir_api/security_spec.rb +58 -0
- data/spec/sleipnir_api/tab_mock_spec.rb +0 -5
- data/spec/sleipnir_api/tab_spec.rb +27 -0
- data/spec/spec_helper.rb +20 -0
- metadata +22 -5
- data/helper/helper.rb +0 -3
@@ -0,0 +1,120 @@
|
|
1
|
+
require "sleipnir_api/util"
|
2
|
+
require "sleipnir_api/profile/util"
|
3
|
+
|
4
|
+
module SleipnirAPI
|
5
|
+
class Profile
|
6
|
+
|
7
|
+
# ini ファイルのキーに対応するオブジェクトです。
|
8
|
+
# このオブジェクトは SleipnirAPI::Profile::Section#key で取得します。
|
9
|
+
#
|
10
|
+
# pnir = SleipnirAPI.new
|
11
|
+
# ini = pnir.profile("CloseURL.ini")
|
12
|
+
# sec = ini.section("ClosedURL2")
|
13
|
+
# key = sec.key("URL0")
|
14
|
+
# key.to_s #=> "http://hogehoge"
|
15
|
+
#
|
16
|
+
class Key < ProfileElement
|
17
|
+
|
18
|
+
# SleipnirAPI::Profile::Section オブジェクトを返します
|
19
|
+
attr_reader :parent
|
20
|
+
|
21
|
+
# SleipnirAPI::Profile::Section オブジェクトを返します
|
22
|
+
alias section parent
|
23
|
+
|
24
|
+
# キー名を返します。
|
25
|
+
attr_reader :name
|
26
|
+
|
27
|
+
# #get_string などに指定するデフォルトの引数を取得します。
|
28
|
+
# 値は #parent から引き継いでいます。
|
29
|
+
attr_reader :default_opts
|
30
|
+
|
31
|
+
def initialize(parent, name, default_opts = nil)
|
32
|
+
super(parent, default_opts)
|
33
|
+
@name = name
|
34
|
+
end
|
35
|
+
|
36
|
+
# call-seq:
|
37
|
+
# list(countkey="Count")
|
38
|
+
# list(countkey="Count", :default => "default value")
|
39
|
+
# list(countkey="Count", :cipher => true)
|
40
|
+
# list(countkey="Count", :cipher => true, :default => "default value")
|
41
|
+
#
|
42
|
+
# このキーから連番キーを生成して、連番データをすべて読み込み配列で返します。
|
43
|
+
#
|
44
|
+
# 例:
|
45
|
+
#
|
46
|
+
# pnir = SleipnirAPI.connect
|
47
|
+
# proxy_ini = pnir.profile.proxy
|
48
|
+
# proxy_sec = proxy_ini.Proxy
|
49
|
+
# proxy_title = proxy_sec.Proxy0_Title
|
50
|
+
# proxy_title.list
|
51
|
+
#
|
52
|
+
# See Also: SleipnirAPI::Profile#list
|
53
|
+
def list(countkey = "Count", opts = nil, &block)
|
54
|
+
@parent.list(@name, countkey, options(opts), &block)
|
55
|
+
end
|
56
|
+
alias to_a list
|
57
|
+
|
58
|
+
# call-seq:
|
59
|
+
# get_string()
|
60
|
+
# get_string(:default => -1)
|
61
|
+
# get_string(:cipher => true)
|
62
|
+
# get_string(:cipher => true, :default => -1)
|
63
|
+
#
|
64
|
+
# このキーの string 値を取得します。
|
65
|
+
#
|
66
|
+
# See Also: SleipnirAPI::Profile#get_string
|
67
|
+
def get_string(opts = nil)
|
68
|
+
@parent.get_string(@name, options(opts))
|
69
|
+
end
|
70
|
+
alias to_s get_string
|
71
|
+
|
72
|
+
# call-seq:
|
73
|
+
# get_int()
|
74
|
+
# get_int(:default => -1)
|
75
|
+
# get_int(:cipher => true)
|
76
|
+
# get_int(:cipher => true, :default => -1)
|
77
|
+
#
|
78
|
+
# このキーの int 値を取得します。
|
79
|
+
#
|
80
|
+
# See Also: SleipnirAPI::Profile#get_int
|
81
|
+
def get_int(opts = nil)
|
82
|
+
@parent.get_int(@name, options(opts))
|
83
|
+
end
|
84
|
+
alias to_i get_int
|
85
|
+
|
86
|
+
# call-seq:
|
87
|
+
# write_string(data)
|
88
|
+
# write_string(data, :cipher => true)
|
89
|
+
#
|
90
|
+
# このキーに指定されたデータを書き込みます。
|
91
|
+
# script.ini でない場合は SleipnirAPI::Profile::ReadOnlyError を raise します。
|
92
|
+
#
|
93
|
+
# See Also: SleipnirAPI::Profile#write_string
|
94
|
+
def write_string(data, opts = nil)
|
95
|
+
@parent.write_string(@name, data, options(opts))
|
96
|
+
end
|
97
|
+
|
98
|
+
# call-seq:
|
99
|
+
# write_int(data)
|
100
|
+
# write_int(data, :cipher => true)
|
101
|
+
#
|
102
|
+
# このキーに指定されたデータを書き込みます。
|
103
|
+
# script.ini でない場合は SleipnirAPI::Profile::ReadOnlyError を raise します。
|
104
|
+
#
|
105
|
+
# See Also: SleipnirAPI::Profile#write_int
|
106
|
+
def write_int(data, opts = nil)
|
107
|
+
@parent.write_int(@name, data, options(opts))
|
108
|
+
end
|
109
|
+
|
110
|
+
# このキーを削除します。
|
111
|
+
# script.ini でない場合は SleipnirAPI::Profile::ReadOnlyError を raise します。
|
112
|
+
#
|
113
|
+
# See Also: SleipnirAPI::Profile#delete
|
114
|
+
def delete
|
115
|
+
@parent.delete(name)
|
116
|
+
end
|
117
|
+
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,147 @@
|
|
1
|
+
require "sleipnir_api/util"
|
2
|
+
require "sleipnir_api/profile/util"
|
3
|
+
|
4
|
+
module SleipnirAPI
|
5
|
+
class Profile
|
6
|
+
|
7
|
+
# ini ファイルのセクションに対応するオブジェクトです。
|
8
|
+
# このオブジェクトは SleipnirAPI::Profile::Ini#section で取得します。
|
9
|
+
#
|
10
|
+
# pnir = SleipnirAPI.new
|
11
|
+
# ini = pnir.profile("CloseURL.ini")
|
12
|
+
# sec = ini.section("ClosedURL2")
|
13
|
+
# sec.get_string("URL0") #=> "http://hogehoge"
|
14
|
+
#
|
15
|
+
# #key でこのセクション内のキーに対応する SleipnirAPI::Profile::Key
|
16
|
+
# オブジェクトを取得できます。
|
17
|
+
class Section < ProfileElement
|
18
|
+
|
19
|
+
# SleipnirAPI::Profile::Ini オブジェクトを返します
|
20
|
+
attr_reader :parent
|
21
|
+
|
22
|
+
# SleipnirAPI::Profile::Ini オブジェクトを返します
|
23
|
+
alias ini parent
|
24
|
+
|
25
|
+
# セクション名を返します。
|
26
|
+
attr_reader :name
|
27
|
+
|
28
|
+
# セクション名を返します。
|
29
|
+
alias to_s name
|
30
|
+
|
31
|
+
# #get_string などに指定するデフォルトの引数を取得します。
|
32
|
+
# 値は #parent から引き継いでいます。
|
33
|
+
attr_reader :default_opts
|
34
|
+
|
35
|
+
def initialize(parent, name, default_opts = nil)
|
36
|
+
super(parent, default_opts)
|
37
|
+
@name = name
|
38
|
+
end
|
39
|
+
|
40
|
+
# call-seq:
|
41
|
+
# list(keyfmt, countkey="Count")
|
42
|
+
# list(keyfmt, countkey="Count", :default => "default value")
|
43
|
+
# list(keyfmt, countkey="Count", :cipher => true)
|
44
|
+
# list(keyfmt, countkey="Count", :cipher => true, :default => "default value")
|
45
|
+
#
|
46
|
+
# このセクションから連番キーのデータをすべて読み込み配列で返します。
|
47
|
+
#
|
48
|
+
# 例:
|
49
|
+
#
|
50
|
+
# pnir = SleipnirAPI.connect
|
51
|
+
# proxy_ini = pnir.profile.proxy
|
52
|
+
# proxy_sec = proxy_ini.Proxy
|
53
|
+
# proxy_sec.list("Proxy0_Title")
|
54
|
+
#
|
55
|
+
# See Also: SleipnirAPI::Profile#list
|
56
|
+
def list(keyformat, countkey = "Count", opts = nil, &block)
|
57
|
+
@parent.list(@name, keyformat, countkey, options(opts), &block)
|
58
|
+
end
|
59
|
+
|
60
|
+
# call-seq:
|
61
|
+
# get_string(key)
|
62
|
+
# get_string(key, :default => -1)
|
63
|
+
# get_string(key, :cipher => true)
|
64
|
+
# get_string(key, :cipher => true, :default => -1)
|
65
|
+
#
|
66
|
+
# このセクションの指定されたキーを取得します。
|
67
|
+
#
|
68
|
+
# See Also: SleipnirAPI::Profile#get_string
|
69
|
+
def get_string(key, opts = nil)
|
70
|
+
@parent.get_string(@name, key, options(opts))
|
71
|
+
end
|
72
|
+
alias [] get_string
|
73
|
+
|
74
|
+
# call-seq:
|
75
|
+
# get_int(key)
|
76
|
+
# get_int(key, :default => -1)
|
77
|
+
# get_int(key, :cipher => true)
|
78
|
+
# get_int(key, :cipher => true, :default => -1)
|
79
|
+
#
|
80
|
+
# このセクションの指定されたキーを取得します。
|
81
|
+
#
|
82
|
+
# See Also: SleipnirAPI::Profile#get_int
|
83
|
+
def get_int(key, opts = nil)
|
84
|
+
@parent.get_int(@name, key, options(opts))
|
85
|
+
end
|
86
|
+
|
87
|
+
# call-seq:
|
88
|
+
# write_string(key, data)
|
89
|
+
# write_string(key, data, :cipher => true)
|
90
|
+
#
|
91
|
+
# このセクションの指定されたキーを書き込みます。
|
92
|
+
# script.ini でない場合は SleipnirAPI::Profile::ReadOnlyError を raise します。
|
93
|
+
#
|
94
|
+
# See Also: SleipnirAPI::Profile#write_string
|
95
|
+
def write_string(key, data, opts = nil)
|
96
|
+
@parent.write_string(@name, key, data, options(opts))
|
97
|
+
end
|
98
|
+
|
99
|
+
# call-seq:
|
100
|
+
# write_int(key, data)
|
101
|
+
# write_int(key, data, :cipher => true)
|
102
|
+
#
|
103
|
+
# このセクションの指定されたキーを書き込みます。
|
104
|
+
# script.ini でない場合は SleipnirAPI::Profile::ReadOnlyError を raise します。
|
105
|
+
#
|
106
|
+
# See Also: SleipnirAPI::Profile#write_int
|
107
|
+
def write_int(key, data, opts = nil)
|
108
|
+
@parent.write_int(@name, key, data, options(opts))
|
109
|
+
end
|
110
|
+
|
111
|
+
# このセクションの指定されたキーを削除します。
|
112
|
+
# script.ini でない場合は SleipnirAPI::Profile::ReadOnlyError を raise します。
|
113
|
+
#
|
114
|
+
# See Also: SleipnirAPI::Profile#delete
|
115
|
+
def delete(key)
|
116
|
+
@parent.delete(@name, key)
|
117
|
+
end
|
118
|
+
|
119
|
+
# キーを操作する Sleipnir::Profile::Key オブジェクトを返します。
|
120
|
+
#
|
121
|
+
# pnir = SleipnirAPI.connect
|
122
|
+
# proxy = pnir.profile.ini("Proxy.ini", :default => 123)
|
123
|
+
# sec = proxy.section("Proxy")
|
124
|
+
# key = sec.key("Count")
|
125
|
+
# key.get_int
|
126
|
+
#
|
127
|
+
# See Also: #method_missing
|
128
|
+
def key(name, opts = nil)
|
129
|
+
Key.new(self, str(name), options(opts))
|
130
|
+
end
|
131
|
+
|
132
|
+
# メソッド名をキー名とみなして Sleipnir::Profile::Key オブジェクトを返します。
|
133
|
+
#
|
134
|
+
# pnir = SleipnirAPI.connect
|
135
|
+
# proxy = pnir.profile.Proxy(:default => 123)
|
136
|
+
# sec = proxy.Proxy
|
137
|
+
# key = sec.Count
|
138
|
+
# key.get_int
|
139
|
+
#
|
140
|
+
# See Also: #key
|
141
|
+
def method_missing(mid, *args, &block)
|
142
|
+
key(mid.to_s, *args, &block)
|
143
|
+
end
|
144
|
+
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require "sleipnir_api/util"
|
2
|
+
|
3
|
+
module SleipnirAPI
|
4
|
+
class Profile
|
5
|
+
#
|
6
|
+
# Script.ini 以外の ini ファイルに対して、書き込み (write_int, write_string, delete)
|
7
|
+
# を行った場合に raise する例外
|
8
|
+
#
|
9
|
+
class ReadOnlyError < StandardError; end
|
10
|
+
|
11
|
+
module OptionArgument #:nodoc:
|
12
|
+
include SleipnirAPI::OptionUtil
|
13
|
+
|
14
|
+
def options(opts, *valid_keys)
|
15
|
+
if opts and not Hash === opts
|
16
|
+
raise ArgumentError, "Invalid options: #{opts.inspect}:#{opts.class} (expected Hash)"
|
17
|
+
end
|
18
|
+
r = @default_opts ? @default_opts.dup : {}
|
19
|
+
valid_keys = [:cipher, :ini, :default] if valid_keys.empty?
|
20
|
+
if opts
|
21
|
+
invalid = (opts.keys - valid_keys).inject({}){|acc,e| acc[e] = opts[e]; acc }
|
22
|
+
unless invalid.empty?
|
23
|
+
raise ArgumentError, "Invalid options: #{invalid.inspect}"
|
24
|
+
end
|
25
|
+
r.update(opts)
|
26
|
+
end
|
27
|
+
r
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
module DataUtil #:nodoc:
|
32
|
+
def str(*args)
|
33
|
+
args = args.map{|e|
|
34
|
+
case e
|
35
|
+
when String
|
36
|
+
e
|
37
|
+
when Symbol
|
38
|
+
e.to_s
|
39
|
+
else
|
40
|
+
raise ArgumentError, "Invalid date #{e.inspect}:#{e.class} (expected String or Symbol)"
|
41
|
+
end
|
42
|
+
}
|
43
|
+
(args.length == 1) ? args[0] : args
|
44
|
+
end
|
45
|
+
|
46
|
+
def check_data(section, key, data, expected)
|
47
|
+
unless expected === data
|
48
|
+
raise ArgumentError, "Invalid data `#{data.inspect}' (#{data.class}): section=#{section}, key=#{key} (expected #{expected})"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
class ProfileElement #:nodoc:
|
54
|
+
include OptionArgument
|
55
|
+
include DataUtil
|
56
|
+
|
57
|
+
def initialize(parent, default_opts)
|
58
|
+
@parent = parent
|
59
|
+
@default_opts = default_opts || {}
|
60
|
+
end
|
61
|
+
|
62
|
+
def parents
|
63
|
+
@parent.nil? ? [] : @parent.nodes
|
64
|
+
end
|
65
|
+
|
66
|
+
def nodes
|
67
|
+
parents + [self]
|
68
|
+
end
|
69
|
+
|
70
|
+
def inspect
|
71
|
+
args = [
|
72
|
+
self.class,
|
73
|
+
object_id << 1,
|
74
|
+
nodes.map{|e| e.name }.join("/"),
|
75
|
+
@default_opts.inspect,
|
76
|
+
]
|
77
|
+
"#<%s:0x%x: %s, opts=%s>" % args
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,133 @@
|
|
1
|
+
require "sleipnir_api/util"
|
2
|
+
|
3
|
+
module SleipnirAPI
|
4
|
+
|
5
|
+
class Searcher #:nodoc:
|
6
|
+
include SleipnirAPI::Util
|
7
|
+
include SleipnirAPI::OptionUtil
|
8
|
+
|
9
|
+
HILIGHT_TAG = "span"
|
10
|
+
HILIGHT_CLASSNAME = "ruby-sleipnir-api-hilight"
|
11
|
+
HILIGHT_STYLE = "color: black; background-color: #ffff66; font-weight: bold"
|
12
|
+
|
13
|
+
def initialize(tab)
|
14
|
+
@tab = tab
|
15
|
+
@range = nil
|
16
|
+
end
|
17
|
+
|
18
|
+
def each_find_text(keyword, range = nil)
|
19
|
+
range = @tab.document.body.createTextRange
|
20
|
+
while range.FindText(keyword)
|
21
|
+
yield range
|
22
|
+
range.Collapse(false)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def find_text(keyword, *options)
|
27
|
+
opts, _ = parse_option_arguments(options)
|
28
|
+
|
29
|
+
if not @range
|
30
|
+
@range = @tab.document.body.createTextRange
|
31
|
+
else
|
32
|
+
@range.Collapse(false)
|
33
|
+
end
|
34
|
+
|
35
|
+
if @range.FindText(keyword)
|
36
|
+
@range.Select if opts[:select]
|
37
|
+
@range.ScrollIntoView if opts[:scroll]
|
38
|
+
true
|
39
|
+
else
|
40
|
+
@tab.document.Selection.empty if opts[:select]
|
41
|
+
@range = nil
|
42
|
+
false
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def hilight(*keywords)
|
47
|
+
remove_hilight
|
48
|
+
range = @tab.document.body.createTextRange
|
49
|
+
matches = []
|
50
|
+
keywords.each do |kwd|
|
51
|
+
each_find_text(kwd, range) do |range|
|
52
|
+
matched_html = range.HTMLText
|
53
|
+
matches << matched_html.gsub(/\A(<[^<>]+>)+/, "").gsub(/(<[^<>]+>)+\z/, "")
|
54
|
+
end
|
55
|
+
range.Collapse(true)
|
56
|
+
end
|
57
|
+
|
58
|
+
all=@tab.document.body.innerHTML
|
59
|
+
@tab.document.body.innerHTML = matches.uniq.inject(all) do |all,m|
|
60
|
+
add_hilight_tag(m, all)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def add_hilight_tag(matched, html=nil, tag=HILIGHT_TAG, classname=HILIGHT_CLASSNAME, style=HILIGHT_STYLE)
|
65
|
+
if html.nil?
|
66
|
+
"<#{tag} class='#{classname}' style='#{style}'>#{matched}</#{tag}>"
|
67
|
+
else
|
68
|
+
matched = matched.gsub(/\A(<[^<>]+>)+/, "").gsub(/(<[^<>]+>)+\z/, "")
|
69
|
+
html.gsub(/#{Regexp.quote(matched)}/) do
|
70
|
+
matched.split(/(<[^<>]+>)/).map{|part|
|
71
|
+
if part[0] != ?< and part =~ /\S/
|
72
|
+
"<#{tag} class='#{classname}' style='#{style}'>#{part}</#{tag}>"
|
73
|
+
else
|
74
|
+
part
|
75
|
+
end
|
76
|
+
}.join
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def remove_hilight
|
82
|
+
@tab.document.body.getElementsByTagName(HILIGHT_TAG).each do |span|
|
83
|
+
span.OuterHTML = span.InnerHTML if span.ClassName == HILIGHT_CLASSNAME
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
=begin
|
88
|
+
def hilight(*keywords)
|
89
|
+
remove_hilight
|
90
|
+
range = @tab.document.body.createTextRange
|
91
|
+
keywords.each do |kwd|
|
92
|
+
each_find_text(kwd, range) do |range|
|
93
|
+
matched_html = range.HTMLText
|
94
|
+
parent_tags = matched_html[/\A(?:<[^<>]+>)+/]
|
95
|
+
if parent_tags.nil?
|
96
|
+
range.PasteHTML(add_hilight_tag(matched_html))
|
97
|
+
else
|
98
|
+
e = parent_tags.scan(/<(\w+)/).flatten.reverse.inject(range.ParentElement) do |elem,tag|
|
99
|
+
if elem.TagName == tag
|
100
|
+
elem.ParentElement
|
101
|
+
elsif elem.InnerHTML =~ /\A<#{tag}/i
|
102
|
+
elem
|
103
|
+
else
|
104
|
+
raise "#{elem}:#{tag}:#{parent_tags.inspect}"
|
105
|
+
end
|
106
|
+
end
|
107
|
+
e.innerHTML = add_hilight_tag(matched_html, e.innerHTML)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
range.Collapse(true)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def add_hilight_tag(matched, html=nil, tag=HILIGHT_TAG, classname=HILIGHT_CLASSNAME, style=HILIGHT_STYLE)
|
115
|
+
if html.nil?
|
116
|
+
"<#{tag} class='#{classname}' style='#{style}'>#{matched}</#{tag}>"
|
117
|
+
else
|
118
|
+
matched = matched.gsub(/\A(<[^<>]+>)+/, "").gsub(/(<[^<>]+>)+\z/, "")
|
119
|
+
html.sub(/#{Regexp.quote(matched)}/) do
|
120
|
+
matched.split(/(<[^<>]+>)/).map{|part|
|
121
|
+
if part[0] != ?< and part =~ /\S/
|
122
|
+
"<#{tag} class='#{classname}' style='#{style}'>#{part}</#{tag}>"
|
123
|
+
else
|
124
|
+
part
|
125
|
+
end
|
126
|
+
}.join
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
=end
|
131
|
+
|
132
|
+
end
|
133
|
+
end
|