=end
class EmptyElementTag
def initialize(name,attr=nil)
@name = name.downcase
@attr = attr
end
attr_accessor :name
attr_accessor :attr
def to_s
if @attr
"<"+@name+@attr.keys.sort.collect{|n|
v = @attr[n]
if v==true
' ' + n
else
' ' + n + '="' + CGI::escapeHTML(v) + '"'
end
}.to_s+">"
else
"<#{@name}>"
end
end
def [](key)
attr and attr[key]
end
def []=(key,value)
if attr
attr[key]=value
else
attr = value and {key=>value}
end
end
end
=begin StartTag
=end
class StartTag
attr_accessor :name
attr_accessor :attr
def initialize(name,attr=nil)
@name = name.downcase
@attr = attr
end
def to_s
if @attr
"<"+@name+@attr.keys.sort.collect{|n|
v = @attr[n]
if v==true
' ' + n
else
' ' + n + '="' + CGI::escapeHTML(v) + '"'
end
}.to_s+">"
else
"<#{@name}>"
end
end
def [](key)
attr and attr[key]
end
def []=(key,value)
if attr
attr[key]=value
else
attr = value and {key=>value}
end
end
end
=begin EndTag
EndTag
終了タグ
クラスメソッド
new(name)
新しいオブジェクトを生成する。 nameはタグの名前
メソッド
name
タグ名を返す。
to_s
HTMLを返す。
=end
class EndTag
def initialize(name)
@name = name.downcase
end
attr_accessor :name
def to_s
"#{@name}>"
end
end
=begin CharacterData
CharacterData
文字データ
クラスメソッド
new(text)
新しいオブジェクトを生成する。 textはテキスト
メソッド
text
テキストを返す。
to_s
HTMLを返す。
=end
class CharacterData
def initialize(text)
@text = text
end
attr_accessor :text
def to_s
@text
end
end
=begin Declaraion
Declaraion
SGML宣言
クラスメソッド
new(text)
新しいオブジェクトを生成する。 textはテキスト
メソッド
text
テキストを返す。
to_s
HTMLを返す。
=end
class Declaration
def initialize(text)
@text = text
end
attr_accessor :text
def to_s
""
end
end
=begin Comment
Comment
コメント
クラスメソッド
new(text)
新しいオブジェクトを生成する。 textはテキスト
メソッド
text
テキストを返す。
to_s
HTMLを返す。
=end
class Comment
def initialize(text)
@text = text
end
attr_accessor :text
def to_s
""
end
end
=begin SSI
SSI
SSI
クラスメソッド
new(text)
新しいオブジェクトを生成する。 textはテキスト
メソッド
text
テキストを返す。
to_s
HTMLを返す。
=end
class SSI
def initialize(text)
@text = text
end
attr_accessor :text
def to_s
""
end
end
=begin ERuby
ERuby
eRuby/ASP/JSPスクリプト
クラスメソッド
new(text)
新しいオブジェクトを生成する。 textはテキスト
メソッド
text
テキストを返す。
to_s
HTMLを返す。
=end
class ERuby
def initialize(text)
@text = text
end
attr_accessor :text
def to_s
"<%#{@text}%>"
end
end
=begin PHP
PHP
PHPスクリプト
クラスメソッド
new(text)
新しいオブジェクトを生成する。 textはテキスト
メソッド
text
テキストを返す。
to_s
HTMLを返す。
=end
class PHP
attr_accessor :text
def initialize(text)
@text = text
end
def to_s
"#{@text}?>"
end
end
=begin HTMLSplit
=end
class HTMLSplit
EMPTY = %w(area base basefont bgsound br col frame hr img input isindex
keygen link meta nextid param spacer wbr)
def initialize(html)
@document = [] #パースしたHTMLのリスト
name = ''
text = ''
attr = {}
attrname = ''
state = :TEXT
#
html.each_byte {|c|
char = c.chr
case state
when :TEXT
if c==60
if text.length>0
@document << CharacterData.new(text)
end
name = ''
attr={}
state = :TAGNAME
else
text << char
end
when :TAGNAME
case char
when '>'
name.downcase!
if EMPTY.include?(name)
@document << EmptyElementTag.new(name,nil)
else
if name[0,1]=='/'
@document << EndTag.new(name[1..-1])
else
@document << StartTag.new(name,nil)
end
end
text = ''
state = :TEXT
when '!'
text = ''
state = :DECLARE
when '%'
text = ''
state = :ERUBY
when '?'
text = ''
state = :PHP
when /\s/
text=''
state = :SPACE
else
name << char
end
when :SPACE #属性間の空白
case char
when '>'
name.downcase!
if EMPTY.include?(name)
@document << EmptyElementTag.new(name,attr)
else
if name[0,1]=='/'
@document << EndTag.new(name[1..-1])
else
@document << StartTag.new(name,attr)
end
end
text = ''
state = :TEXT
when /\s/
else
attrname=char
state = :ATTRNAME
end
when :ATTRNAME #属性名
case char
when /\s/
state = :BEFOREEQUAL
when '='
state = :AFTEREQUAL
when '>'
attr[attrname.downcase]=true
name.downcase!
if EMPTY.include?(name)
@document << EmptyElementTag.new(name,attr)
else
if name[0,1]=='/'
@document << EndTag.new(name[1..-1])
else
@document << StartTag.new(name,attr)
end
end
text = ''
state = :TEXT
else
attrname << char
end
when :BEFOREEQUAL #=
case char
when '='
state = :AFTEREQUAL
when '>'
attr[attrname.downcase]=true
name.downcase!
if EMPTY.include?(name)
@document << EmptyElementTag.new(name,attr)
else
if name[0,1]=='/'
@document << EndTag.new(name[1..-1])
else
@document << StartTag.new(name,attr)
end
end
text = ''
state = :TEXT
when /\s/
else
attr[attrname.downcase]=true
attrname = char
state = :ATTRNAME
end
when :AFTEREQUAL #=
case char
when "'"
text=''
state = :SQVALUE
when '"'
text=''
state = :DQVALUE
when '>'
attr[attrname.downcase]=true
name.downcase!
if EMPTY.include?(name)
@document << EmptyElementTag.new(name,attr)
else
if name[0,1]=='/'
@document << EndTag.new(name[1..-1])
else
@document << StartTag.new(name,attr)
end
end
text = ''
state = :TEXT
when /\s/
else
text=char
state = :VALUE
end
when :VALUE #値
case char
when /\s/
attr[attrname.downcase]=CGI::unescapeHTML(text)
state = :SPACE
when '>'
attr[attrname.downcase]=CGI::unescapeHTML(text)
name.downcase!
if EMPTY.include?(name)
@document << EmptyElementTag.new(name,attr)
else
if name[0,1]=='/'
@document << EndTag.new(name[1..-1])
else
@document << StartTag.new(name,attr)
end
end
text = ''
state = :TEXT
else
text << char
end
when :SQVALUE #'値'
if c==39
attr[attrname.downcase]=CGI::unescapeHTML(text)
state = :SPACE
else
text << char
end
when :DQVALUE #"値"
if c==34
attr[attrname.downcase]=CGI::unescapeHTML(text)
state = :SPACE
else
text << char
end
when :COMMENT
case char
when '>'
if text[-2,2]=='--' #コメント終了
text = text[0..-3]
if text=~/^#[a-z]+/ #SSI
@document << SSI.new(text)
else
@document << Comment.new(text)
end
text = ''
state = :TEXT
else
text << char
end
else
text << char
end
when :ERUBY
case char
when '>'
if text[-1,1]=='%' #eRuby終了
text = text[0..-2]
@document << ERuby.new(text)
text = ''
state = :TEXT
else
text << char
end
else
text << char
end
when :PHP
case char
when '>'
if text[-1,1]=='?' #eRuby終了
text = text[0..-2]
@document << PHP.new(text)
text = ''
state = :TEXT
else
text << char
end
else
text << char
end
when :DECLARE
case char
when '>'
@document << Declaration.new(text)
text = ''
state = :TEXT
else
text << char
if text=='--'
text = ''
state = :COMMENT
end
end
end
}
#EOFの処理
case state
when :TEXT
@document << CharacterData.new(text) if text.length>0
when :TAGNAME
@document << CharacterData.new('<'+text)
when :SPACE #属性間の空白
name.downcase!
if EMPTY.include?(name)
@document << EmptyElementTag.new(name,attr)
else
if name[0,1]=='/'
@document << EndTag.new(name[1..-1])
else
@document << StartTag.new(name,attr)
end
end
when :ATTRNAME #属性名
attr[attrname.downcase]=true
name.downcase!
if EMPTY.include?(name)
@document << EmptyElementTag.new(name,attr)
else
if name[0,1]=='/'
@document << EndTag.new(name[1..-1])
else
@document << StartTag.new(name,attr)
end
end
when :BEFOREEQUAL #=
attr[attrname.downcase]=true
name.downcase!
if EMPTY.include?(name)
@document << EmptyElementTag.new(name,attr)
else
if name[0,1]=='/'
@document << EndTag.new(name[1..-1])
else
@document << StartTag.new(name,attr)
end
end
when :AFTEREQUAL #=
attr[attrname.downcase]=true
name.downcase!
if EMPTY.include?(name)
@document << EmptyElementTag.new(name,attr)
else
if name[0,1]=='/'
@document << EndTag.new(name[1..-1])
else
@document << StartTag.new(name,attr)
end
end
when :VALUE #値
attr[attrname.downcase]=CGI::unescapeHTML(text)
name.downcase!
if EMPTY.include?(name)
@document << EmptyElementTag.new(name,attr)
else
if name[0,1]=='/'
@document << EndTag.new(name[1..-1])
else
@document << StartTag.new(name,attr)
end
end
when :SQVALUE #'値'
attr[attrname.downcase]=CGI::unescapeHTML(text)
when :DQVALUE #"値"
attr[attrname.downcase]=CGI::unescapeHTML(text)
when :COMMENT
if text=~/^#[a-zA-Z]+/ #SSI
@document << SSI.new(text)
else
@document << Comment.new(text)
end
when :ERUBY
@document << ERuby.new(text)
when :PHP
@document << PHP.new(text)
when :DECLARE
@document << Declaration.new(text)
end
end
#
attr_accessor :document
#
def to_s
s = ''
@document.each {|e|
s<<(e.to_s)
}
s
end
#
def each
tag = []
i = 0
@document.each {|e|
case e
when StartTag
tag.push [e,i]
when EndTag
idx = nil
(tag.size-1).downto(0) {|j|
if tag[j][0].name==e.name
idx = j
break
end
}
#
if idx
if idx==0
tag = []
else
tag = tag[0..idx-1]
end
end
else
end
yield e,tag
i += 1
}
end
#
def index(_class,_start=0,_end=-1,value=nil,count=1)
idx=_start
found=false
@document[_start.._end].each {|obj|
if obj.type==_class
if value
case obj
when StartTag,EmptyElementTag,EndTag
if value===obj.name
if (not iterator?) or yield(obj)
if (count-=1)<=0
found = true
break
end
end
end
else
if value===obj.text
if (not iterator?) or yield(obj)
if (count-=1)<=0
found = true
break
end
end
end
end
else
if (not iterator?) or yield(obj)
if (count-=1)<=0
found = true
break
end
end
end
end
idx+=1
}
if found
idx
else
nil
end
end
#
def end_index(start_index)
tag = []
end_index = nil
(start_index...@document.size).each {|idx|
e= @document[idx]
case e
when StartTag
tag.push [e,idx]
when EndTag
i = nil
(tag.size-1).downto(0) {|j|
if tag[j][0].name==e.name
i = j
break
end
}
#
if i
if i==0
tag = []
else
tag = tag[0..i-1]
end
end
if tag.size==0
end_index = idx
break
end
else
end
}
end_index
end
end
=begin End of Document
=end