diff --git a/app/controllers/file_controller.rb b/app/controllers/file_controller.rb index 499c0a2d..b6621c37 100644 --- a/app/controllers/file_controller.rb +++ b/app/controllers/file_controller.rb @@ -1,7 +1,7 @@ # Controller responsible for serving files and pictures. require 'zip/zip' -require 'string_utils' +require 'sanitize' class FileController < ApplicationController diff --git a/app/controllers/wiki_controller.rb b/app/controllers/wiki_controller.rb index ec646a82..37e0c822 100644 --- a/app/controllers/wiki_controller.rb +++ b/app/controllers/wiki_controller.rb @@ -1,9 +1,9 @@ require 'fileutils' -require 'redcloth_for_tex' +#require 'redcloth_for_tex' +require 'maruku' require 'parsedate' require 'zip/zip' require 'sanitize' -require 'string_utils' class WikiController < ApplicationController @@ -11,7 +11,7 @@ class WikiController < ApplicationController caches_action :show, :published, :authors, :tex, :s5, :print, :recently_revised, :list, :atom_with_content, :atom_with_headlines cache_sweeper :revision_sweeper - layout 'default', :except => [:atom_with_content, :atom_with_headlines, :atom, :tex, :pdf, :s5, :export_tex, :export_html] + layout 'default', :except => [:atom_with_content, :atom_with_headlines, :atom, :tex, :s5, :export_html] include Sanitize @@ -95,21 +95,21 @@ class WikiController < ApplicationController export_pages_as_zip(@web.markup) { |page| page.content } end - def export_pdf - file_name = "#{@web.address}-tex-#{@web.revised_at.strftime('%Y-%m-%d-%H-%M-%S')}" - file_path = File.join(@wiki.storage_path, file_name) +# def export_pdf +# file_name = "#{@web.address}-tex-#{@web.revised_at.strftime('%Y-%m-%d-%H-%M-%S')}" +# file_path = File.join(@wiki.storage_path, file_name) +# +# export_web_to_tex "#{file_path}.tex" unless FileTest.exists? "#{file_path}.tex" +# convert_tex_to_pdf "#{file_path}.tex" +# send_file "#{file_path}.pdf" +# end - export_web_to_tex "#{file_path}.tex" unless FileTest.exists? "#{file_path}.tex" - convert_tex_to_pdf "#{file_path}.tex" - send_file "#{file_path}.pdf" - end - - def export_tex - file_name = "#{@web.address}-tex-#{@web.revised_at.strftime('%Y-%m-%d-%H-%M-%S')}.tex" - file_path = File.join(@wiki.storage_path, file_name) - export_web_to_tex(file_path) unless FileTest.exists?(file_path) - send_file file_path - end +# def export_tex +# file_name = "#{@web.address}-tex-#{@web.revised_at.strftime('%Y-%m-%d-%H-%M-%S')}.tex" +# file_path = File.join(@wiki.storage_path, file_name) +# export_web_to_tex(file_path) unless FileTest.exists?(file_path) +# send_file file_path +# end def feeds @rss_with_content_allowed = rss_with_content_allowed? @@ -180,17 +180,17 @@ class WikiController < ApplicationController # to template end - def pdf - page = wiki.read_page(@web_name, @page_name) - safe_page_name = @page.name.gsub(/\W/, '') - file_name = "#{safe_page_name}-#{@web.address}-#{@page.revised_at.strftime('%Y-%m-%d-%H-%M-%S')}" - file_path = File.join(@wiki.storage_path, file_name) - - export_page_to_tex("#{file_path}.tex") unless FileTest.exists?("#{file_path}.tex") - # NB: this is _very_ slow - convert_tex_to_pdf("#{file_path}.tex") - send_file "#{file_path}.pdf" - end +# def pdf +# page = wiki.read_page(@web_name, @page_name) +# safe_page_name = @page.name.gsub(/\W/, '') +# file_name = "#{safe_page_name}-#{@web.address}-#{@page.revised_at.strftime('%Y-%m-%d-%H-%M-%S')}" +# file_path = File.join(@wiki.storage_path, file_name) +# +# export_page_to_tex("#{file_path}.tex") unless FileTest.exists?("#{file_path}.tex") +# # NB: this is _very_ slow +# convert_tex_to_pdf("#{file_path}.tex") +# send_file "#{file_path}.pdf" +# end def print if @page.nil? @@ -285,10 +285,10 @@ class WikiController < ApplicationController end def tex - if @web.markup == :markdownMML + if @web.markup == :markdownMML or @web.markup == :markdown @tex_content = Maruku.new(@page.content).to_latex else - @tex_content = RedClothForTex.new(@page.content).to_tex + @tex_content = 'TeX export only supported with the Markdown text filters.' end end @@ -315,23 +315,23 @@ class WikiController < ApplicationController private - def convert_tex_to_pdf(tex_path) - # TODO remove earlier PDF files with the same prefix - # TODO handle gracefully situation where pdflatex is not available - begin - wd = Dir.getwd - Dir.chdir(File.dirname(tex_path)) - logger.info `pdflatex --interaction=nonstopmode #{File.basename(tex_path)}` - ensure - Dir.chdir(wd) - end - end +# def convert_tex_to_pdf(tex_path) +# # TODO remove earlier PDF files with the same prefix +# # TODO handle gracefully situation where pdflatex is not available +# begin +# wd = Dir.getwd +# Dir.chdir(File.dirname(tex_path)) +# logger.info `pdflatex --interaction=nonstopmode #{File.basename(tex_path)}` +# ensure +# Dir.chdir(wd) +# end +# end def export_page_to_tex(file_path) if @web.markup == :markdownMML @tex_content = Maruku.new(@page.content).to_latex else - @tex_content = RedClothForTex.new(@page.content).to_tex + @tex_content = 'TeX export only supported with the Markdown text filters.' end File.open(file_path, 'w') { |f| f.write(render_to_string(:template => 'wiki/tex', :layout => 'tex')) } end @@ -360,15 +360,15 @@ class WikiController < ApplicationController send_file file_path end - def export_web_to_tex(file_path) +# def export_web_to_tex(file_path) # if @web.markup == :markdownMML # @tex_content = Maruku.new(@page.content).to_latex # else -# @tex_content = RedClothForTex.new(@page.content).to_tex +# @tex_content = 'TeX export only supported with the Markdown text filters.' # end - @tex_content = table_of_contents(@web.page('HomePage').content, render_tex_web) - File.open(file_path, 'w') { |f| f.write(render_to_string(:template => 'wiki/tex_web', :layout => tex)) } - end +# @tex_content = table_of_contents(@web.page('HomePage').content, render_tex_web) +# File.open(file_path, 'w') { |f| f.write(render_to_string(:template => 'wiki/tex_web', :layout => tex)) } +# end def get_page_and_revision if params['rev'] @@ -411,7 +411,7 @@ class WikiController < ApplicationController if @web.markup == :markdownMML tex_web[page.name] = Maruku.new(page.content).to_latex else - tex_web[page.name] = RedClothForTex.new(page.content).to_tex + tex_web[page.name] = 'TeX export only supported with the Markdown text filters.' end tex_web end diff --git a/app/views/wiki/edit.rhtml b/app/views/wiki/edit.rhtml index f0d8f0c5..709b22ad 100644 --- a/app/views/wiki/edit.rhtml +++ b/app/views/wiki/edit.rhtml @@ -13,7 +13,7 @@ { 'id' => 'editForm', 'method' => 'post', 'onsubmit' => 'cleanAuthorName()', 'accept-charset' => 'utf-8' }) do %>
\\1
" ) - - #line.gsub!( "#{ code }
#{ after }"
- end
- end
-
- def shelve( val )
- @shelf << val
- " <#{ @shelf.length }>"
- end
-
- def retrieve( text )
- @shelf.each_with_index do |r, i|
- text.gsub!( " <#{ i + 1 }>", r )
- end
- end
-
- def incoming_entities( text )
- ## turn any incoming ampersands into a dummy character for now.
- ## This uses a negative lookahead for alphanumerics followed by a semicolon,
- ## implying an incoming html entity, to be skipped
-
- text.gsub!( /&(?![#a-z0-9]+;)/i, "x%x%" )
- end
-
- def encode_entities( text )
- ## Convert high and low ascii to entities.
- # if $-K == "UTF-8"
- # encode_high( text )
- # else
- text.texesc!( :NoQuotes )
- # end
- end
-
- def fix_entities( text )
- ## de-entify any remaining angle brackets or ampersands
- text.gsub!( "\&", "&" )
- text.gsub!( "\%", "%" )
- end
-
- def clean_white_space( text )
- text.gsub!( /\r\n/, "\n" )
- text.gsub!( /\t/, '' )
- text.gsub!( /\n{3,}/, "\n\n" )
- text.gsub!( /\n *\n/, "\n\n" )
- text.gsub!( /"$/, "\" " )
- end
-
- def no_textile( text )
- text.gsub!( /(^|\s)==(.*?)==(\s|$)?/,
- '\1, etc.
- if tagline
- if line =~ /<(#{ offtags })>/i
- codepre += 1
- used_offtags[$1] = true
- line.texesc!( :NoQuotes ) if codepre - used_offtags.length > 0
- elsif line =~ /<\/(#{ offtags })>/i
- line.texesc!( :NoQuotes ) if codepre - used_offtags.length > 0
- codepre -= 1 unless codepre.zero?
- used_offtags = {} if codepre.zero?
- elsif @filter_html or codepre > 0
- line.texesc!( :NoQuotes )
- ## line.gsub!( /<(\/?#{ offtags })>/, '<\1>' )
- end
- ## do htmlspecial if between
- elsif codepre > 0
- line.texesc!( :NoQuotes )
- ## line.gsub!( /<(\/?#{ offtags })>/, '<\1>' )
- elsif not tagline
- inline line
- glyphs_deep line
- end
-
- line
- end
- end
- end
-
- def glyphs( text )
- text.gsub!( /"\z/, "\" " )
- ## if no html, do a simple search and replace...
- if text !~ /<.*>/
- inline text
- end
- glyphs_deep text
- end
-
- def i_align( text )
- I_ALGN_VALS[text]
- end
-
- def h_align( text )
- H_ALGN_VALS[text]
- end
-
- def v_align( text )
- V_ALGN_VALS[text]
- end
-
- def encode_high( text )
- ## mb_encode_numericentity($text, $cmap, $charset);
- end
-
- def decode_high( text )
- ## mb_decode_numericentity($text, $cmap, $charset);
- end
-
- def textile_popup_help( name, helpvar, windowW, windowH )
- ' ' + name + '
'
- end
-
- CMAP = [
- 160, 255, 0, 0xffff,
- 402, 402, 0, 0xffff,
- 913, 929, 0, 0xffff,
- 931, 937, 0, 0xffff,
- 945, 969, 0, 0xffff,
- 977, 978, 0, 0xffff,
- 982, 982, 0, 0xffff,
- 8226, 8226, 0, 0xffff,
- 8230, 8230, 0, 0xffff,
- 8242, 8243, 0, 0xffff,
- 8254, 8254, 0, 0xffff,
- 8260, 8260, 0, 0xffff,
- 8465, 8465, 0, 0xffff,
- 8472, 8472, 0, 0xffff,
- 8476, 8476, 0, 0xffff,
- 8482, 8482, 0, 0xffff,
- 8501, 8501, 0, 0xffff,
- 8592, 8596, 0, 0xffff,
- 8629, 8629, 0, 0xffff,
- 8656, 8660, 0, 0xffff,
- 8704, 8704, 0, 0xffff,
- 8706, 8707, 0, 0xffff,
- 8709, 8709, 0, 0xffff,
- 8711, 8713, 0, 0xffff,
- 8715, 8715, 0, 0xffff,
- 8719, 8719, 0, 0xffff,
- 8721, 8722, 0, 0xffff,
- 8727, 8727, 0, 0xffff,
- 8730, 8730, 0, 0xffff,
- 8733, 8734, 0, 0xffff,
- 8736, 8736, 0, 0xffff,
- 8743, 8747, 0, 0xffff,
- 8756, 8756, 0, 0xffff,
- 8764, 8764, 0, 0xffff,
- 8773, 8773, 0, 0xffff,
- 8776, 8776, 0, 0xffff,
- 8800, 8801, 0, 0xffff,
- 8804, 8805, 0, 0xffff,
- 8834, 8836, 0, 0xffff,
- 8838, 8839, 0, 0xffff,
- 8853, 8853, 0, 0xffff,
- 8855, 8855, 0, 0xffff,
- 8869, 8869, 0, 0xffff,
- 8901, 8901, 0, 0xffff,
- 8968, 8971, 0, 0xffff,
- 9001, 9002, 0, 0xffff,
- 9674, 9674, 0, 0xffff,
- 9824, 9824, 0, 0xffff,
- 9827, 9827, 0, 0xffff,
- 9829, 9830, 0, 0xffff,
- 338, 339, 0, 0xffff,
- 352, 353, 0, 0xffff,
- 376, 376, 0, 0xffff,
- 710, 710, 0, 0xffff,
- 732, 732, 0, 0xffff,
- 8194, 8195, 0, 0xffff,
- 8201, 8201, 0, 0xffff,
- 8204, 8207, 0, 0xffff,
- 8211, 8212, 0, 0xffff,
- 8216, 8218, 0, 0xffff,
- 8218, 8218, 0, 0xffff,
- 8220, 8222, 0, 0xffff,
- 8224, 8225, 0, 0xffff,
- 8240, 8240, 0, 0xffff,
- 8249, 8250, 0, 0xffff,
- 8364, 8364, 0, 0xffff
- ]
- end
diff --git a/lib/sanitize.rb b/lib/sanitize.rb
index 69f8e3e7..c36e7583 100644
--- a/lib/sanitize.rb
+++ b/lib/sanitize.rb
@@ -1,26 +1,2313 @@
-module Sanitize
-
-# This module provides sanitization of XHTML+MathML+SVG
-# and of inline style attributes.
+# == Introduction
#
-# Uses the HTML5lib parser, so that the parsing behaviour should
+# This module provides sanitization of XHTML+MathML+SVG
+# and of inline style attributes. Its genesis is {described here}[http://golem.ph.utexas.edu/~distler/blog/archives/001181.html].
+#
+# Uses the {HTML5lib parser}[http://code.google.com/p/html5lib/], so that the parsing behaviour should
# resemble that of browsers.
#
# sanitize_xhtml() is a case-sensitive sanitizer, suitable for XHTML
# sanitize_html() is a case-insensitive sanitizer suitable for HTML
+# sanitize_rexml() sanitizes a REXML tree, returning a string
+#
+# == Files
+#
+# {sanitize.rb}[http://golem.ph.utexas.edu/~distler/code/instiki/svn/lib/sanitize.rb],
+# {HTML5lib}[http://golem.ph.utexas.edu/~distler/code/instiki/svn/vendor/plugins/HTML5lib/]
+#
+# == Author
+#
+# {Jacques Distler}[http://golem.ph.utexas.edu/~distler/]
+#
+# == License
+#
+# Ruby License
+module Sanitize
- require 'html5lib/sanitizer'
require 'html5lib/html5parser'
require 'html5lib/liberalxmlparser'
+ require 'html5lib/treewalkers'
+ require 'html5lib/treebuilders'
+ require 'html5lib/serializer'
+ require 'html5lib/sanitizer'
+
include HTML5lib
- def sanitize_xhtml(html)
- XHTMLParser.parseFragment(html, :tokenizer => HTMLSanitizer).to_s
+# Sanitize a string, parsed using XHTML parsing rules.
+#
+# :call-seq:
+# sanitize_xhtml(string) -> string
+# sanitize_xhtml(string, {:encoding => 'iso-8859-1', :to_tree => true}) -> REXML::Document
+#
+# Unless otherwise specified, the string is assumed to be utf-8 encoded.
+# By default, the output is a string. But, optionally, you can return a REXML tree.
+#
+# The string returned is utf-8 encoded. If you want, you can use iconv to convert it to some other encoding.
+# (REXML trees are always utf-8 encoded.)
+ def sanitize_xhtml(html, options = {})
+ @encoding = 'utf-8'
+ @treebuilder = TreeBuilders::REXML::TreeBuilder
+ @to_tree = false
+ options.each do |name, value|
+ next unless %w(encoding treebuilder to_tree).include? name.to_s
+ if name.to_s == 'treebuilder'
+ @treebuilder = HTML5lib::TreeBuilders.getTreeBuilder(value)
+ else
+ instance_variable_set("@#{name}", value)
+ end
+ end
+ parsed = XHTMLParser.parseFragment(html.to_ncr, {:tokenizer => HTMLSanitizer,
+ :encoding => @encoding, :tree => @treebuilder })
+ return parsed if @to_tree
+ return parsed.to_s
end
- def sanitize_html(html)
- HTMLParser.parseFragment(html, :tokenizer => HTMLSanitizer).to_s
+# Sanitize a string, parsed using HTML parsing rules.
+#
+# :call-seq:
+# sanitize_html( string ) -> string
+# sanitize_html( string, {:encoding => 'iso-8859-1', :to_tree => true} ) -> REXML::Document
+#
+# Unless otherwise specified, the string is assumed to be utf-8 encoded.
+# By default, the output is a string. But, optionally, you can return a REXML tree.
+#
+# The string returned is utf-8 encoded. If you want, you can use iconv to convert it to some other encoding.
+# (REXML trees are always utf-8 encoded.)
+ def sanitize_html(html, options = {})
+ @encoding = 'utf-8'
+ @treebuilder = TreeBuilders::REXML::TreeBuilder
+ @to_tree = false
+ options.each do |name, value|
+ next unless %w(encoding treebuilder to_tree).include? name.to_s
+ if name.to_s == 'treebuilder'
+ @treebuilder = HTML5lib::TreeBuilders.getTreeBuilder(value)
+ else
+ instance_variable_set("@#{name}", value)
+ end
+ end
+ parsed = HTMLParser.parseFragment(html.to_ncr, {:tokenizer => HTMLSanitizer,
+ :encoding => @encoding, :tree => @treebuilder })
+ return parsed if @to_tree
+ return parsed.to_s
end
+# Sanitize a REXML tree. The output is a string.
+#
+# :call-seq:
+# sanitize_rexml(tree) -> string
+#
+ def sanitize_rexml(tree)
+ tokens = TreeWalkers.getTreeWalker('rexml').new(tree.to_ncr)
+ HTMLSerializer.serialize(tokens, {:encoding=>'utf-8',
+ :quote_attr_values => true,
+ :minimize_boolean_attributes => false,
+ :use_trailing_solidus => true,
+ :space_before_trailing_solidus => true,
+ :omit_optional_tags => false,
+ :inject_meta_charset => false,
+ :sanitize => true})
+ end
+end
+
+# Some useful additions to the String class
+
+class String
+
+# Check whether a string is valid utf-8
+#
+# :call-seq:
+# string.is_utf8? -> boolean
+#
+# returns true if the sequence of bytes in string is valid utf-8
+ def is_utf8?
+ self =~ /^(
+ [\x09\x0A\x0D\x20-\x7E] # ASCII
+ | [\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte
+ | \xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs
+ | [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte
+ | \xED[\x80-\x9F][\x80-\xBF] # excluding surrogates
+ | \xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3
+ | [\xF1-\xF3][\x80-\xBF]{3} # planes 4-15
+ | \xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16
+ )*$/x;
+ end
+
+#:stopdoc:
+ MATHML_ENTITIES = {
+ 'Alpha' => 'Α',
+ 'Beta' => 'Β',
+ 'Epsilon' => 'Ε',
+ 'Zeta' => 'Ζ',
+ 'Eta' => 'Η',
+ 'Iota' => 'Ι',
+ 'Kappa' => 'Κ',
+ 'Mu' => 'Μ',
+ 'Nu' => 'Ν',
+ 'Omicron' => 'Ο',
+ 'Rho' => 'Ρ',
+ 'Tau' => 'Τ',
+ 'Chi' => 'Χ',
+ 'epsilon' => 'ε',
+ 'zeta' => 'ζ',
+ 'omicron' => 'ο',
+ 'sigmaf' => 'ς',
+ 'thetasym' => 'ϑ',
+ 'upsih' => 'ϒ',
+ 'oline' => '‾',
+ 'frasl' => '⁄',
+ 'alefsym' => 'ℵ',
+ 'crarr' => '↵',
+ 'empty' => '∅',
+ 'amp' => '&',
+ 'lt' => '<',
+ 'zwnj' => '',
+ 'zwj' => '',
+ 'lrm' => '',
+ 'rlm' => '',
+ 'sbquo' => '‚',
+ 'bdquo' => '„',
+ 'lsaquo' => '‹',
+ 'rsaquo' => '›',
+ 'euro' => '€',
+ 'angzarr' => '⍼',
+ 'cirmid' => '⫯',
+ 'cudarrl' => '⤸',
+ 'cudarrr' => '⤵',
+ 'cularr' => '↶',
+ 'cularrp' => '⤽',
+ 'curarr' => '↷',
+ 'curarrm' => '⤼',
+ 'Darr' => '↡',
+ 'dArr' => '⇓',
+ 'ddarr' => '⇊',
+ 'DDotrahd' => '⤑',
+ 'dfisht' => '⥿',
+ 'dHar' => '⥥',
+ 'dharl' => '⇃',
+ 'dharr' => '⇂',
+ 'duarr' => '⇵',
+ 'duhar' => '⥯',
+ 'dzigrarr' => '⟿',
+ 'erarr' => '⥱',
+ 'hArr' => '⇔',
+ 'harr' => '↔',
+ 'harrcir' => '⥈',
+ 'harrw' => '↭',
+ 'hoarr' => '⇿',
+ 'imof' => '⊷',
+ 'lAarr' => '⇚',
+ 'Larr' => '↞',
+ 'larrbfs' => '⤟',
+ 'larrfs' => '⤝',
+ 'larrhk' => '↩',
+ 'larrlp' => '↫',
+ 'larrpl' => '⤹',
+ 'larrsim' => '⥳',
+ 'larrtl' => '↢',
+ 'lAtail' => '⤛',
+ 'latail' => '⤙',
+ 'lBarr' => '⤎',
+ 'lbarr' => '⤌',
+ 'ldca' => '⤶',
+ 'ldrdhar' => '⥧',
+ 'ldrushar' => '⥋',
+ 'ldsh' => '↲',
+ 'lfisht' => '⥼',
+ 'lHar' => '⥢',
+ 'lhard' => '↽',
+ 'lharu' => '↼',
+ 'lharul' => '⥪',
+ 'llarr' => '⇇',
+ 'llhard' => '⥫',
+ 'loarr' => '⇽',
+ 'lrarr' => '⇆',
+ 'lrhar' => '⇋',
+ 'lrhard' => '⥭',
+ 'lsh' => '↰',
+ 'lurdshar' => '⥊',
+ 'luruhar' => '⥦',
+ 'Map' => '⤅',
+ 'map' => '↦',
+ 'midcir' => '⫰',
+ 'mumap' => '⊸',
+ 'nearhk' => '⤤',
+ 'neArr' => '⇗',
+ 'nearr' => '↗',
+ 'nesear' => '⤨',
+ 'nhArr' => '⇎',
+ 'nharr' => '↮',
+ 'nlArr' => '⇍',
+ 'nlarr' => '↚',
+ 'nrArr' => '⇏',
+ 'nrarr' => '↛',
+ 'nrarrc' => '⤳̸',
+ 'nrarrw' => '↝̸',
+ 'nvHarr' => '⤄',
+ 'nvlArr' => '⤂',
+ 'nvrArr' => '⤃',
+ 'nwarhk' => '⤣',
+ 'nwArr' => '⇖',
+ 'nwarr' => '↖',
+ 'nwnear' => '⤧',
+ 'olarr' => '↺',
+ 'orarr' => '↻',
+ 'origof' => '⊶',
+ 'rAarr' => '⇛',
+ 'Rarr' => '↠',
+ 'rarrap' => '⥵',
+ 'rarrbfs' => '⤠',
+ 'rarrc' => '⤳',
+ 'rarrfs' => '⤞',
+ 'rarrhk' => '↪',
+ 'rarrlp' => '↬',
+ 'rarrpl' => '⥅',
+ 'rarrsim' => '⥴',
+ 'Rarrtl' => '⤖',
+ 'rarrtl' => '↣',
+ 'rarrw' => '↝',
+ 'rAtail' => '⤜',
+ 'ratail' => '⤚',
+ 'RBarr' => '⤐',
+ 'rBarr' => '⤏',
+ 'rbarr' => '⤍',
+ 'rdca' => '⤷',
+ 'rdldhar' => '⥩',
+ 'rdsh' => '↳',
+ 'rfisht' => '⥽',
+ 'rHar' => '⥤',
+ 'rhard' => '⇁',
+ 'rharu' => '⇀',
+ 'rharul' => '⥬',
+ 'rlarr' => '⇄',
+ 'rlhar' => '⇌',
+ 'roarr' => '⇾',
+ 'rrarr' => '⇉',
+ 'rsh' => '↱',
+ 'ruluhar' => '⥨',
+ 'searhk' => '⤥',
+ 'seArr' => '⇘',
+ 'searr' => '↘',
+ 'seswar' => '⤩',
+ 'simrarr' => '⥲',
+ 'slarr' => '←',
+ 'srarr' => '→',
+ 'swarhk' => '⤦',
+ 'swArr' => '⇙',
+ 'swarr' => '↙',
+ 'swnwar' => '⤪',
+ 'Uarr' => '↟',
+ 'uArr' => '⇑',
+ 'Uarrocir' => '⥉',
+ 'udarr' => '⇅',
+ 'udhar' => '⥮',
+ 'ufisht' => '⥾',
+ 'uHar' => '⥣',
+ 'uharl' => '↿',
+ 'uharr' => '↾',
+ 'uuarr' => '⇈',
+ 'vArr' => '⇕',
+ 'varr' => '↕',
+ 'xhArr' => '⟺',
+ 'xharr' => '⟷',
+ 'xlArr' => '⟸',
+ 'xlarr' => '⟵',
+ 'xmap' => '⟼',
+ 'xrArr' => '⟹',
+ 'xrarr' => '⟶',
+ 'zigrarr' => '⇝',
+ 'ac' => '∾',
+ 'acE' => '∾̳',
+ 'amalg' => '⨿',
+ 'barvee' => '⊽',
+ 'Barwed' => '⌆',
+ 'barwed' => '⌅',
+ 'bsolb' => '⧅',
+ 'Cap' => '⋒',
+ 'capand' => '⩄',
+ 'capbrcup' => '⩉',
+ 'capcap' => '⩋',
+ 'capcup' => '⩇',
+ 'capdot' => '⩀',
+ 'caps' => '∩︀',
+ 'ccaps' => '⩍',
+ 'ccups' => '⩌',
+ 'ccupssm' => '⩐',
+ 'coprod' => '∐',
+ 'Cup' => '⋓',
+ 'cupbrcap' => '⩈',
+ 'cupcap' => '⩆',
+ 'cupcup' => '⩊',
+ 'cupdot' => '⊍',
+ 'cupor' => '⩅',
+ 'cups' => '∪︀',
+ 'cuvee' => '⋎',
+ 'cuwed' => '⋏',
+ 'Dagger' => '‡',
+ 'dagger' => '†',
+ 'diam' => '⋄',
+ 'divonx' => '⋇',
+ 'eplus' => '⩱',
+ 'hercon' => '⊹',
+ 'intcal' => '⊺',
+ 'iprod' => '⨼',
+ 'loplus' => '⨭',
+ 'lotimes' => '⨴',
+ 'lthree' => '⋋',
+ 'ltimes' => '⋉',
+ 'midast' => '*',
+ 'minusb' => '⊟',
+ 'minusd' => '∸',
+ 'minusdu' => '⨪',
+ 'ncap' => '⩃',
+ 'ncup' => '⩂',
+ 'oast' => '⊛',
+ 'ocir' => '⊚',
+ 'odash' => '⊝',
+ 'odiv' => '⨸',
+ 'odot' => '⊙',
+ 'odsold' => '⦼',
+ 'ofcir' => '⦿',
+ 'ogt' => '⧁',
+ 'ohbar' => '⦵',
+ 'olcir' => '⦾',
+ 'olt' => '⧀',
+ 'omid' => '⦶',
+ 'ominus' => '⊖',
+ 'opar' => '⦷',
+ 'operp' => '⦹',
+ 'oplus' => '⊕',
+ 'osol' => '⊘',
+ 'Otimes' => '⨷',
+ 'otimes' => '⊗',
+ 'otimesas' => '⨶',
+ 'ovbar' => '⌽',
+ 'plusacir' => '⨣',
+ 'plusb' => '⊞',
+ 'pluscir' => '⨢',
+ 'plusdo' => '∔',
+ 'plusdu' => '⨥',
+ 'pluse' => '⩲',
+ 'plussim' => '⨦',
+ 'plustwo' => '⨧',
+ 'prod' => '∏',
+ 'race' => '⧚',
+ 'roplus' => '⨮',
+ 'rotimes' => '⨵',
+ 'rthree' => '⋌',
+ 'rtimes' => '⋊',
+ 'sdot' => '⋅',
+ 'sdotb' => '⊡',
+ 'setmn' => '∖',
+ 'simplus' => '⨤',
+ 'smashp' => '⨳',
+ 'solb' => '⧄',
+ 'sqcap' => '⊓',
+ 'sqcaps' => '⊓︀',
+ 'sqcup' => '⊔',
+ 'sqcups' => '⊔︀',
+ 'ssetmn' => '∖',
+ 'sstarf' => '⋆',
+ 'subdot' => '⪽',
+ 'sum' => '∑',
+ 'supdot' => '⪾',
+ 'timesb' => '⊠',
+ 'timesbar' => '⨱',
+ 'timesd' => '⨰',
+ 'tridot' => '◬',
+ 'triminus' => '⨺',
+ 'triplus' => '⨹',
+ 'trisb' => '⧍',
+ 'tritime' => '⨻',
+ 'uplus' => '⊎',
+ 'veebar' => '⊻',
+ 'wedbar' => '⩟',
+ 'wreath' => '≀',
+ 'xcap' => '⋂',
+ 'xcirc' => '◯',
+ 'xcup' => '⋃',
+ 'xdtri' => '▽',
+ 'xodot' => '⨀',
+ 'xoplus' => '⨁',
+ 'xotime' => '⨂',
+ 'xsqcup' => '⨆',
+ 'xuplus' => '⨄',
+ 'xutri' => '△',
+ 'xvee' => '⋁',
+ 'xwedge' => '⋀',
+ 'dlcorn' => '⌞',
+ 'drcorn' => '⌟',
+ 'gtlPar' => '⦕',
+ 'langd' => '⦑',
+ 'lbrke' => '⦋',
+ 'lbrksld' => '⦏',
+ 'lbrkslu' => '⦍',
+ 'lceil' => '⌈',
+ 'lfloor' => '⌊',
+ 'lmoust' => '⎰',
+ 'lparlt' => '⦓',
+ 'ltrPar' => '⦖',
+ 'rangd' => '⦒',
+ 'rbrke' => '⦌',
+ 'rbrksld' => '⦎',
+ 'rbrkslu' => '⦐',
+ 'rceil' => '⌉',
+ 'rfloor' => '⌋',
+ 'rmoust' => '⎱',
+ 'rpargt' => '⦔',
+ 'ulcorn' => '⌜',
+ 'urcorn' => '⌝',
+ 'gnap' => '⪊',
+ 'gnE' => '≩',
+ 'gne' => '⪈',
+ 'gnsim' => '⋧',
+ 'gvnE' => '≩︀',
+ 'lnap' => '⪉',
+ 'lnE' => '≨',
+ 'lne' => '⪇',
+ 'lnsim' => '⋦',
+ 'lvnE' => '≨︀',
+ 'nap' => '≉',
+ 'napE' => '⩰̸',
+ 'napid' => '≋̸',
+ 'ncong' => '≇',
+ 'ncongdot' => '⩭̸',
+ 'nequiv' => '≢',
+ 'ngE' => '≧̸',
+ 'nge' => '≱',
+ 'nges' => '⩾̸',
+ 'nGg' => '⋙̸',
+ 'ngsim' => '≵',
+ 'nGt' => '≫⃒',
+ 'ngt' => '≯',
+ 'nGtv' => '≫̸',
+ 'nlE' => '≦̸',
+ 'nle' => '≰',
+ 'nles' => '⩽̸',
+ 'nLl' => '⋘̸',
+ 'nlsim' => '≴',
+ 'nLt' => '≪⃒',
+ 'nlt' => '≮',
+ 'nltri' => '⋪',
+ 'nltrie' => '⋬',
+ 'nLtv' => '≪̸',
+ 'nmid' => '∤',
+ 'npar' => '∦',
+ 'npr' => '⊀',
+ 'nprcue' => '⋠',
+ 'npre' => '⪯̸',
+ 'nrtri' => '⋫',
+ 'nrtrie' => '⋭',
+ 'nsc' => '⊁',
+ 'nsccue' => '⋡',
+ 'nsce' => '⪰̸',
+ 'nsim' => '≁',
+ 'nsime' => '≄',
+ 'nsmid' => '∤',
+ 'nspar' => '∦',
+ 'nsqsube' => '⋢',
+ 'nsqsupe' => '⋣',
+ 'nsub' => '⊄',
+ 'nsubE' => '⫅̸',
+ 'nsube' => '⊈',
+ 'nsup' => '⊅',
+ 'nsupE' => '⫆̸',
+ 'nsupe' => '⊉',
+ 'ntgl' => '≹',
+ 'ntlg' => '≸',
+ 'nvap' => '≍⃒',
+ 'nVDash' => '⊯',
+ 'nVdash' => '⊮',
+ 'nvDash' => '⊭',
+ 'nvdash' => '⊬',
+ 'nvge' => '≥⃒',
+ 'nvgt' => '>⃒',
+ 'nvle' => '≤⃒',
+ 'nvltrie' => '⊴⃒',
+ 'nvrtrie' => '⊵⃒',
+ 'nvsim' => '∼⃒',
+ 'parsim' => '⫳',
+ 'prnap' => '⪹',
+ 'prnE' => '⪵',
+ 'prnsim' => '⋨',
+ 'rnmid' => '⫮',
+ 'scnap' => '⪺',
+ 'scnE' => '⪶',
+ 'scnsim' => '⋩',
+ 'simne' => '≆',
+ 'solbar' => '⌿',
+ 'subnE' => '⫋',
+ 'subne' => '⊊',
+ 'supnE' => '⫌',
+ 'supne' => '⊋',
+ 'vnsub' => '⊂⃒',
+ 'vnsup' => '⊃⃒',
+ 'vsubnE' => '⫋︀',
+ 'vsubne' => '⊊︀',
+ 'vsupnE' => '⫌︀',
+ 'vsupne' => '⊋︀',
+ 'ang' => '∠',
+ 'ange' => '⦤',
+ 'angmsd' => '∡',
+ 'angmsdaa' => '⦨',
+ 'angmsdab' => '⦩',
+ 'angmsdac' => '⦪',
+ 'angmsdad' => '⦫',
+ 'angmsdae' => '⦬',
+ 'angmsdaf' => '⦭',
+ 'angmsdag' => '⦮',
+ 'angmsdah' => '⦯',
+ 'angrtvb' => '⊾',
+ 'angrtvbd' => '⦝',
+ 'bbrk' => '⎵',
+ 'bbrktbrk' => '⎶',
+ 'bemptyv' => '⦰',
+ 'beth' => 'ℶ',
+ 'boxbox' => '⧉',
+ 'bprime' => '‵',
+ 'bsemi' => '⁏',
+ 'cemptyv' => '⦲',
+ 'cirE' => '⧃',
+ 'cirscir' => '⧂',
+ 'comp' => '∁',
+ 'daleth' => 'ℸ',
+ 'demptyv' => '⦱',
+ 'ell' => 'ℓ',
+ 'empty' => '∅',
+ 'emptyv' => '∅',
+ 'gimel' => 'ℷ',
+ 'iiota' => '℩',
+ 'image' => 'ℑ',
+ 'imath' => 'ı',
+ 'jmath' => 'j',
+ 'laemptyv' => '⦴',
+ 'lltri' => '◺',
+ 'lrtri' => '⊿',
+ 'mho' => '℧',
+ 'nang' => '∠⃒',
+ 'nexist' => '∄',
+ 'oS' => 'Ⓢ',
+ 'planck' => 'ℏ',
+ 'plankv' => 'ℏ',
+ 'raemptyv' => '⦳',
+ 'range' => '⦥',
+ 'real' => 'ℜ',
+ 'tbrk' => '⎴',
+ 'trpezium' => '�',
+ 'ultri' => '◸',
+ 'urtri' => '◹',
+ 'vzigzag' => '⦚',
+ 'weierp' => '℘',
+ 'apE' => '⩰',
+ 'ape' => '≊',
+ 'apid' => '≋',
+ 'asymp' => '≈',
+ 'Barv' => '⫧',
+ 'bcong' => '≌',
+ 'bepsi' => '϶',
+ 'bowtie' => '⋈',
+ 'bsim' => '∽',
+ 'bsime' => '⋍',
+ 'bsolhsub' => '\⊂',
+ 'bump' => '≎',
+ 'bumpE' => '⪮',
+ 'bumpe' => '≏',
+ 'cire' => '≗',
+ 'Colon' => '∷',
+ 'Colone' => '⩴',
+ 'colone' => '≔',
+ 'congdot' => '⩭',
+ 'csub' => '⫏',
+ 'csube' => '⫑',
+ 'csup' => '⫐',
+ 'csupe' => '⫒',
+ 'cuepr' => '⋞',
+ 'cuesc' => '⋟',
+ 'Dashv' => '⫤',
+ 'dashv' => '⊣',
+ 'easter' => '⩮',
+ 'ecir' => '≖',
+ 'ecolon' => '≕',
+ 'eDDot' => '⩷',
+ 'eDot' => '≑',
+ 'efDot' => '≒',
+ 'eg' => '⪚',
+ 'egs' => '⪖',
+ 'egsdot' => '⪘',
+ 'el' => '⪙',
+ 'els' => '⪕',
+ 'elsdot' => '⪗',
+ 'equest' => '≟',
+ 'equivDD' => '⩸',
+ 'erDot' => '≓',
+ 'esdot' => '≐',
+ 'Esim' => '⩳',
+ 'esim' => '≂',
+ 'fork' => '⋔',
+ 'forkv' => '⫙',
+ 'frown' => '⌢',
+ 'gap' => '⪆',
+ 'gE' => '≧',
+ 'gEl' => '⪌',
+ 'gel' => '⋛',
+ 'ges' => '⩾',
+ 'gescc' => '⪩',
+ 'gesdot' => '⪀',
+ 'gesdoto' => '⪂',
+ 'gesdotol' => '⪄',
+ 'gesl' => '⋛︀',
+ 'gesles' => '⪔',
+ 'Gg' => '⋙',
+ 'gl' => '≷',
+ 'gla' => '⪥',
+ 'glE' => '⪒',
+ 'glj' => '⪤',
+ 'gsim' => '≳',
+ 'gsime' => '⪎',
+ 'gsiml' => '⪐',
+ 'Gt' => '≫',
+ 'gtcc' => '⪧',
+ 'gtcir' => '⩺',
+ 'gtdot' => '⋗',
+ 'gtquest' => '⩼',
+ 'gtrarr' => '⥸',
+ 'homtht' => '∻',
+ 'lap' => '⪅',
+ 'lat' => '⪫',
+ 'late' => '⪭',
+ 'lates' => '⪭︀',
+ 'lE' => '≦',
+ 'lEg' => '⪋',
+ 'leg' => '⋚',
+ 'les' => '⩽',
+ 'lescc' => '⪨',
+ 'lesdot' => '⩿',
+ 'lesdoto' => '⪁',
+ 'lesdotor' => '⪃',
+ 'lesg' => '⋚︀',
+ 'lesges' => '⪓',
+ 'lg' => '≶',
+ 'lgE' => '⪑',
+ 'Ll' => '⋘',
+ 'lsim' => '≲',
+ 'lsime' => '⪍',
+ 'lsimg' => '⪏',
+ 'Lt' => '≪',
+ 'ltcc' => '⪦',
+ 'ltcir' => '⩹',
+ 'ltdot' => '⋖',
+ 'ltlarr' => '⥶',
+ 'ltquest' => '⩻',
+ 'ltrie' => '⊴',
+ 'mcomma' => '⨩',
+ 'mDDot' => '∺',
+ 'mid' => '∣',
+ 'mlcp' => '⫛',
+ 'models' => '⊧',
+ 'mstpos' => '∾',
+ 'Pr' => '⪻',
+ 'pr' => '≺',
+ 'prap' => '⪷',
+ 'prcue' => '≼',
+ 'prE' => '⪳',
+ 'pre' => '⪯',
+ 'prsim' => '≾',
+ 'prurel' => '⊰',
+ 'ratio' => '∶',
+ 'rtrie' => '⊵',
+ 'rtriltri' => '⧎',
+ 'Sc' => '⪼',
+ 'sc' => '≻',
+ 'scap' => '⪸',
+ 'sccue' => '≽',
+ 'scE' => '⪴',
+ 'sce' => '⪰',
+ 'scsim' => '≿',
+ 'sdote' => '⩦',
+ 'sfrown' => '⌢',
+ 'simg' => '⪞',
+ 'simgE' => '⪠',
+ 'siml' => '⪝',
+ 'simlE' => '⪟',
+ 'smid' => '∣',
+ 'smile' => '⌣',
+ 'smt' => '⪪',
+ 'smte' => '⪬',
+ 'smtes' => '⪬︀',
+ 'spar' => '∥',
+ 'sqsub' => '⊏',
+ 'sqsube' => '⊑',
+ 'sqsup' => '⊐',
+ 'sqsupe' => '⊒',
+ 'ssmile' => '⌣',
+ 'Sub' => '⋐',
+ 'subE' => '⫅',
+ 'subedot' => '⫃',
+ 'submult' => '⫁',
+ 'subplus' => '⪿',
+ 'subrarr' => '⥹',
+ 'subsim' => '⫇',
+ 'subsub' => '⫕',
+ 'subsup' => '⫓',
+ 'Sup' => '⋑',
+ 'supdsub' => '⫘',
+ 'supE' => '⫆',
+ 'supedot' => '⫄',
+ 'suphsol' => '⊃/',
+ 'suphsub' => '⫗',
+ 'suplarr' => '⥻',
+ 'supmult' => '⫂',
+ 'supplus' => '⫀',
+ 'supsim' => '⫈',
+ 'supsub' => '⫔',
+ 'supsup' => '⫖',
+ 'thkap' => '≈',
+ 'thksim' => '∼',
+ 'topfork' => '⫚',
+ 'trie' => '≜',
+ 'twixt' => '≬',
+ 'Vbar' => '⫫',
+ 'vBar' => '⫨',
+ 'vBarv' => '⫩',
+ 'VDash' => '⊫',
+ 'Vdash' => '⊩',
+ 'vDash' => '⊨',
+ 'vdash' => '⊢',
+ 'Vdashl' => '⫦',
+ 'vltri' => '⊲',
+ 'vprop' => '∝',
+ 'vrtri' => '⊳',
+ 'Vvdash' => '⊪',
+ 'alpha' => 'α',
+ 'beta' => 'β',
+ 'chi' => 'χ',
+ 'Delta' => 'Δ',
+ 'delta' => 'δ',
+ 'epsi' => 'ϵ',
+ 'epsiv' => 'ε',
+ 'eta' => 'η',
+ 'Gamma' => 'Γ',
+ 'gamma' => 'γ',
+ 'Gammad' => 'Ϝ',
+ 'gammad' => 'ϝ',
+ 'iota' => 'ι',
+ 'kappa' => 'κ',
+ 'kappav' => 'ϰ',
+ 'Lambda' => 'Λ',
+ 'lambda' => 'λ',
+ 'mu' => 'μ',
+ 'nu' => 'ν',
+ 'Omega' => 'Ω',
+ 'omega' => 'ω',
+ 'Phi' => 'Φ',
+ 'phi' => 'ϕ',
+ 'phiv' => 'φ',
+ 'Pi' => 'Π',
+ 'pi' => 'π',
+ 'piv' => 'ϖ',
+ 'Psi' => 'Ψ',
+ 'psi' => 'ψ',
+ 'rho' => 'ρ',
+ 'rhov' => 'ϱ',
+ 'Sigma' => 'Σ',
+ 'sigma' => 'σ',
+ 'sigmav' => 'ς',
+ 'tau' => 'τ',
+ 'Theta' => 'Θ',
+ 'theta' => 'θ',
+ 'thetav' => 'ϑ',
+ 'Upsi' => 'ϒ',
+ 'upsi' => 'υ',
+ 'Xi' => 'Ξ',
+ 'xi' => 'ξ',
+ 'zeta' => 'ζ',
+ 'Afr' => '𝔄',
+ 'afr' => '𝔞',
+ 'Bfr' => '𝔅',
+ 'bfr' => '𝔟',
+ 'Cfr' => 'ℭ',
+ 'cfr' => '𝔠',
+ 'Dfr' => '𝔇',
+ 'dfr' => '𝔡',
+ 'Efr' => '𝔈',
+ 'efr' => '𝔢',
+ 'Ffr' => '𝔉',
+ 'ffr' => '𝔣',
+ 'Gfr' => '𝔊',
+ 'gfr' => '𝔤',
+ 'Hfr' => 'ℌ',
+ 'hfr' => '𝔥',
+ 'Ifr' => 'ℑ',
+ 'ifr' => '𝔦',
+ 'Jfr' => '𝔍',
+ 'jfr' => '𝔧',
+ 'Kfr' => '𝔎',
+ 'kfr' => '𝔨',
+ 'Lfr' => '𝔏',
+ 'lfr' => '𝔩',
+ 'Mfr' => '𝔐',
+ 'mfr' => '𝔪',
+ 'Nfr' => '𝔑',
+ 'nfr' => '𝔫',
+ 'Ofr' => '𝔒',
+ 'ofr' => '𝔬',
+ 'Pfr' => '𝔓',
+ 'pfr' => '𝔭',
+ 'Qfr' => '𝔔',
+ 'qfr' => '𝔮',
+ 'Rfr' => 'ℜ',
+ 'rfr' => '𝔯',
+ 'Sfr' => '𝔖',
+ 'sfr' => '𝔰',
+ 'Tfr' => '𝔗',
+ 'tfr' => '𝔱',
+ 'Ufr' => '𝔘',
+ 'ufr' => '𝔲',
+ 'Vfr' => '𝔙',
+ 'vfr' => '𝔳',
+ 'Wfr' => '𝔚',
+ 'wfr' => '𝔴',
+ 'Xfr' => '𝔛',
+ 'xfr' => '𝔵',
+ 'Yfr' => '𝔜',
+ 'yfr' => '𝔶',
+ 'Zfr' => 'ℨ',
+ 'zfr' => '𝔷',
+ 'Aopf' => '𝔸',
+ 'Bopf' => '𝔹',
+ 'Copf' => 'ℂ',
+ 'Dopf' => '𝔻',
+ 'Eopf' => '𝔼',
+ 'Fopf' => '𝔽',
+ 'Gopf' => '𝔾',
+ 'Hopf' => 'ℍ',
+ 'Iopf' => '𝕀',
+ 'Jopf' => '𝕁',
+ 'Kopf' => '𝕂',
+ 'Lopf' => '𝕃',
+ 'Mopf' => '𝕄',
+ 'Nopf' => 'ℕ',
+ 'Oopf' => '𝕆',
+ 'Popf' => 'ℙ',
+ 'Qopf' => 'ℚ',
+ 'Ropf' => 'ℝ',
+ 'Sopf' => '𝕊',
+ 'Topf' => '𝕋',
+ 'Uopf' => '𝕌',
+ 'Vopf' => '𝕍',
+ 'Wopf' => '𝕎',
+ 'Xopf' => '𝕏',
+ 'Yopf' => '𝕐',
+ 'Zopf' => 'ℤ',
+ 'Ascr' => '𝒜',
+ 'ascr' => '𝒶',
+ 'Bscr' => 'ℬ',
+ 'bscr' => '𝒷',
+ 'Cscr' => '𝒞',
+ 'cscr' => '𝒸',
+ 'Dscr' => '𝒟',
+ 'dscr' => '𝒹',
+ 'Escr' => 'ℰ',
+ 'escr' => 'ℯ',
+ 'Fscr' => 'ℱ',
+ 'fscr' => '𝒻',
+ 'Gscr' => '𝒢',
+ 'gscr' => 'ℊ',
+ 'Hscr' => 'ℋ',
+ 'hscr' => '𝒽',
+ 'Iscr' => 'ℐ',
+ 'iscr' => '𝒾',
+ 'Jscr' => '𝒥',
+ 'jscr' => '𝒿',
+ 'Kscr' => '𝒦',
+ 'kscr' => '𝓀',
+ 'Lscr' => 'ℒ',
+ 'lscr' => '𝓁',
+ 'Mscr' => 'ℳ',
+ 'mscr' => '𝓂',
+ 'Nscr' => '𝒩',
+ 'nscr' => '𝓃',
+ 'Oscr' => '𝒪',
+ 'oscr' => 'ℴ',
+ 'Pscr' => '𝒫',
+ 'pscr' => '𝓅',
+ 'Qscr' => '𝒬',
+ 'qscr' => '𝓆',
+ 'Rscr' => 'ℛ',
+ 'rscr' => '𝓇',
+ 'Sscr' => '𝒮',
+ 'sscr' => '𝓈',
+ 'Tscr' => '𝒯',
+ 'tscr' => '𝓉',
+ 'Uscr' => '𝒰',
+ 'uscr' => '𝓊',
+ 'Vscr' => '𝒱',
+ 'vscr' => '𝓋',
+ 'Wscr' => '𝒲',
+ 'wscr' => '𝓌',
+ 'Xscr' => '𝒳',
+ 'xscr' => '𝓍',
+ 'Yscr' => '𝒴',
+ 'yscr' => '𝓎',
+ 'Zscr' => '𝒵',
+ 'zscr' => '𝓏',
+ 'acd' => '∿',
+ 'aleph' => 'ℵ',
+ 'And' => '⩓',
+ 'and' => '∧',
+ 'andand' => '⩕',
+ 'andd' => '⩜',
+ 'andslope' => '⩘',
+ 'andv' => '⩚',
+ 'angrt' => '∟',
+ 'angsph' => '∢',
+ 'angst' => 'Å',
+ 'ap' => '≈',
+ 'apacir' => '⩯',
+ 'awconint' => '∳',
+ 'awint' => '⨑',
+ 'becaus' => '∵',
+ 'bernou' => 'ℬ',
+ 'bne' => '=⃥',
+ 'bnequiv' => '≡⃥',
+ 'bNot' => '⫭',
+ 'bnot' => '⌐',
+ 'bottom' => '⊥',
+ 'cap' => '∩',
+ 'Cconint' => '∰',
+ 'cirfnint' => '⨐',
+ 'compfn' => '∘',
+ 'cong' => '≅',
+ 'Conint' => '∯',
+ 'conint' => '∮',
+ 'ctdot' => '⋯',
+ 'cup' => '∪',
+ 'cwconint' => '∲',
+ 'cwint' => '∱',
+ 'cylcty' => '⌭',
+ 'disin' => '⋲',
+ 'Dot' => '¨',
+ 'DotDot' => '⃜',
+ 'dsol' => '⧶',
+ 'dtdot' => '⋱',
+ 'dwangle' => '⦦',
+ 'elinters' => '�',
+ 'epar' => '⋕',
+ 'eparsl' => '⧣',
+ 'equiv' => '≡',
+ 'eqvparsl' => '⧥',
+ 'exist' => '∃',
+ 'fltns' => '▱',
+ 'fnof' => 'ƒ',
+ 'forall' => '∀',
+ 'fpartint' => '⨍',
+ 'ge' => '≥',
+ 'hamilt' => 'ℋ',
+ 'iff' => '⇔',
+ 'iinfin' => '⧜',
+ 'imped' => 'Ƶ',
+ 'infin' => '∞',
+ 'infintie' => '⧝',
+ 'Int' => '∬',
+ 'int' => '∫',
+ 'intlarhk' => '⨗',
+ 'isin' => '∈',
+ 'isindot' => '⋵',
+ 'isinE' => '⋹',
+ 'isins' => '⋴',
+ 'isinsv' => '⋳',
+ 'isinv' => '∈',
+ 'lagran' => 'ℒ',
+ 'Lang' => '《',
+ 'lang' => '〈',
+ 'lArr' => '⇐',
+ 'lbbrk' => '〔',
+ 'le' => '≤',
+ 'loang' => '〘',
+ 'lobrk' => '〚',
+ 'lopar' => '⦅',
+ 'lowast' => '∗',
+ 'minus' => '−',
+ 'mnplus' => '∓',
+ 'nabla' => '∇',
+ 'ne' => '≠',
+ 'nedot' => '≐̸',
+ 'nhpar' => '⫲',
+ 'ni' => '∋',
+ 'nis' => '⋼',
+ 'nisd' => '⋺',
+ 'niv' => '∋',
+ 'Not' => '⫬',
+ 'notin' => '∉',
+ 'notindot' => '⋵̸',
+ 'notinE' => '⋹̸',
+ 'notinva' => '∉',
+ 'notinvb' => '⋷',
+ 'notinvc' => '⋶',
+ 'notni' => '∌',
+ 'notniva' => '∌',
+ 'notnivb' => '⋾',
+ 'notnivc' => '⋽',
+ 'nparsl' => '⫽⃥',
+ 'npart' => '∂̸',
+ 'npolint' => '⨔',
+ 'nvinfin' => '⧞',
+ 'olcross' => '⦻',
+ 'Or' => '⩔',
+ 'or' => '∨',
+ 'ord' => '⩝',
+ 'order' => 'ℴ',
+ 'oror' => '⩖',
+ 'orslope' => '⩗',
+ 'orv' => '⩛',
+ 'par' => '∥',
+ 'parsl' => '⫽',
+ 'part' => '∂',
+ 'permil' => '‰',
+ 'perp' => '⊥',
+ 'pertenk' => '‱',
+ 'phmmat' => 'ℳ',
+ 'pointint' => '⨕',
+ 'Prime' => '″',
+ 'prime' => '′',
+ 'profalar' => '⌮',
+ 'profline' => '⌒',
+ 'profsurf' => '⌓',
+ 'prop' => '∝',
+ 'qint' => '⨌',
+ 'qprime' => '⁗',
+ 'quatint' => '⨖',
+ 'radic' => '√',
+ 'Rang' => '》',
+ 'rang' => '〉',
+ 'rArr' => '⇒',
+ 'rbbrk' => '〕',
+ 'roang' => '〙',
+ 'robrk' => '〛',
+ 'ropar' => '⦆',
+ 'rppolint' => '⨒',
+ 'scpolint' => '⨓',
+ 'sim' => '∼',
+ 'simdot' => '⩪',
+ 'sime' => '≃',
+ 'smeparsl' => '⧤',
+ 'square' => '□',
+ 'squarf' => '▪',
+ 'strns' => '¯',
+ 'sub' => '⊂',
+ 'sube' => '⊆',
+ 'sup' => '⊃',
+ 'supe' => '⊇',
+ 'tdot' => '⃛',
+ 'there4' => '∴',
+ 'tint' => '∭',
+ 'top' => '⊤',
+ 'topbot' => '⌶',
+ 'topcir' => '⫱',
+ 'tprime' => '‴',
+ 'utdot' => '⋰',
+ 'uwangle' => '⦧',
+ 'vangrt' => '⦜',
+ 'veeeq' => '≚',
+ 'Verbar' => '‖',
+ 'wedgeq' => '≙',
+ 'xnis' => '⋻',
+ 'boxDL' => '╗',
+ 'boxDl' => '╖',
+ 'boxdL' => '╕',
+ 'boxdl' => '┐',
+ 'boxDR' => '╔',
+ 'boxDr' => '╓',
+ 'boxdR' => '╒',
+ 'boxdr' => '┌',
+ 'boxH' => '═',
+ 'boxh' => '─',
+ 'boxHD' => '╦',
+ 'boxHd' => '╤',
+ 'boxhD' => '╥',
+ 'boxhd' => '┬',
+ 'boxHU' => '╩',
+ 'boxHu' => '╧',
+ 'boxhU' => '╨',
+ 'boxhu' => '┴',
+ 'boxUL' => '╝',
+ 'boxUl' => '╜',
+ 'boxuL' => '╛',
+ 'boxul' => '┘',
+ 'boxUR' => '╚',
+ 'boxUr' => '╙',
+ 'boxuR' => '╘',
+ 'boxur' => '└',
+ 'boxV' => '║',
+ 'boxv' => '│',
+ 'boxVH' => '╬',
+ 'boxVh' => '╫',
+ 'boxvH' => '╪',
+ 'boxvh' => '┼',
+ 'boxVL' => '╣',
+ 'boxVl' => '╢',
+ 'boxvL' => '╡',
+ 'boxvl' => '┤',
+ 'boxVR' => '╠',
+ 'boxVr' => '╟',
+ 'boxvR' => '╞',
+ 'boxvr' => '├',
+ 'Acy' => 'А',
+ 'acy' => 'а',
+ 'Bcy' => 'Б',
+ 'bcy' => 'б',
+ 'CHcy' => 'Ч',
+ 'chcy' => 'ч',
+ 'Dcy' => 'Д',
+ 'dcy' => 'д',
+ 'Ecy' => 'Э',
+ 'ecy' => 'э',
+ 'Fcy' => 'Ф',
+ 'fcy' => 'ф',
+ 'Gcy' => 'Г',
+ 'gcy' => 'г',
+ 'HARDcy' => 'Ъ',
+ 'hardcy' => 'ъ',
+ 'Icy' => 'И',
+ 'icy' => 'и',
+ 'IEcy' => 'Е',
+ 'iecy' => 'е',
+ 'IOcy' => 'Ё',
+ 'iocy' => 'ё',
+ 'Jcy' => 'Й',
+ 'jcy' => 'й',
+ 'Kcy' => 'К',
+ 'kcy' => 'к',
+ 'KHcy' => 'Х',
+ 'khcy' => 'х',
+ 'Lcy' => 'Л',
+ 'lcy' => 'л',
+ 'Mcy' => 'М',
+ 'mcy' => 'м',
+ 'Ncy' => 'Н',
+ 'ncy' => 'н',
+ 'numero' => '№',
+ 'Ocy' => 'О',
+ 'ocy' => 'о',
+ 'Pcy' => 'П',
+ 'pcy' => 'п',
+ 'Rcy' => 'Р',
+ 'rcy' => 'р',
+ 'Scy' => 'С',
+ 'scy' => 'с',
+ 'SHCHcy' => 'Щ',
+ 'shchcy' => 'щ',
+ 'SHcy' => 'Ш',
+ 'shcy' => 'ш',
+ 'SOFTcy' => 'Ь',
+ 'softcy' => 'ь',
+ 'Tcy' => 'Т',
+ 'tcy' => 'т',
+ 'TScy' => 'Ц',
+ 'tscy' => 'ц',
+ 'Ucy' => 'У',
+ 'ucy' => 'у',
+ 'Vcy' => 'В',
+ 'vcy' => 'в',
+ 'YAcy' => 'Я',
+ 'yacy' => 'я',
+ 'Ycy' => 'Ы',
+ 'ycy' => 'ы',
+ 'YUcy' => 'Ю',
+ 'yucy' => 'ю',
+ 'Zcy' => 'З',
+ 'zcy' => 'з',
+ 'ZHcy' => 'Ж',
+ 'zhcy' => 'ж',
+ 'DJcy' => 'Ђ',
+ 'djcy' => 'ђ',
+ 'DScy' => 'Ѕ',
+ 'dscy' => 'ѕ',
+ 'DZcy' => 'Џ',
+ 'dzcy' => 'џ',
+ 'GJcy' => 'Ѓ',
+ 'gjcy' => 'ѓ',
+ 'Iukcy' => 'І',
+ 'iukcy' => 'і',
+ 'Jsercy' => 'Ј',
+ 'jsercy' => 'ј',
+ 'Jukcy' => 'Є',
+ 'jukcy' => 'є',
+ 'KJcy' => 'Ќ',
+ 'kjcy' => 'ќ',
+ 'LJcy' => 'Љ',
+ 'ljcy' => 'љ',
+ 'NJcy' => 'Њ',
+ 'njcy' => 'њ',
+ 'TSHcy' => 'Ћ',
+ 'tshcy' => 'ћ',
+ 'Ubrcy' => 'Ў',
+ 'ubrcy' => 'ў',
+ 'YIcy' => 'Ї',
+ 'yicy' => 'ї',
+ 'acute' => '´',
+ 'breve' => '˘',
+ 'caron' => 'ˇ',
+ 'cedil' => '¸',
+ 'circ' => 'ˆ',
+ 'dblac' => '˝',
+ 'die' => '¨',
+ 'dot' => '˙',
+ 'grave' => '`',
+ 'macr' => '¯',
+ 'ogon' => '˛',
+ 'ring' => '˚',
+ 'tilde' => '˜',
+ 'uml' => '¨',
+ 'Aacute' => 'Á',
+ 'aacute' => 'á',
+ 'Acirc' => 'Â',
+ 'acirc' => 'â',
+ 'AElig' => 'Æ',
+ 'aelig' => 'æ',
+ 'Agrave' => 'À',
+ 'agrave' => 'à',
+ 'Aring' => 'Å',
+ 'aring' => 'å',
+ 'Atilde' => 'Ã',
+ 'atilde' => 'ã',
+ 'Auml' => 'Ä',
+ 'auml' => 'ä',
+ 'Ccedil' => 'Ç',
+ 'ccedil' => 'ç',
+ 'Eacute' => 'É',
+ 'eacute' => 'é',
+ 'Ecirc' => 'Ê',
+ 'ecirc' => 'ê',
+ 'Egrave' => 'È',
+ 'egrave' => 'è',
+ 'ETH' => 'Ð',
+ 'eth' => 'ð',
+ 'Euml' => 'Ë',
+ 'euml' => 'ë',
+ 'Iacute' => 'Í',
+ 'iacute' => 'í',
+ 'Icirc' => 'Î',
+ 'icirc' => 'î',
+ 'Igrave' => 'Ì',
+ 'igrave' => 'ì',
+ 'Iuml' => 'Ï',
+ 'iuml' => 'ï',
+ 'Ntilde' => 'Ñ',
+ 'ntilde' => 'ñ',
+ 'Oacute' => 'Ó',
+ 'oacute' => 'ó',
+ 'Ocirc' => 'Ô',
+ 'ocirc' => 'ô',
+ 'Ograve' => 'Ò',
+ 'ograve' => 'ò',
+ 'Oslash' => 'Ø',
+ 'oslash' => 'ø',
+ 'Otilde' => 'Õ',
+ 'otilde' => 'õ',
+ 'Ouml' => 'Ö',
+ 'ouml' => 'ö',
+ 'szlig' => 'ß',
+ 'THORN' => 'Þ',
+ 'thorn' => 'þ',
+ 'Uacute' => 'Ú',
+ 'uacute' => 'ú',
+ 'Ucirc' => 'Û',
+ 'ucirc' => 'û',
+ 'Ugrave' => 'Ù',
+ 'ugrave' => 'ù',
+ 'Uuml' => 'Ü',
+ 'uuml' => 'ü',
+ 'Yacute' => 'Ý',
+ 'yacute' => 'ý',
+ 'yuml' => 'ÿ',
+ 'Abreve' => 'Ă',
+ 'abreve' => 'ă',
+ 'Amacr' => 'Ā',
+ 'amacr' => 'ā',
+ 'Aogon' => 'Ą',
+ 'aogon' => 'ą',
+ 'Cacute' => 'Ć',
+ 'cacute' => 'ć',
+ 'Ccaron' => 'Č',
+ 'ccaron' => 'č',
+ 'Ccirc' => 'Ĉ',
+ 'ccirc' => 'ĉ',
+ 'Cdot' => 'Ċ',
+ 'cdot' => 'ċ',
+ 'Dcaron' => 'Ď',
+ 'dcaron' => 'ď',
+ 'Dstrok' => 'Đ',
+ 'dstrok' => 'đ',
+ 'Ecaron' => 'Ě',
+ 'ecaron' => 'ě',
+ 'Edot' => 'Ė',
+ 'edot' => 'ė',
+ 'Emacr' => 'Ē',
+ 'emacr' => 'ē',
+ 'ENG' => 'Ŋ',
+ 'eng' => 'ŋ',
+ 'Eogon' => 'Ę',
+ 'eogon' => 'ę',
+ 'gacute' => 'ǵ',
+ 'Gbreve' => 'Ğ',
+ 'gbreve' => 'ğ',
+ 'Gcedil' => 'Ģ',
+ 'Gcirc' => 'Ĝ',
+ 'gcirc' => 'ĝ',
+ 'Gdot' => 'Ġ',
+ 'gdot' => 'ġ',
+ 'Hcirc' => 'Ĥ',
+ 'hcirc' => 'ĥ',
+ 'Hstrok' => 'Ħ',
+ 'hstrok' => 'ħ',
+ 'Idot' => 'İ',
+ 'IJlig' => 'IJ',
+ 'ijlig' => 'ij',
+ 'Imacr' => 'Ī',
+ 'imacr' => 'ī',
+ 'inodot' => 'ı',
+ 'Iogon' => 'Į',
+ 'iogon' => 'į',
+ 'Itilde' => 'Ĩ',
+ 'itilde' => 'ĩ',
+ 'Jcirc' => 'Ĵ',
+ 'jcirc' => 'ĵ',
+ 'Kcedil' => 'Ķ',
+ 'kcedil' => 'ķ',
+ 'kgreen' => 'ĸ',
+ 'Lacute' => 'Ĺ',
+ 'lacute' => 'ĺ',
+ 'Lcaron' => 'Ľ',
+ 'lcaron' => 'ľ',
+ 'Lcedil' => 'Ļ',
+ 'lcedil' => 'ļ',
+ 'Lmidot' => 'Ŀ',
+ 'lmidot' => 'ŀ',
+ 'Lstrok' => 'Ł',
+ 'lstrok' => 'ł',
+ 'Nacute' => 'Ń',
+ 'nacute' => 'ń',
+ 'napos' => 'ʼn',
+ 'Ncaron' => 'Ň',
+ 'ncaron' => 'ň',
+ 'Ncedil' => 'Ņ',
+ 'ncedil' => 'ņ',
+ 'Odblac' => 'Ő',
+ 'odblac' => 'ő',
+ 'OElig' => 'Œ',
+ 'oelig' => 'œ',
+ 'Omacr' => 'Ō',
+ 'omacr' => 'ō',
+ 'Racute' => 'Ŕ',
+ 'racute' => 'ŕ',
+ 'Rcaron' => 'Ř',
+ 'rcaron' => 'ř',
+ 'Rcedil' => 'Ŗ',
+ 'rcedil' => 'ŗ',
+ 'Sacute' => 'Ś',
+ 'sacute' => 'ś',
+ 'Scaron' => 'Š',
+ 'scaron' => 'š',
+ 'Scedil' => 'Ş',
+ 'scedil' => 'ş',
+ 'Scirc' => 'Ŝ',
+ 'scirc' => 'ŝ',
+ 'Tcaron' => 'Ť',
+ 'tcaron' => 'ť',
+ 'Tcedil' => 'Ţ',
+ 'tcedil' => 'ţ',
+ 'Tstrok' => 'Ŧ',
+ 'tstrok' => 'ŧ',
+ 'Ubreve' => 'Ŭ',
+ 'ubreve' => 'ŭ',
+ 'Udblac' => 'Ű',
+ 'udblac' => 'ű',
+ 'Umacr' => 'Ū',
+ 'umacr' => 'ū',
+ 'Uogon' => 'Ų',
+ 'uogon' => 'ų',
+ 'Uring' => 'Ů',
+ 'uring' => 'ů',
+ 'Utilde' => 'Ũ',
+ 'utilde' => 'ũ',
+ 'Wcirc' => 'Ŵ',
+ 'wcirc' => 'ŵ',
+ 'Ycirc' => 'Ŷ',
+ 'ycirc' => 'ŷ',
+ 'Yuml' => 'Ÿ',
+ 'Zacute' => 'Ź',
+ 'zacute' => 'ź',
+ 'Zcaron' => 'Ž',
+ 'zcaron' => 'ž',
+ 'Zdot' => 'Ż',
+ 'zdot' => 'ż',
+ 'apos' => ''',
+ 'ast' => '*',
+ 'brvbar' => '¦',
+ 'bsol' => '\',
+ 'cent' => '¢',
+ 'colon' => ':',
+ 'comma' => ',',
+ 'commat' => '@',
+ 'copy' => '©',
+ 'curren' => '¤',
+ 'darr' => '↓',
+ 'deg' => '°',
+ 'divide' => '÷',
+ 'dollar' => '$',
+ 'equals' => '=',
+ 'excl' => '!',
+ 'frac12' => '½',
+ 'frac14' => '¼',
+ 'frac18' => '⅛',
+ 'frac34' => '¾',
+ 'frac38' => '⅜',
+ 'frac58' => '⅝',
+ 'frac78' => '⅞',
+ 'gt' => '>',
+ 'half' => '½',
+ 'horbar' => '―',
+ 'hyphen' => '‐',
+ 'iexcl' => '¡',
+ 'iquest' => '¿',
+ 'laquo' => '«',
+ 'larr' => '←',
+ 'lcub' => '{',
+ 'ldquo' => '“',
+ 'lowbar' => '_',
+ 'lpar' => '(',
+ 'lsqb' => '[',
+ 'lsquo' => '‘',
+ 'micro' => 'µ',
+ 'middot' => '·',
+ 'nbsp' => ' ',
+ 'not' => '¬',
+ 'num' => '#',
+ 'ohm' => 'Ω',
+ 'ordf' => 'ª',
+ 'ordm' => 'º',
+ 'para' => '¶',
+ 'percnt' => '%',
+ 'period' => '.',
+ 'plus' => '+',
+ 'plusmn' => '±',
+ 'pound' => '£',
+ 'quest' => '?',
+ 'quot' => '"',
+ 'raquo' => '»',
+ 'rarr' => '→',
+ 'rcub' => '}',
+ 'rdquo' => '”',
+ 'reg' => '®',
+ 'rpar' => ')',
+ 'rsqb' => ']',
+ 'rsquo' => '’',
+ 'sect' => '§',
+ 'semi' => ';',
+ 'shy' => '',
+ 'sol' => '/',
+ 'sung' => '♪',
+ 'sup1' => '¹',
+ 'sup2' => '²',
+ 'sup3' => '³',
+ 'times' => '×',
+ 'trade' => '™',
+ 'uarr' => '↑',
+ 'verbar' => '|',
+ 'yen' => '¥',
+ 'blank' => '␣',
+ 'blk12' => '▒',
+ 'blk14' => '░',
+ 'blk34' => '▓',
+ 'block' => '█',
+ 'bull' => '•',
+ 'caret' => '⁁',
+ 'check' => '✓',
+ 'cir' => '○',
+ 'clubs' => '♣',
+ 'copysr' => '℗',
+ 'cross' => '✗',
+ 'Dagger' => '‡',
+ 'dagger' => '†',
+ 'dash' => '‐',
+ 'diams' => '♦',
+ 'dlcrop' => '⌍',
+ 'drcrop' => '⌌',
+ 'dtri' => '▿',
+ 'dtrif' => '▾',
+ 'emsp' => ' ',
+ 'emsp13' => ' ',
+ 'emsp14' => ' ',
+ 'ensp' => ' ',
+ 'female' => '♀',
+ 'ffilig' => 'ffi',
+ 'fflig' => 'ff',
+ 'ffllig' => 'ffl',
+ 'filig' => 'fi',
+ 'flat' => '♭',
+ 'fllig' => 'fl',
+ 'frac13' => '⅓',
+ 'frac15' => '⅕',
+ 'frac16' => '⅙',
+ 'frac23' => '⅔',
+ 'frac25' => '⅖',
+ 'frac35' => '⅗',
+ 'frac45' => '⅘',
+ 'frac56' => '⅚',
+ 'hairsp' => ' ',
+ 'hearts' => '♥',
+ 'hellip' => '…',
+ 'hybull' => '⁃',
+ 'incare' => '℅',
+ 'ldquor' => '„',
+ 'lhblk' => '▄',
+ 'loz' => '◊',
+ 'lozf' => '⧫',
+ 'lsquor' => '‚',
+ 'ltri' => '◃',
+ 'ltrif' => '◂',
+ 'male' => '♂',
+ 'malt' => '✠',
+ 'marker' => '▮',
+ 'mdash' => '—',
+ 'mldr' => '…',
+ 'natur' => '♮',
+ 'ndash' => '–',
+ 'nldr' => '‥',
+ 'numsp' => ' ',
+ 'phone' => '☎',
+ 'puncsp' => ' ',
+ 'rdquor' => '”',
+ 'rect' => '▭',
+ 'rsquor' => '’',
+ 'rtri' => '▹',
+ 'rtrif' => '▸',
+ 'rx' => '℞',
+ 'sext' => '✶',
+ 'sharp' => '♯',
+ 'spades' => '♠',
+ 'squ' => '□',
+ 'squf' => '▪',
+ 'star' => '☆',
+ 'starf' => '★',
+ 'target' => '⌖',
+ 'telrec' => '⌕',
+ 'thinsp' => ' ',
+ 'uhblk' => '▀',
+ 'ulcrop' => '⌏',
+ 'urcrop' => '⌎',
+ 'utri' => '▵',
+ 'utrif' => '▴',
+ 'vellip' => '⋮',
+ 'af' => '',
+ 'aopf' => '𝕒',
+ 'asympeq' => '≍',
+ 'bopf' => '𝕓',
+ 'copf' => '𝕔',
+ 'Cross' => '⨯',
+ 'DD' => 'ⅅ',
+ 'dd' => 'ⅆ',
+ 'dopf' => '𝕕',
+ 'DownArrowBar' => '⤓',
+ 'DownBreve' => '̑',
+ 'DownLeftRightVector' => '⥐',
+ 'DownLeftTeeVector' => '⥞',
+ 'DownLeftVectorBar' => '⥖',
+ 'DownRightTeeVector' => '⥟',
+ 'DownRightVectorBar' => '⥗',
+ 'ee' => 'ⅇ',
+ 'EmptySmallSquare' => '◻',
+ 'EmptyVerySmallSquare' => '▫',
+ 'eopf' => '𝕖',
+ 'Equal' => '⩵',
+ 'FilledSmallSquare' => '◼',
+ 'FilledVerySmallSquare' => '▪',
+ 'fopf' => '𝕗',
+ 'gopf' => '𝕘',
+ 'GreaterGreater' => '⪢',
+ 'Hat' => '^',
+ 'hopf' => '𝕙',
+ 'HorizontalLine' => '─',
+ 'ic' => '',
+ 'ii' => 'ⅈ',
+ 'iopf' => '𝕚',
+ 'it' => '',
+ 'jopf' => '𝕛',
+ 'kopf' => '𝕜',
+ 'larrb' => '⇤',
+ 'LeftDownTeeVector' => '⥡',
+ 'LeftDownVectorBar' => '⥙',
+ 'LeftRightVector' => '⥎',
+ 'LeftTeeVector' => '⥚',
+ 'LeftTriangleBar' => '⧏',
+ 'LeftUpDownVector' => '⥑',
+ 'LeftUpTeeVector' => '⥠',
+ 'LeftUpVectorBar' => '⥘',
+ 'LeftVectorBar' => '⥒',
+ 'LessLess' => '⪡',
+ 'lopf' => '𝕝',
+ 'mapstodown' => '↧',
+ 'mapstoleft' => '↤',
+ 'mapstoup' => '↥',
+ 'MediumSpace' => ' ',
+ 'mopf' => '𝕞',
+ 'nbump' => '≎̸',
+ 'nbumpe' => '≏̸',
+ 'nesim' => '≂̸',
+ 'NewLine' => '
',
+ 'NoBreak' => '',
+ 'nopf' => '𝕟',
+ 'NotCupCap' => '≭',
+ 'NotHumpEqual' => '≏̸',
+ 'NotLeftTriangleBar' => '⧏̸',
+ 'NotNestedGreaterGreater' => '⪢̸',
+ 'NotNestedLessLess' => '⪡̸',
+ 'NotRightTriangleBar' => '⧐̸',
+ 'NotSquareSubset' => '⊏̸',
+ 'NotSquareSuperset' => '⊐̸',
+ 'NotSucceedsTilde' => '≿̸',
+ 'oopf' => '𝕠',
+ 'OverBar' => '¯',
+ 'OverBrace' => '︷',
+ 'OverBracket' => '⎴',
+ 'OverParenthesis' => '︵',
+ 'planckh' => 'ℎ',
+ 'popf' => '𝕡',
+ 'Product' => '∏',
+ 'qopf' => '𝕢',
+ 'rarrb' => '⇥',
+ 'RightDownTeeVector' => '⥝',
+ 'RightDownVectorBar' => '⥕',
+ 'RightTeeVector' => '⥛',
+ 'RightTriangleBar' => '⧐',
+ 'RightUpDownVector' => '⥏',
+ 'RightUpTeeVector' => '⥜',
+ 'RightUpVectorBar' => '⥔',
+ 'RightVectorBar' => '⥓',
+ 'ropf' => '𝕣',
+ 'RoundImplies' => '⥰',
+ 'RuleDelayed' => '⧴',
+ 'sopf' => '𝕤',
+ 'Tab' => ' ',
+ 'ThickSpace' => ' ',
+ 'topf' => '𝕥',
+ 'UnderBar' => '̲',
+ 'UnderBrace' => '︸',
+ 'UnderBracket' => '⎵',
+ 'UnderParenthesis' => '︶',
+ 'uopf' => '𝕦',
+ 'UpArrowBar' => '⤒',
+ 'Upsilon' => 'Υ',
+ 'VerticalLine' => '|',
+ 'VerticalSeparator' => '❘',
+ 'vopf' => '𝕧',
+ 'wopf' => '𝕨',
+ 'xopf' => '𝕩',
+ 'yopf' => '𝕪',
+ 'ZeroWidthSpace' => '',
+ 'zopf' => '𝕫',
+ 'angle' => '∠',
+ 'ApplyFunction' => '',
+ 'approx' => '≈',
+ 'approxeq' => '≊',
+ 'Assign' => '≔',
+ 'backcong' => '≌',
+ 'backepsilon' => '϶',
+ 'backprime' => '‵',
+ 'backsim' => '∽',
+ 'backsimeq' => '⋍',
+ 'Backslash' => '∖',
+ 'barwedge' => '⌅',
+ 'Because' => '∵',
+ 'because' => '∵',
+ 'Bernoullis' => 'ℬ',
+ 'between' => '≬',
+ 'bigcap' => '⋂',
+ 'bigcirc' => '◯',
+ 'bigcup' => '⋃',
+ 'bigodot' => '⨀',
+ 'bigoplus' => '⨁',
+ 'bigotimes' => '⨂',
+ 'bigsqcup' => '⨆',
+ 'bigstar' => '★',
+ 'bigtriangledown' => '▽',
+ 'bigtriangleup' => '△',
+ 'biguplus' => '⨄',
+ 'bigvee' => '⋁',
+ 'bigwedge' => '⋀',
+ 'bkarow' => '⤍',
+ 'blacklozenge' => '⧫',
+ 'blacksquare' => '▪',
+ 'blacktriangle' => '▴',
+ 'blacktriangledown' => '▾',
+ 'blacktriangleleft' => '◂',
+ 'blacktriangleright' => '▸',
+ 'bot' => '⊥',
+ 'boxminus' => '⊟',
+ 'boxplus' => '⊞',
+ 'boxtimes' => '⊠',
+ 'Breve' => '˘',
+ 'bullet' => '•',
+ 'Bumpeq' => '≎',
+ 'bumpeq' => '≏',
+ 'CapitalDifferentialD' => 'ⅅ',
+ 'Cayleys' => 'ℭ',
+ 'Cedilla' => '¸',
+ 'CenterDot' => '·',
+ 'centerdot' => '·',
+ 'checkmark' => '✓',
+ 'circeq' => '≗',
+ 'circlearrowleft' => '↺',
+ 'circlearrowright' => '↻',
+ 'circledast' => '⊛',
+ 'circledcirc' => '⊚',
+ 'circleddash' => '⊝',
+ 'CircleDot' => '⊙',
+ 'circledR' => '®',
+ 'circledS' => 'Ⓢ',
+ 'CircleMinus' => '⊖',
+ 'CirclePlus' => '⊕',
+ 'CircleTimes' => '⊗',
+ 'ClockwiseContourIntegral' => '∲',
+ 'CloseCurlyDoubleQuote' => '”',
+ 'CloseCurlyQuote' => '’',
+ 'clubsuit' => '♣',
+ 'coloneq' => '≔',
+ 'complement' => '∁',
+ 'complexes' => 'ℂ',
+ 'Congruent' => '≡',
+ 'ContourIntegral' => '∮',
+ 'Coproduct' => '∐',
+ 'CounterClockwiseContourIntegral' => '∳',
+ 'CupCap' => '≍',
+ 'curlyeqprec' => '⋞',
+ 'curlyeqsucc' => '⋟',
+ 'curlyvee' => '⋎',
+ 'curlywedge' => '⋏',
+ 'curvearrowleft' => '↶',
+ 'curvearrowright' => '↷',
+ 'dbkarow' => '⤏',
+ 'ddagger' => '‡',
+ 'ddotseq' => '⩷',
+ 'Del' => '∇',
+ 'DiacriticalAcute' => '´',
+ 'DiacriticalDot' => '˙',
+ 'DiacriticalDoubleAcute' => '˝',
+ 'DiacriticalGrave' => '`',
+ 'DiacriticalTilde' => '˜',
+ 'Diamond' => '⋄',
+ 'diamond' => '⋄',
+ 'diamondsuit' => '♦',
+ 'DifferentialD' => 'ⅆ',
+ 'digamma' => 'ϝ',
+ 'div' => '÷',
+ 'divideontimes' => '⋇',
+ 'doteq' => '≐',
+ 'doteqdot' => '≑',
+ 'DotEqual' => '≐',
+ 'dotminus' => '∸',
+ 'dotplus' => '∔',
+ 'dotsquare' => '⊡',
+ 'doublebarwedge' => '⌆',
+ 'DoubleContourIntegral' => '∯',
+ 'DoubleDot' => '¨',
+ 'DoubleDownArrow' => '⇓',
+ 'DoubleLeftArrow' => '⇐',
+ 'DoubleLeftRightArrow' => '⇔',
+ 'DoubleLeftTee' => '⫤',
+ 'DoubleLongLeftArrow' => '⟸',
+ 'DoubleLongLeftRightArrow' => '⟺',
+ 'DoubleLongRightArrow' => '⟹',
+ 'DoubleRightArrow' => '⇒',
+ 'DoubleRightTee' => '⊨',
+ 'DoubleUpArrow' => '⇑',
+ 'DoubleUpDownArrow' => '⇕',
+ 'DoubleVerticalBar' => '∥',
+ 'DownArrow' => '↓',
+ 'Downarrow' => '⇓',
+ 'downarrow' => '↓',
+ 'DownArrowUpArrow' => '⇵',
+ 'downdownarrows' => '⇊',
+ 'downharpoonleft' => '⇃',
+ 'downharpoonright' => '⇂',
+ 'DownLeftVector' => '↽',
+ 'DownRightVector' => '⇁',
+ 'DownTee' => '⊤',
+ 'DownTeeArrow' => '↧',
+ 'drbkarow' => '⤐',
+ 'Element' => '∈',
+ 'emptyset' => '∅',
+ 'eqcirc' => '≖',
+ 'eqcolon' => '≕',
+ 'eqsim' => '≂',
+ 'eqslantgtr' => '⪖',
+ 'eqslantless' => '⪕',
+ 'EqualTilde' => '≂',
+ 'Equilibrium' => '⇌',
+ 'Exists' => '∃',
+ 'expectation' => 'ℰ',
+ 'ExponentialE' => 'ⅇ',
+ 'exponentiale' => 'ⅇ',
+ 'fallingdotseq' => '≒',
+ 'ForAll' => '∀',
+ 'Fouriertrf' => 'ℱ',
+ 'geq' => '≥',
+ 'geqq' => '≧',
+ 'geqslant' => '⩾',
+ 'gg' => '≫',
+ 'ggg' => '⋙',
+ 'gnapprox' => '⪊',
+ 'gneq' => '⪈',
+ 'gneqq' => '≩',
+ 'GreaterEqual' => '≥',
+ 'GreaterEqualLess' => '⋛',
+ 'GreaterFullEqual' => '≧',
+ 'GreaterLess' => '≷',
+ 'GreaterSlantEqual' => '⩾',
+ 'GreaterTilde' => '≳',
+ 'gtrapprox' => '⪆',
+ 'gtrdot' => '⋗',
+ 'gtreqless' => '⋛',
+ 'gtreqqless' => '⪌',
+ 'gtrless' => '≷',
+ 'gtrsim' => '≳',
+ 'gvertneqq' => '≩︀',
+ 'Hacek' => 'ˇ',
+ 'hbar' => 'ℏ',
+ 'heartsuit' => '♥',
+ 'HilbertSpace' => 'ℋ',
+ 'hksearow' => '⤥',
+ 'hkswarow' => '⤦',
+ 'hookleftarrow' => '↩',
+ 'hookrightarrow' => '↪',
+ 'hslash' => 'ℏ',
+ 'HumpDownHump' => '≎',
+ 'HumpEqual' => '≏',
+ 'iiiint' => '⨌',
+ 'iiint' => '∭',
+ 'Im' => 'ℑ',
+ 'ImaginaryI' => 'ⅈ',
+ 'imagline' => 'ℐ',
+ 'imagpart' => 'ℑ',
+ 'Implies' => '⇒',
+ 'in' => '∈',
+ 'integers' => 'ℤ',
+ 'Integral' => '∫',
+ 'intercal' => '⊺',
+ 'Intersection' => '⋂',
+ 'intprod' => '⨼',
+ 'InvisibleComma' => '',
+ 'InvisibleTimes' => '',
+ 'langle' => '〈',
+ 'Laplacetrf' => 'ℒ',
+ 'lbrace' => '{',
+ 'lbrack' => '[',
+ 'LeftAngleBracket' => '〈',
+ 'LeftArrow' => '←',
+ 'Leftarrow' => '⇐',
+ 'leftarrow' => '←',
+ 'LeftArrowBar' => '⇤',
+ 'LeftArrowRightArrow' => '⇆',
+ 'leftarrowtail' => '↢',
+ 'LeftCeiling' => '⌈',
+ 'LeftDoubleBracket' => '〚',
+ 'LeftDownVector' => '⇃',
+ 'LeftFloor' => '⌊',
+ 'leftharpoondown' => '↽',
+ 'leftharpoonup' => '↼',
+ 'leftleftarrows' => '⇇',
+ 'LeftRightArrow' => '↔',
+ 'Leftrightarrow' => '⇔',
+ 'leftrightarrow' => '↔',
+ 'leftrightarrows' => '⇆',
+ 'leftrightharpoons' => '⇋',
+ 'leftrightsquigarrow' => '↭',
+ 'LeftTee' => '⊣',
+ 'LeftTeeArrow' => '↤',
+ 'leftthreetimes' => '⋋',
+ 'LeftTriangle' => '⊲',
+ 'LeftTriangleEqual' => '⊴',
+ 'LeftUpVector' => '↿',
+ 'LeftVector' => '↼',
+ 'leq' => '≤',
+ 'leqq' => '≦',
+ 'leqslant' => '⩽',
+ 'lessapprox' => '⪅',
+ 'lessdot' => '⋖',
+ 'lesseqgtr' => '⋚',
+ 'lesseqqgtr' => '⪋',
+ 'LessEqualGreater' => '⋚',
+ 'LessFullEqual' => '≦',
+ 'LessGreater' => '≶',
+ 'lessgtr' => '≶',
+ 'lesssim' => '≲',
+ 'LessSlantEqual' => '⩽',
+ 'LessTilde' => '≲',
+ 'll' => '≪',
+ 'llcorner' => '⌞',
+ 'Lleftarrow' => '⇚',
+ 'lmoustache' => '⎰',
+ 'lnapprox' => '⪉',
+ 'lneq' => '⪇',
+ 'lneqq' => '≨',
+ 'LongLeftArrow' => '⟵',
+ 'Longleftarrow' => '⟸',
+ 'longleftarrow' => '⟵',
+ 'LongLeftRightArrow' => '⟷',
+ 'Longleftrightarrow' => '⟺',
+ 'longleftrightarrow' => '⟷',
+ 'longmapsto' => '⟼',
+ 'LongRightArrow' => '⟶',
+ 'Longrightarrow' => '⟹',
+ 'longrightarrow' => '⟶',
+ 'looparrowleft' => '↫',
+ 'looparrowright' => '↬',
+ 'LowerLeftArrow' => '↙',
+ 'LowerRightArrow' => '↘',
+ 'lozenge' => '◊',
+ 'lrcorner' => '⌟',
+ 'Lsh' => '↰',
+ 'lvertneqq' => '≨︀',
+ 'maltese' => '✠',
+ 'mapsto' => '↦',
+ 'measuredangle' => '∡',
+ 'Mellintrf' => 'ℳ',
+ 'MinusPlus' => '∓',
+ 'mp' => '∓',
+ 'multimap' => '⊸',
+ 'napprox' => '≉',
+ 'natural' => '♮',
+ 'naturals' => 'ℕ',
+ 'nearrow' => '↗',
+ 'NegativeMediumSpace' => '',
+ 'NegativeThickSpace' => '',
+ 'NegativeThinSpace' => '',
+ 'NegativeVeryThinSpace' => '',
+ 'NestedGreaterGreater' => '≫',
+ 'NestedLessLess' => '≪',
+ 'nexists' => '∄',
+ 'ngeq' => '≱',
+ 'ngeqq' => '≧̸',
+ 'ngeqslant' => '⩾̸',
+ 'ngtr' => '≯',
+ 'nLeftarrow' => '⇍',
+ 'nleftarrow' => '↚',
+ 'nLeftrightarrow' => '⇎',
+ 'nleftrightarrow' => '↮',
+ 'nleq' => '≰',
+ 'nleqq' => '≦̸',
+ 'nleqslant' => '⩽̸',
+ 'nless' => '≮',
+ 'NonBreakingSpace' => ' ',
+ 'NotCongruent' => '≢',
+ 'NotDoubleVerticalBar' => '∦',
+ 'NotElement' => '∉',
+ 'NotEqual' => '≠',
+ 'NotEqualTilde' => '≂̸',
+ 'NotExists' => '∄',
+ 'NotGreater' => '≯',
+ 'NotGreaterEqual' => '≱',
+ 'NotGreaterFullEqual' => '≦̸',
+ 'NotGreaterGreater' => '≫̸',
+ 'NotGreaterLess' => '≹',
+ 'NotGreaterSlantEqual' => '⩾̸',
+ 'NotGreaterTilde' => '≵',
+ 'NotHumpDownHump' => '≎̸',
+ 'NotLeftTriangle' => '⋪',
+ 'NotLeftTriangleEqual' => '⋬',
+ 'NotLess' => '≮',
+ 'NotLessEqual' => '≰',
+ 'NotLessGreater' => '≸',
+ 'NotLessLess' => '≪̸',
+ 'NotLessSlantEqual' => '⩽̸',
+ 'NotLessTilde' => '≴',
+ 'NotPrecedes' => '⊀',
+ 'NotPrecedesEqual' => '⪯̸',
+ 'NotPrecedesSlantEqual' => '⋠',
+ 'NotReverseElement' => '∌',
+ 'NotRightTriangle' => '⋫',
+ 'NotRightTriangleEqual' => '⋭',
+ 'NotSquareSubsetEqual' => '⋢',
+ 'NotSquareSupersetEqual' => '⋣',
+ 'NotSubset' => '⊂⃒',
+ 'NotSubsetEqual' => '⊈',
+ 'NotSucceeds' => '⊁',
+ 'NotSucceedsEqual' => '⪰̸',
+ 'NotSucceedsSlantEqual' => '⋡',
+ 'NotSuperset' => '⊃⃒',
+ 'NotSupersetEqual' => '⊉',
+ 'NotTilde' => '≁',
+ 'NotTildeEqual' => '≄',
+ 'NotTildeFullEqual' => '≇',
+ 'NotTildeTilde' => '≉',
+ 'NotVerticalBar' => '∤',
+ 'nparallel' => '∦',
+ 'nprec' => '⊀',
+ 'npreceq' => '⪯̸',
+ 'nRightarrow' => '⇏',
+ 'nrightarrow' => '↛',
+ 'nshortmid' => '∤',
+ 'nshortparallel' => '∦',
+ 'nsimeq' => '≄',
+ 'nsubset' => '⊂⃒',
+ 'nsubseteq' => '⊈',
+ 'nsubseteqq' => '⫅̸',
+ 'nsucc' => '⊁',
+ 'nsucceq' => '⪰̸',
+ 'nsupset' => '⊃⃒',
+ 'nsupseteq' => '⊉',
+ 'nsupseteqq' => '⫆̸',
+ 'ntriangleleft' => '⋪',
+ 'ntrianglelefteq' => '⋬',
+ 'ntriangleright' => '⋫',
+ 'ntrianglerighteq' => '⋭',
+ 'nwarrow' => '↖',
+ 'oint' => '∮',
+ 'OpenCurlyDoubleQuote' => '“',
+ 'OpenCurlyQuote' => '‘',
+ 'orderof' => 'ℴ',
+ 'parallel' => '∥',
+ 'PartialD' => '∂',
+ 'pitchfork' => '⋔',
+ 'PlusMinus' => '±',
+ 'pm' => '±',
+ 'Poincareplane' => 'ℌ',
+ 'prec' => '≺',
+ 'precapprox' => '⪷',
+ 'preccurlyeq' => '≼',
+ 'Precedes' => '≺',
+ 'PrecedesEqual' => '⪯',
+ 'PrecedesSlantEqual' => '≼',
+ 'PrecedesTilde' => '≾',
+ 'preceq' => '⪯',
+ 'precnapprox' => '⪹',
+ 'precneqq' => '⪵',
+ 'precnsim' => '⋨',
+ 'precsim' => '≾',
+ 'primes' => 'ℙ',
+ 'Proportion' => '∷',
+ 'Proportional' => '∝',
+ 'propto' => '∝',
+ 'quaternions' => 'ℍ',
+ 'questeq' => '≟',
+ 'rangle' => '〉',
+ 'rationals' => 'ℚ',
+ 'rbrace' => '}',
+ 'rbrack' => ']',
+ 'Re' => 'ℜ',
+ 'realine' => 'ℛ',
+ 'realpart' => 'ℜ',
+ 'reals' => 'ℝ',
+ 'ReverseElement' => '∋',
+ 'ReverseEquilibrium' => '⇋',
+ 'ReverseUpEquilibrium' => '⥯',
+ 'RightAngleBracket' => '〉',
+ 'RightArrow' => '→',
+ 'Rightarrow' => '⇒',
+ 'rightarrow' => '→',
+ 'RightArrowBar' => '⇥',
+ 'RightArrowLeftArrow' => '⇄',
+ 'rightarrowtail' => '↣',
+ 'RightCeiling' => '⌉',
+ 'RightDoubleBracket' => '〛',
+ 'RightDownVector' => '⇂',
+ 'RightFloor' => '⌋',
+ 'rightharpoondown' => '⇁',
+ 'rightharpoonup' => '⇀',
+ 'rightleftarrows' => '⇄',
+ 'rightleftharpoons' => '⇌',
+ 'rightrightarrows' => '⇉',
+ 'rightsquigarrow' => '↝',
+ 'RightTee' => '⊢',
+ 'RightTeeArrow' => '↦',
+ 'rightthreetimes' => '⋌',
+ 'RightTriangle' => '⊳',
+ 'RightTriangleEqual' => '⊵',
+ 'RightUpVector' => '↾',
+ 'RightVector' => '⇀',
+ 'risingdotseq' => '≓',
+ 'rmoustache' => '⎱',
+ 'Rrightarrow' => '⇛',
+ 'Rsh' => '↱',
+ 'searrow' => '↘',
+ 'setminus' => '∖',
+ 'ShortDownArrow' => '↓',
+ 'ShortLeftArrow' => '←',
+ 'shortmid' => '∣',
+ 'shortparallel' => '∥',
+ 'ShortRightArrow' => '→',
+ 'ShortUpArrow' => '↑',
+ 'simeq' => '≃',
+ 'SmallCircle' => '∘',
+ 'smallsetminus' => '∖',
+ 'spadesuit' => '♠',
+ 'Sqrt' => '√',
+ 'sqsubset' => '⊏',
+ 'sqsubseteq' => '⊑',
+ 'sqsupset' => '⊐',
+ 'sqsupseteq' => '⊒',
+ 'Square' => '□',
+ 'SquareIntersection' => '⊓',
+ 'SquareSubset' => '⊏',
+ 'SquareSubsetEqual' => '⊑',
+ 'SquareSuperset' => '⊐',
+ 'SquareSupersetEqual' => '⊒',
+ 'SquareUnion' => '⊔',
+ 'Star' => '⋆',
+ 'straightepsilon' => 'ϵ',
+ 'straightphi' => 'ϕ',
+ 'Subset' => '⋐',
+ 'subset' => '⊂',
+ 'subseteq' => '⊆',
+ 'subseteqq' => '⫅',
+ 'SubsetEqual' => '⊆',
+ 'subsetneq' => '⊊',
+ 'subsetneqq' => '⫋',
+ 'succ' => '≻',
+ 'succapprox' => '⪸',
+ 'succcurlyeq' => '≽',
+ 'Succeeds' => '≻',
+ 'SucceedsEqual' => '⪰',
+ 'SucceedsSlantEqual' => '≽',
+ 'SucceedsTilde' => '≿',
+ 'succeq' => '⪰',
+ 'succnapprox' => '⪺',
+ 'succneqq' => '⪶',
+ 'succnsim' => '⋩',
+ 'succsim' => '≿',
+ 'SuchThat' => '∋',
+ 'Sum' => '∑',
+ 'Superset' => '⊃',
+ 'SupersetEqual' => '⊇',
+ 'Supset' => '⋑',
+ 'supset' => '⊃',
+ 'supseteq' => '⊇',
+ 'supseteqq' => '⫆',
+ 'supsetneq' => '⊋',
+ 'supsetneqq' => '⫌',
+ 'swarrow' => '↙',
+ 'Therefore' => '∴',
+ 'therefore' => '∴',
+ 'thickapprox' => '≈',
+ 'thicksim' => '∼',
+ 'ThinSpace' => ' ',
+ 'Tilde' => '∼',
+ 'TildeEqual' => '≃',
+ 'TildeFullEqual' => '≅',
+ 'TildeTilde' => '≈',
+ 'toea' => '⤨',
+ 'tosa' => '⤩',
+ 'triangle' => '▵',
+ 'triangledown' => '▿',
+ 'triangleleft' => '◃',
+ 'trianglelefteq' => '⊴',
+ 'triangleq' => '≜',
+ 'triangleright' => '▹',
+ 'trianglerighteq' => '⊵',
+ 'TripleDot' => '⃛',
+ 'twoheadleftarrow' => '↞',
+ 'twoheadrightarrow' => '↠',
+ 'ulcorner' => '⌜',
+ 'Union' => '⋃',
+ 'UnionPlus' => '⊎',
+ 'UpArrow' => '↑',
+ 'Uparrow' => '⇑',
+ 'uparrow' => '↑',
+ 'UpArrowDownArrow' => '⇅',
+ 'UpDownArrow' => '↕',
+ 'Updownarrow' => '⇕',
+ 'updownarrow' => '↕',
+ 'UpEquilibrium' => '⥮',
+ 'upharpoonleft' => '↿',
+ 'upharpoonright' => '↾',
+ 'UpperLeftArrow' => '↖',
+ 'UpperRightArrow' => '↗',
+ 'upsilon' => 'υ',
+ 'UpTee' => '⊥',
+ 'UpTeeArrow' => '↥',
+ 'upuparrows' => '⇈',
+ 'urcorner' => '⌝',
+ 'varepsilon' => 'ε',
+ 'varkappa' => 'ϰ',
+ 'varnothing' => '∅',
+ 'varphi' => 'φ',
+ 'varpi' => 'ϖ',
+ 'varpropto' => '∝',
+ 'varrho' => 'ϱ',
+ 'varsigma' => 'ς',
+ 'varsubsetneq' => '⊊︀',
+ 'varsubsetneqq' => '⫋︀',
+ 'varsupsetneq' => '⊋︀',
+ 'varsupsetneqq' => '⫌︀',
+ 'vartheta' => 'ϑ',
+ 'vartriangleleft' => '⊲',
+ 'vartriangleright' => '⊳',
+ 'Vee' => '⋁',
+ 'vee' => '∨',
+ 'Vert' => '‖',
+ 'vert' => '|',
+ 'VerticalBar' => '∣',
+ 'VerticalTilde' => '≀',
+ 'VeryThinSpace' => ' ',
+ 'Wedge' => '⋀',
+ 'wedge' => '∧',
+ 'wp' => '℘',
+ 'wr' => '≀',
+ 'zeetrf' => 'ℨ'
+ }
+#:startdoc:
+
+# Converts XHTML+MathML named entities to Numeric Character References
+#
+# :call-seq:
+# string.to_ncr -> string
+#
+ def to_ncr
+ self.gsub(/&(?:(lt|gt|amp|quot|apos)|[a-zA-Z0-9]+);/){|s| $1 ? s : s.convert_to_ncr}
+ end
+
+# Converts XHTML+MathML named entities to Numeric Character References
+#
+# :call-seq:
+# string.to_ncr! -> str or nil
+#
+# Substitution is done in-place.
+ def to_ncr!
+ self.gsub!(/&(?:(lt|gt|amp|quot|apos)|[a-zA-Z0-9]+);/){|s| $1 ? s : s.convert_to_ncr}
+ end
+
+ protected
+
+ def convert_to_ncr #:nodoc:
+ self =~ /^&([a-zA-Z0-9]+);$/
+ name = $1
+ return MATHML_ENTITIES.has_key?(name) ? MATHML_ENTITIES[name] : "&" + name + ";"
+ end
+
+end
+
+require 'rexml/element'
+module REXML #:nodoc:
+ class Element
+
+# Convert XHTML+MathML Named Entities in a REXML::Element to Numeric Character References
+#
+# :call-seq:
+# tree.to_ncr -> REXML::Element
+#
+# REXML, typically, converts NCRs to utf-8 characters, which is what you'll see when you
+# access the resulting REXML document.
+ def to_ncr
+ XPath.each(self, '//*') { |el|
+ el.texts.each_index {|i|
+ el.texts[i].value = el.texts[i].to_s.to_ncr
+ }
+ el.attributes.each { |name,val|
+ el.attributes[name] = val.to_ncr
+ }
+ }
+ return self
+ end
+ end
end
diff --git a/lib/string_utils.rb b/lib/string_utils.rb
deleted file mode 100644
index 04ec2b2c..00000000
--- a/lib/string_utils.rb
+++ /dev/null
@@ -1,2157 +0,0 @@
-# Some useful additions to the String class
-
-class String
-
- def is_utf8?
- self =~ /^(
- [\x09\x0A\x0D\x20-\x7E] # ASCII
- | [\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte
- | \xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs
- | [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte
- | \xED[\x80-\x9F][\x80-\xBF] # excluding surrogates
- | \xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3
- | [\xF1-\xF3][\x80-\xBF]{3} # planes 4-15
- | \xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16
- )*$/x;
- end
-
- MATHML_ENTITIES = {
- 'Alpha' => 'Α',
- 'Beta' => 'Β',
- 'Epsilon' => 'Ε',
- 'Zeta' => 'Ζ',
- 'Eta' => 'Η',
- 'Iota' => 'Ι',
- 'Kappa' => 'Κ',
- 'Mu' => 'Μ',
- 'Nu' => 'Ν',
- 'Omicron' => 'Ο',
- 'Rho' => 'Ρ',
- 'Tau' => 'Τ',
- 'Chi' => 'Χ',
- 'epsilon' => 'ε',
- 'zeta' => 'ζ',
- 'omicron' => 'ο',
- 'sigmaf' => 'ς',
- 'thetasym' => 'ϑ',
- 'upsih' => 'ϒ',
- 'oline' => '‾',
- 'frasl' => '⁄',
- 'alefsym' => 'ℵ',
- 'crarr' => '↵',
- 'empty' => '∅',
- 'amp' => '&',
- 'lt' => '<',
- 'zwnj' => '',
- 'zwj' => '',
- 'lrm' => '',
- 'rlm' => '',
- 'sbquo' => '‚',
- 'bdquo' => '„',
- 'lsaquo' => '‹',
- 'rsaquo' => '›',
- 'euro' => '€',
- 'angzarr' => '⍼',
- 'cirmid' => '⫯',
- 'cudarrl' => '⤸',
- 'cudarrr' => '⤵',
- 'cularr' => '↶',
- 'cularrp' => '⤽',
- 'curarr' => '↷',
- 'curarrm' => '⤼',
- 'Darr' => '↡',
- 'dArr' => '⇓',
- 'ddarr' => '⇊',
- 'DDotrahd' => '⤑',
- 'dfisht' => '⥿',
- 'dHar' => '⥥',
- 'dharl' => '⇃',
- 'dharr' => '⇂',
- 'duarr' => '⇵',
- 'duhar' => '⥯',
- 'dzigrarr' => '⟿',
- 'erarr' => '⥱',
- 'hArr' => '⇔',
- 'harr' => '↔',
- 'harrcir' => '⥈',
- 'harrw' => '↭',
- 'hoarr' => '⇿',
- 'imof' => '⊷',
- 'lAarr' => '⇚',
- 'Larr' => '↞',
- 'larrbfs' => '⤟',
- 'larrfs' => '⤝',
- 'larrhk' => '↩',
- 'larrlp' => '↫',
- 'larrpl' => '⤹',
- 'larrsim' => '⥳',
- 'larrtl' => '↢',
- 'lAtail' => '⤛',
- 'latail' => '⤙',
- 'lBarr' => '⤎',
- 'lbarr' => '⤌',
- 'ldca' => '⤶',
- 'ldrdhar' => '⥧',
- 'ldrushar' => '⥋',
- 'ldsh' => '↲',
- 'lfisht' => '⥼',
- 'lHar' => '⥢',
- 'lhard' => '↽',
- 'lharu' => '↼',
- 'lharul' => '⥪',
- 'llarr' => '⇇',
- 'llhard' => '⥫',
- 'loarr' => '⇽',
- 'lrarr' => '⇆',
- 'lrhar' => '⇋',
- 'lrhard' => '⥭',
- 'lsh' => '↰',
- 'lurdshar' => '⥊',
- 'luruhar' => '⥦',
- 'Map' => '⤅',
- 'map' => '↦',
- 'midcir' => '⫰',
- 'mumap' => '⊸',
- 'nearhk' => '⤤',
- 'neArr' => '⇗',
- 'nearr' => '↗',
- 'nesear' => '⤨',
- 'nhArr' => '⇎',
- 'nharr' => '↮',
- 'nlArr' => '⇍',
- 'nlarr' => '↚',
- 'nrArr' => '⇏',
- 'nrarr' => '↛',
- 'nrarrc' => '⤳̸',
- 'nrarrw' => '↝̸',
- 'nvHarr' => '⤄',
- 'nvlArr' => '⤂',
- 'nvrArr' => '⤃',
- 'nwarhk' => '⤣',
- 'nwArr' => '⇖',
- 'nwarr' => '↖',
- 'nwnear' => '⤧',
- 'olarr' => '↺',
- 'orarr' => '↻',
- 'origof' => '⊶',
- 'rAarr' => '⇛',
- 'Rarr' => '↠',
- 'rarrap' => '⥵',
- 'rarrbfs' => '⤠',
- 'rarrc' => '⤳',
- 'rarrfs' => '⤞',
- 'rarrhk' => '↪',
- 'rarrlp' => '↬',
- 'rarrpl' => '⥅',
- 'rarrsim' => '⥴',
- 'Rarrtl' => '⤖',
- 'rarrtl' => '↣',
- 'rarrw' => '↝',
- 'rAtail' => '⤜',
- 'ratail' => '⤚',
- 'RBarr' => '⤐',
- 'rBarr' => '⤏',
- 'rbarr' => '⤍',
- 'rdca' => '⤷',
- 'rdldhar' => '⥩',
- 'rdsh' => '↳',
- 'rfisht' => '⥽',
- 'rHar' => '⥤',
- 'rhard' => '⇁',
- 'rharu' => '⇀',
- 'rharul' => '⥬',
- 'rlarr' => '⇄',
- 'rlhar' => '⇌',
- 'roarr' => '⇾',
- 'rrarr' => '⇉',
- 'rsh' => '↱',
- 'ruluhar' => '⥨',
- 'searhk' => '⤥',
- 'seArr' => '⇘',
- 'searr' => '↘',
- 'seswar' => '⤩',
- 'simrarr' => '⥲',
- 'slarr' => '←',
- 'srarr' => '→',
- 'swarhk' => '⤦',
- 'swArr' => '⇙',
- 'swarr' => '↙',
- 'swnwar' => '⤪',
- 'Uarr' => '↟',
- 'uArr' => '⇑',
- 'Uarrocir' => '⥉',
- 'udarr' => '⇅',
- 'udhar' => '⥮',
- 'ufisht' => '⥾',
- 'uHar' => '⥣',
- 'uharl' => '↿',
- 'uharr' => '↾',
- 'uuarr' => '⇈',
- 'vArr' => '⇕',
- 'varr' => '↕',
- 'xhArr' => '⟺',
- 'xharr' => '⟷',
- 'xlArr' => '⟸',
- 'xlarr' => '⟵',
- 'xmap' => '⟼',
- 'xrArr' => '⟹',
- 'xrarr' => '⟶',
- 'zigrarr' => '⇝',
- 'ac' => '∾',
- 'acE' => '∾̳',
- 'amalg' => '⨿',
- 'barvee' => '⊽',
- 'Barwed' => '⌆',
- 'barwed' => '⌅',
- 'bsolb' => '⧅',
- 'Cap' => '⋒',
- 'capand' => '⩄',
- 'capbrcup' => '⩉',
- 'capcap' => '⩋',
- 'capcup' => '⩇',
- 'capdot' => '⩀',
- 'caps' => '∩︀',
- 'ccaps' => '⩍',
- 'ccups' => '⩌',
- 'ccupssm' => '⩐',
- 'coprod' => '∐',
- 'Cup' => '⋓',
- 'cupbrcap' => '⩈',
- 'cupcap' => '⩆',
- 'cupcup' => '⩊',
- 'cupdot' => '⊍',
- 'cupor' => '⩅',
- 'cups' => '∪︀',
- 'cuvee' => '⋎',
- 'cuwed' => '⋏',
- 'Dagger' => '‡',
- 'dagger' => '†',
- 'diam' => '⋄',
- 'divonx' => '⋇',
- 'eplus' => '⩱',
- 'hercon' => '⊹',
- 'intcal' => '⊺',
- 'iprod' => '⨼',
- 'loplus' => '⨭',
- 'lotimes' => '⨴',
- 'lthree' => '⋋',
- 'ltimes' => '⋉',
- 'midast' => '*',
- 'minusb' => '⊟',
- 'minusd' => '∸',
- 'minusdu' => '⨪',
- 'ncap' => '⩃',
- 'ncup' => '⩂',
- 'oast' => '⊛',
- 'ocir' => '⊚',
- 'odash' => '⊝',
- 'odiv' => '⨸',
- 'odot' => '⊙',
- 'odsold' => '⦼',
- 'ofcir' => '⦿',
- 'ogt' => '⧁',
- 'ohbar' => '⦵',
- 'olcir' => '⦾',
- 'olt' => '⧀',
- 'omid' => '⦶',
- 'ominus' => '⊖',
- 'opar' => '⦷',
- 'operp' => '⦹',
- 'oplus' => '⊕',
- 'osol' => '⊘',
- 'Otimes' => '⨷',
- 'otimes' => '⊗',
- 'otimesas' => '⨶',
- 'ovbar' => '⌽',
- 'plusacir' => '⨣',
- 'plusb' => '⊞',
- 'pluscir' => '⨢',
- 'plusdo' => '∔',
- 'plusdu' => '⨥',
- 'pluse' => '⩲',
- 'plussim' => '⨦',
- 'plustwo' => '⨧',
- 'prod' => '∏',
- 'race' => '⧚',
- 'roplus' => '⨮',
- 'rotimes' => '⨵',
- 'rthree' => '⋌',
- 'rtimes' => '⋊',
- 'sdot' => '⋅',
- 'sdotb' => '⊡',
- 'setmn' => '∖',
- 'simplus' => '⨤',
- 'smashp' => '⨳',
- 'solb' => '⧄',
- 'sqcap' => '⊓',
- 'sqcaps' => '⊓︀',
- 'sqcup' => '⊔',
- 'sqcups' => '⊔︀',
- 'ssetmn' => '∖',
- 'sstarf' => '⋆',
- 'subdot' => '⪽',
- 'sum' => '∑',
- 'supdot' => '⪾',
- 'timesb' => '⊠',
- 'timesbar' => '⨱',
- 'timesd' => '⨰',
- 'tridot' => '◬',
- 'triminus' => '⨺',
- 'triplus' => '⨹',
- 'trisb' => '⧍',
- 'tritime' => '⨻',
- 'uplus' => '⊎',
- 'veebar' => '⊻',
- 'wedbar' => '⩟',
- 'wreath' => '≀',
- 'xcap' => '⋂',
- 'xcirc' => '◯',
- 'xcup' => '⋃',
- 'xdtri' => '▽',
- 'xodot' => '⨀',
- 'xoplus' => '⨁',
- 'xotime' => '⨂',
- 'xsqcup' => '⨆',
- 'xuplus' => '⨄',
- 'xutri' => '△',
- 'xvee' => '⋁',
- 'xwedge' => '⋀',
- 'dlcorn' => '⌞',
- 'drcorn' => '⌟',
- 'gtlPar' => '⦕',
- 'langd' => '⦑',
- 'lbrke' => '⦋',
- 'lbrksld' => '⦏',
- 'lbrkslu' => '⦍',
- 'lceil' => '⌈',
- 'lfloor' => '⌊',
- 'lmoust' => '⎰',
- 'lparlt' => '⦓',
- 'ltrPar' => '⦖',
- 'rangd' => '⦒',
- 'rbrke' => '⦌',
- 'rbrksld' => '⦎',
- 'rbrkslu' => '⦐',
- 'rceil' => '⌉',
- 'rfloor' => '⌋',
- 'rmoust' => '⎱',
- 'rpargt' => '⦔',
- 'ulcorn' => '⌜',
- 'urcorn' => '⌝',
- 'gnap' => '⪊',
- 'gnE' => '≩',
- 'gne' => '⪈',
- 'gnsim' => '⋧',
- 'gvnE' => '≩︀',
- 'lnap' => '⪉',
- 'lnE' => '≨',
- 'lne' => '⪇',
- 'lnsim' => '⋦',
- 'lvnE' => '≨︀',
- 'nap' => '≉',
- 'napE' => '⩰̸',
- 'napid' => '≋̸',
- 'ncong' => '≇',
- 'ncongdot' => '⩭̸',
- 'nequiv' => '≢',
- 'ngE' => '≧̸',
- 'nge' => '≱',
- 'nges' => '⩾̸',
- 'nGg' => '⋙̸',
- 'ngsim' => '≵',
- 'nGt' => '≫⃒',
- 'ngt' => '≯',
- 'nGtv' => '≫̸',
- 'nlE' => '≦̸',
- 'nle' => '≰',
- 'nles' => '⩽̸',
- 'nLl' => '⋘̸',
- 'nlsim' => '≴',
- 'nLt' => '≪⃒',
- 'nlt' => '≮',
- 'nltri' => '⋪',
- 'nltrie' => '⋬',
- 'nLtv' => '≪̸',
- 'nmid' => '∤',
- 'npar' => '∦',
- 'npr' => '⊀',
- 'nprcue' => '⋠',
- 'npre' => '⪯̸',
- 'nrtri' => '⋫',
- 'nrtrie' => '⋭',
- 'nsc' => '⊁',
- 'nsccue' => '⋡',
- 'nsce' => '⪰̸',
- 'nsim' => '≁',
- 'nsime' => '≄',
- 'nsmid' => '∤',
- 'nspar' => '∦',
- 'nsqsube' => '⋢',
- 'nsqsupe' => '⋣',
- 'nsub' => '⊄',
- 'nsubE' => '⫅̸',
- 'nsube' => '⊈',
- 'nsup' => '⊅',
- 'nsupE' => '⫆̸',
- 'nsupe' => '⊉',
- 'ntgl' => '≹',
- 'ntlg' => '≸',
- 'nvap' => '≍⃒',
- 'nVDash' => '⊯',
- 'nVdash' => '⊮',
- 'nvDash' => '⊭',
- 'nvdash' => '⊬',
- 'nvge' => '≥⃒',
- 'nvgt' => '>⃒',
- 'nvle' => '≤⃒',
- 'nvltrie' => '⊴⃒',
- 'nvrtrie' => '⊵⃒',
- 'nvsim' => '∼⃒',
- 'parsim' => '⫳',
- 'prnap' => '⪹',
- 'prnE' => '⪵',
- 'prnsim' => '⋨',
- 'rnmid' => '⫮',
- 'scnap' => '⪺',
- 'scnE' => '⪶',
- 'scnsim' => '⋩',
- 'simne' => '≆',
- 'solbar' => '⌿',
- 'subnE' => '⫋',
- 'subne' => '⊊',
- 'supnE' => '⫌',
- 'supne' => '⊋',
- 'vnsub' => '⊂⃒',
- 'vnsup' => '⊃⃒',
- 'vsubnE' => '⫋︀',
- 'vsubne' => '⊊︀',
- 'vsupnE' => '⫌︀',
- 'vsupne' => '⊋︀',
- 'ang' => '∠',
- 'ange' => '⦤',
- 'angmsd' => '∡',
- 'angmsdaa' => '⦨',
- 'angmsdab' => '⦩',
- 'angmsdac' => '⦪',
- 'angmsdad' => '⦫',
- 'angmsdae' => '⦬',
- 'angmsdaf' => '⦭',
- 'angmsdag' => '⦮',
- 'angmsdah' => '⦯',
- 'angrtvb' => '⊾',
- 'angrtvbd' => '⦝',
- 'bbrk' => '⎵',
- 'bbrktbrk' => '⎶',
- 'bemptyv' => '⦰',
- 'beth' => 'ℶ',
- 'boxbox' => '⧉',
- 'bprime' => '‵',
- 'bsemi' => '⁏',
- 'cemptyv' => '⦲',
- 'cirE' => '⧃',
- 'cirscir' => '⧂',
- 'comp' => '∁',
- 'daleth' => 'ℸ',
- 'demptyv' => '⦱',
- 'ell' => 'ℓ',
- 'empty' => '∅',
- 'emptyv' => '∅',
- 'gimel' => 'ℷ',
- 'iiota' => '℩',
- 'image' => 'ℑ',
- 'imath' => 'ı',
- 'jmath' => 'j',
- 'laemptyv' => '⦴',
- 'lltri' => '◺',
- 'lrtri' => '⊿',
- 'mho' => '℧',
- 'nang' => '∠⃒',
- 'nexist' => '∄',
- 'oS' => 'Ⓢ',
- 'planck' => 'ℏ',
- 'plankv' => 'ℏ',
- 'raemptyv' => '⦳',
- 'range' => '⦥',
- 'real' => 'ℜ',
- 'tbrk' => '⎴',
- 'trpezium' => '�',
- 'ultri' => '◸',
- 'urtri' => '◹',
- 'vzigzag' => '⦚',
- 'weierp' => '℘',
- 'apE' => '⩰',
- 'ape' => '≊',
- 'apid' => '≋',
- 'asymp' => '≈',
- 'Barv' => '⫧',
- 'bcong' => '≌',
- 'bepsi' => '϶',
- 'bowtie' => '⋈',
- 'bsim' => '∽',
- 'bsime' => '⋍',
- 'bsolhsub' => '\⊂',
- 'bump' => '≎',
- 'bumpE' => '⪮',
- 'bumpe' => '≏',
- 'cire' => '≗',
- 'Colon' => '∷',
- 'Colone' => '⩴',
- 'colone' => '≔',
- 'congdot' => '⩭',
- 'csub' => '⫏',
- 'csube' => '⫑',
- 'csup' => '⫐',
- 'csupe' => '⫒',
- 'cuepr' => '⋞',
- 'cuesc' => '⋟',
- 'Dashv' => '⫤',
- 'dashv' => '⊣',
- 'easter' => '⩮',
- 'ecir' => '≖',
- 'ecolon' => '≕',
- 'eDDot' => '⩷',
- 'eDot' => '≑',
- 'efDot' => '≒',
- 'eg' => '⪚',
- 'egs' => '⪖',
- 'egsdot' => '⪘',
- 'el' => '⪙',
- 'els' => '⪕',
- 'elsdot' => '⪗',
- 'equest' => '≟',
- 'equivDD' => '⩸',
- 'erDot' => '≓',
- 'esdot' => '≐',
- 'Esim' => '⩳',
- 'esim' => '≂',
- 'fork' => '⋔',
- 'forkv' => '⫙',
- 'frown' => '⌢',
- 'gap' => '⪆',
- 'gE' => '≧',
- 'gEl' => '⪌',
- 'gel' => '⋛',
- 'ges' => '⩾',
- 'gescc' => '⪩',
- 'gesdot' => '⪀',
- 'gesdoto' => '⪂',
- 'gesdotol' => '⪄',
- 'gesl' => '⋛︀',
- 'gesles' => '⪔',
- 'Gg' => '⋙',
- 'gl' => '≷',
- 'gla' => '⪥',
- 'glE' => '⪒',
- 'glj' => '⪤',
- 'gsim' => '≳',
- 'gsime' => '⪎',
- 'gsiml' => '⪐',
- 'Gt' => '≫',
- 'gtcc' => '⪧',
- 'gtcir' => '⩺',
- 'gtdot' => '⋗',
- 'gtquest' => '⩼',
- 'gtrarr' => '⥸',
- 'homtht' => '∻',
- 'lap' => '⪅',
- 'lat' => '⪫',
- 'late' => '⪭',
- 'lates' => '⪭︀',
- 'lE' => '≦',
- 'lEg' => '⪋',
- 'leg' => '⋚',
- 'les' => '⩽',
- 'lescc' => '⪨',
- 'lesdot' => '⩿',
- 'lesdoto' => '⪁',
- 'lesdotor' => '⪃',
- 'lesg' => '⋚︀',
- 'lesges' => '⪓',
- 'lg' => '≶',
- 'lgE' => '⪑',
- 'Ll' => '⋘',
- 'lsim' => '≲',
- 'lsime' => '⪍',
- 'lsimg' => '⪏',
- 'Lt' => '≪',
- 'ltcc' => '⪦',
- 'ltcir' => '⩹',
- 'ltdot' => '⋖',
- 'ltlarr' => '⥶',
- 'ltquest' => '⩻',
- 'ltrie' => '⊴',
- 'mcomma' => '⨩',
- 'mDDot' => '∺',
- 'mid' => '∣',
- 'mlcp' => '⫛',
- 'models' => '⊧',
- 'mstpos' => '∾',
- 'Pr' => '⪻',
- 'pr' => '≺',
- 'prap' => '⪷',
- 'prcue' => '≼',
- 'prE' => '⪳',
- 'pre' => '⪯',
- 'prsim' => '≾',
- 'prurel' => '⊰',
- 'ratio' => '∶',
- 'rtrie' => '⊵',
- 'rtriltri' => '⧎',
- 'Sc' => '⪼',
- 'sc' => '≻',
- 'scap' => '⪸',
- 'sccue' => '≽',
- 'scE' => '⪴',
- 'sce' => '⪰',
- 'scsim' => '≿',
- 'sdote' => '⩦',
- 'sfrown' => '⌢',
- 'simg' => '⪞',
- 'simgE' => '⪠',
- 'siml' => '⪝',
- 'simlE' => '⪟',
- 'smid' => '∣',
- 'smile' => '⌣',
- 'smt' => '⪪',
- 'smte' => '⪬',
- 'smtes' => '⪬︀',
- 'spar' => '∥',
- 'sqsub' => '⊏',
- 'sqsube' => '⊑',
- 'sqsup' => '⊐',
- 'sqsupe' => '⊒',
- 'ssmile' => '⌣',
- 'Sub' => '⋐',
- 'subE' => '⫅',
- 'subedot' => '⫃',
- 'submult' => '⫁',
- 'subplus' => '⪿',
- 'subrarr' => '⥹',
- 'subsim' => '⫇',
- 'subsub' => '⫕',
- 'subsup' => '⫓',
- 'Sup' => '⋑',
- 'supdsub' => '⫘',
- 'supE' => '⫆',
- 'supedot' => '⫄',
- 'suphsol' => '⊃/',
- 'suphsub' => '⫗',
- 'suplarr' => '⥻',
- 'supmult' => '⫂',
- 'supplus' => '⫀',
- 'supsim' => '⫈',
- 'supsub' => '⫔',
- 'supsup' => '⫖',
- 'thkap' => '≈',
- 'thksim' => '∼',
- 'topfork' => '⫚',
- 'trie' => '≜',
- 'twixt' => '≬',
- 'Vbar' => '⫫',
- 'vBar' => '⫨',
- 'vBarv' => '⫩',
- 'VDash' => '⊫',
- 'Vdash' => '⊩',
- 'vDash' => '⊨',
- 'vdash' => '⊢',
- 'Vdashl' => '⫦',
- 'vltri' => '⊲',
- 'vprop' => '∝',
- 'vrtri' => '⊳',
- 'Vvdash' => '⊪',
- 'alpha' => 'α',
- 'beta' => 'β',
- 'chi' => 'χ',
- 'Delta' => 'Δ',
- 'delta' => 'δ',
- 'epsi' => 'ϵ',
- 'epsiv' => 'ε',
- 'eta' => 'η',
- 'Gamma' => 'Γ',
- 'gamma' => 'γ',
- 'Gammad' => 'Ϝ',
- 'gammad' => 'ϝ',
- 'iota' => 'ι',
- 'kappa' => 'κ',
- 'kappav' => 'ϰ',
- 'Lambda' => 'Λ',
- 'lambda' => 'λ',
- 'mu' => 'μ',
- 'nu' => 'ν',
- 'Omega' => 'Ω',
- 'omega' => 'ω',
- 'Phi' => 'Φ',
- 'phi' => 'ϕ',
- 'phiv' => 'φ',
- 'Pi' => 'Π',
- 'pi' => 'π',
- 'piv' => 'ϖ',
- 'Psi' => 'Ψ',
- 'psi' => 'ψ',
- 'rho' => 'ρ',
- 'rhov' => 'ϱ',
- 'Sigma' => 'Σ',
- 'sigma' => 'σ',
- 'sigmav' => 'ς',
- 'tau' => 'τ',
- 'Theta' => 'Θ',
- 'theta' => 'θ',
- 'thetav' => 'ϑ',
- 'Upsi' => 'ϒ',
- 'upsi' => 'υ',
- 'Xi' => 'Ξ',
- 'xi' => 'ξ',
- 'zeta' => 'ζ',
- 'Afr' => '𝔄',
- 'afr' => '𝔞',
- 'Bfr' => '𝔅',
- 'bfr' => '𝔟',
- 'Cfr' => 'ℭ',
- 'cfr' => '𝔠',
- 'Dfr' => '𝔇',
- 'dfr' => '𝔡',
- 'Efr' => '𝔈',
- 'efr' => '𝔢',
- 'Ffr' => '𝔉',
- 'ffr' => '𝔣',
- 'Gfr' => '𝔊',
- 'gfr' => '𝔤',
- 'Hfr' => 'ℌ',
- 'hfr' => '𝔥',
- 'Ifr' => 'ℑ',
- 'ifr' => '𝔦',
- 'Jfr' => '𝔍',
- 'jfr' => '𝔧',
- 'Kfr' => '𝔎',
- 'kfr' => '𝔨',
- 'Lfr' => '𝔏',
- 'lfr' => '𝔩',
- 'Mfr' => '𝔐',
- 'mfr' => '𝔪',
- 'Nfr' => '𝔑',
- 'nfr' => '𝔫',
- 'Ofr' => '𝔒',
- 'ofr' => '𝔬',
- 'Pfr' => '𝔓',
- 'pfr' => '𝔭',
- 'Qfr' => '𝔔',
- 'qfr' => '𝔮',
- 'Rfr' => 'ℜ',
- 'rfr' => '𝔯',
- 'Sfr' => '𝔖',
- 'sfr' => '𝔰',
- 'Tfr' => '𝔗',
- 'tfr' => '𝔱',
- 'Ufr' => '𝔘',
- 'ufr' => '𝔲',
- 'Vfr' => '𝔙',
- 'vfr' => '𝔳',
- 'Wfr' => '𝔚',
- 'wfr' => '𝔴',
- 'Xfr' => '𝔛',
- 'xfr' => '𝔵',
- 'Yfr' => '𝔜',
- 'yfr' => '𝔶',
- 'Zfr' => 'ℨ',
- 'zfr' => '𝔷',
- 'Aopf' => '𝔸',
- 'Bopf' => '𝔹',
- 'Copf' => 'ℂ',
- 'Dopf' => '𝔻',
- 'Eopf' => '𝔼',
- 'Fopf' => '𝔽',
- 'Gopf' => '𝔾',
- 'Hopf' => 'ℍ',
- 'Iopf' => '𝕀',
- 'Jopf' => '𝕁',
- 'Kopf' => '𝕂',
- 'Lopf' => '𝕃',
- 'Mopf' => '𝕄',
- 'Nopf' => 'ℕ',
- 'Oopf' => '𝕆',
- 'Popf' => 'ℙ',
- 'Qopf' => 'ℚ',
- 'Ropf' => 'ℝ',
- 'Sopf' => '𝕊',
- 'Topf' => '𝕋',
- 'Uopf' => '𝕌',
- 'Vopf' => '𝕍',
- 'Wopf' => '𝕎',
- 'Xopf' => '𝕏',
- 'Yopf' => '𝕐',
- 'Zopf' => 'ℤ',
- 'Ascr' => '𝒜',
- 'ascr' => '𝒶',
- 'Bscr' => 'ℬ',
- 'bscr' => '𝒷',
- 'Cscr' => '𝒞',
- 'cscr' => '𝒸',
- 'Dscr' => '𝒟',
- 'dscr' => '𝒹',
- 'Escr' => 'ℰ',
- 'escr' => 'ℯ',
- 'Fscr' => 'ℱ',
- 'fscr' => '𝒻',
- 'Gscr' => '𝒢',
- 'gscr' => 'ℊ',
- 'Hscr' => 'ℋ',
- 'hscr' => '𝒽',
- 'Iscr' => 'ℐ',
- 'iscr' => '𝒾',
- 'Jscr' => '𝒥',
- 'jscr' => '𝒿',
- 'Kscr' => '𝒦',
- 'kscr' => '𝓀',
- 'Lscr' => 'ℒ',
- 'lscr' => '𝓁',
- 'Mscr' => 'ℳ',
- 'mscr' => '𝓂',
- 'Nscr' => '𝒩',
- 'nscr' => '𝓃',
- 'Oscr' => '𝒪',
- 'oscr' => 'ℴ',
- 'Pscr' => '𝒫',
- 'pscr' => '𝓅',
- 'Qscr' => '𝒬',
- 'qscr' => '𝓆',
- 'Rscr' => 'ℛ',
- 'rscr' => '𝓇',
- 'Sscr' => '𝒮',
- 'sscr' => '𝓈',
- 'Tscr' => '𝒯',
- 'tscr' => '𝓉',
- 'Uscr' => '𝒰',
- 'uscr' => '𝓊',
- 'Vscr' => '𝒱',
- 'vscr' => '𝓋',
- 'Wscr' => '𝒲',
- 'wscr' => '𝓌',
- 'Xscr' => '𝒳',
- 'xscr' => '𝓍',
- 'Yscr' => '𝒴',
- 'yscr' => '𝓎',
- 'Zscr' => '𝒵',
- 'zscr' => '𝓏',
- 'acd' => '∿',
- 'aleph' => 'ℵ',
- 'And' => '⩓',
- 'and' => '∧',
- 'andand' => '⩕',
- 'andd' => '⩜',
- 'andslope' => '⩘',
- 'andv' => '⩚',
- 'angrt' => '∟',
- 'angsph' => '∢',
- 'angst' => 'Å',
- 'ap' => '≈',
- 'apacir' => '⩯',
- 'awconint' => '∳',
- 'awint' => '⨑',
- 'becaus' => '∵',
- 'bernou' => 'ℬ',
- 'bne' => '=⃥',
- 'bnequiv' => '≡⃥',
- 'bNot' => '⫭',
- 'bnot' => '⌐',
- 'bottom' => '⊥',
- 'cap' => '∩',
- 'Cconint' => '∰',
- 'cirfnint' => '⨐',
- 'compfn' => '∘',
- 'cong' => '≅',
- 'Conint' => '∯',
- 'conint' => '∮',
- 'ctdot' => '⋯',
- 'cup' => '∪',
- 'cwconint' => '∲',
- 'cwint' => '∱',
- 'cylcty' => '⌭',
- 'disin' => '⋲',
- 'Dot' => '¨',
- 'DotDot' => '⃜',
- 'dsol' => '⧶',
- 'dtdot' => '⋱',
- 'dwangle' => '⦦',
- 'elinters' => '�',
- 'epar' => '⋕',
- 'eparsl' => '⧣',
- 'equiv' => '≡',
- 'eqvparsl' => '⧥',
- 'exist' => '∃',
- 'fltns' => '▱',
- 'fnof' => 'ƒ',
- 'forall' => '∀',
- 'fpartint' => '⨍',
- 'ge' => '≥',
- 'hamilt' => 'ℋ',
- 'iff' => '⇔',
- 'iinfin' => '⧜',
- 'imped' => 'Ƶ',
- 'infin' => '∞',
- 'infintie' => '⧝',
- 'Int' => '∬',
- 'int' => '∫',
- 'intlarhk' => '⨗',
- 'isin' => '∈',
- 'isindot' => '⋵',
- 'isinE' => '⋹',
- 'isins' => '⋴',
- 'isinsv' => '⋳',
- 'isinv' => '∈',
- 'lagran' => 'ℒ',
- 'Lang' => '《',
- 'lang' => '〈',
- 'lArr' => '⇐',
- 'lbbrk' => '〔',
- 'le' => '≤',
- 'loang' => '〘',
- 'lobrk' => '〚',
- 'lopar' => '⦅',
- 'lowast' => '∗',
- 'minus' => '−',
- 'mnplus' => '∓',
- 'nabla' => '∇',
- 'ne' => '≠',
- 'nedot' => '≐̸',
- 'nhpar' => '⫲',
- 'ni' => '∋',
- 'nis' => '⋼',
- 'nisd' => '⋺',
- 'niv' => '∋',
- 'Not' => '⫬',
- 'notin' => '∉',
- 'notindot' => '⋵̸',
- 'notinE' => '⋹̸',
- 'notinva' => '∉',
- 'notinvb' => '⋷',
- 'notinvc' => '⋶',
- 'notni' => '∌',
- 'notniva' => '∌',
- 'notnivb' => '⋾',
- 'notnivc' => '⋽',
- 'nparsl' => '⫽⃥',
- 'npart' => '∂̸',
- 'npolint' => '⨔',
- 'nvinfin' => '⧞',
- 'olcross' => '⦻',
- 'Or' => '⩔',
- 'or' => '∨',
- 'ord' => '⩝',
- 'order' => 'ℴ',
- 'oror' => '⩖',
- 'orslope' => '⩗',
- 'orv' => '⩛',
- 'par' => '∥',
- 'parsl' => '⫽',
- 'part' => '∂',
- 'permil' => '‰',
- 'perp' => '⊥',
- 'pertenk' => '‱',
- 'phmmat' => 'ℳ',
- 'pointint' => '⨕',
- 'Prime' => '″',
- 'prime' => '′',
- 'profalar' => '⌮',
- 'profline' => '⌒',
- 'profsurf' => '⌓',
- 'prop' => '∝',
- 'qint' => '⨌',
- 'qprime' => '⁗',
- 'quatint' => '⨖',
- 'radic' => '√',
- 'Rang' => '》',
- 'rang' => '〉',
- 'rArr' => '⇒',
- 'rbbrk' => '〕',
- 'roang' => '〙',
- 'robrk' => '〛',
- 'ropar' => '⦆',
- 'rppolint' => '⨒',
- 'scpolint' => '⨓',
- 'sim' => '∼',
- 'simdot' => '⩪',
- 'sime' => '≃',
- 'smeparsl' => '⧤',
- 'square' => '□',
- 'squarf' => '▪',
- 'strns' => '¯',
- 'sub' => '⊂',
- 'sube' => '⊆',
- 'sup' => '⊃',
- 'supe' => '⊇',
- 'tdot' => '⃛',
- 'there4' => '∴',
- 'tint' => '∭',
- 'top' => '⊤',
- 'topbot' => '⌶',
- 'topcir' => '⫱',
- 'tprime' => '‴',
- 'utdot' => '⋰',
- 'uwangle' => '⦧',
- 'vangrt' => '⦜',
- 'veeeq' => '≚',
- 'Verbar' => '‖',
- 'wedgeq' => '≙',
- 'xnis' => '⋻',
- 'boxDL' => '╗',
- 'boxDl' => '╖',
- 'boxdL' => '╕',
- 'boxdl' => '┐',
- 'boxDR' => '╔',
- 'boxDr' => '╓',
- 'boxdR' => '╒',
- 'boxdr' => '┌',
- 'boxH' => '═',
- 'boxh' => '─',
- 'boxHD' => '╦',
- 'boxHd' => '╤',
- 'boxhD' => '╥',
- 'boxhd' => '┬',
- 'boxHU' => '╩',
- 'boxHu' => '╧',
- 'boxhU' => '╨',
- 'boxhu' => '┴',
- 'boxUL' => '╝',
- 'boxUl' => '╜',
- 'boxuL' => '╛',
- 'boxul' => '┘',
- 'boxUR' => '╚',
- 'boxUr' => '╙',
- 'boxuR' => '╘',
- 'boxur' => '└',
- 'boxV' => '║',
- 'boxv' => '│',
- 'boxVH' => '╬',
- 'boxVh' => '╫',
- 'boxvH' => '╪',
- 'boxvh' => '┼',
- 'boxVL' => '╣',
- 'boxVl' => '╢',
- 'boxvL' => '╡',
- 'boxvl' => '┤',
- 'boxVR' => '╠',
- 'boxVr' => '╟',
- 'boxvR' => '╞',
- 'boxvr' => '├',
- 'Acy' => 'А',
- 'acy' => 'а',
- 'Bcy' => 'Б',
- 'bcy' => 'б',
- 'CHcy' => 'Ч',
- 'chcy' => 'ч',
- 'Dcy' => 'Д',
- 'dcy' => 'д',
- 'Ecy' => 'Э',
- 'ecy' => 'э',
- 'Fcy' => 'Ф',
- 'fcy' => 'ф',
- 'Gcy' => 'Г',
- 'gcy' => 'г',
- 'HARDcy' => 'Ъ',
- 'hardcy' => 'ъ',
- 'Icy' => 'И',
- 'icy' => 'и',
- 'IEcy' => 'Е',
- 'iecy' => 'е',
- 'IOcy' => 'Ё',
- 'iocy' => 'ё',
- 'Jcy' => 'Й',
- 'jcy' => 'й',
- 'Kcy' => 'К',
- 'kcy' => 'к',
- 'KHcy' => 'Х',
- 'khcy' => 'х',
- 'Lcy' => 'Л',
- 'lcy' => 'л',
- 'Mcy' => 'М',
- 'mcy' => 'м',
- 'Ncy' => 'Н',
- 'ncy' => 'н',
- 'numero' => '№',
- 'Ocy' => 'О',
- 'ocy' => 'о',
- 'Pcy' => 'П',
- 'pcy' => 'п',
- 'Rcy' => 'Р',
- 'rcy' => 'р',
- 'Scy' => 'С',
- 'scy' => 'с',
- 'SHCHcy' => 'Щ',
- 'shchcy' => 'щ',
- 'SHcy' => 'Ш',
- 'shcy' => 'ш',
- 'SOFTcy' => 'Ь',
- 'softcy' => 'ь',
- 'Tcy' => 'Т',
- 'tcy' => 'т',
- 'TScy' => 'Ц',
- 'tscy' => 'ц',
- 'Ucy' => 'У',
- 'ucy' => 'у',
- 'Vcy' => 'В',
- 'vcy' => 'в',
- 'YAcy' => 'Я',
- 'yacy' => 'я',
- 'Ycy' => 'Ы',
- 'ycy' => 'ы',
- 'YUcy' => 'Ю',
- 'yucy' => 'ю',
- 'Zcy' => 'З',
- 'zcy' => 'з',
- 'ZHcy' => 'Ж',
- 'zhcy' => 'ж',
- 'DJcy' => 'Ђ',
- 'djcy' => 'ђ',
- 'DScy' => 'Ѕ',
- 'dscy' => 'ѕ',
- 'DZcy' => 'Џ',
- 'dzcy' => 'џ',
- 'GJcy' => 'Ѓ',
- 'gjcy' => 'ѓ',
- 'Iukcy' => 'І',
- 'iukcy' => 'і',
- 'Jsercy' => 'Ј',
- 'jsercy' => 'ј',
- 'Jukcy' => 'Є',
- 'jukcy' => 'є',
- 'KJcy' => 'Ќ',
- 'kjcy' => 'ќ',
- 'LJcy' => 'Љ',
- 'ljcy' => 'љ',
- 'NJcy' => 'Њ',
- 'njcy' => 'њ',
- 'TSHcy' => 'Ћ',
- 'tshcy' => 'ћ',
- 'Ubrcy' => 'Ў',
- 'ubrcy' => 'ў',
- 'YIcy' => 'Ї',
- 'yicy' => 'ї',
- 'acute' => '´',
- 'breve' => '˘',
- 'caron' => 'ˇ',
- 'cedil' => '¸',
- 'circ' => 'ˆ',
- 'dblac' => '˝',
- 'die' => '¨',
- 'dot' => '˙',
- 'grave' => '`',
- 'macr' => '¯',
- 'ogon' => '˛',
- 'ring' => '˚',
- 'tilde' => '˜',
- 'uml' => '¨',
- 'Aacute' => 'Á',
- 'aacute' => 'á',
- 'Acirc' => 'Â',
- 'acirc' => 'â',
- 'AElig' => 'Æ',
- 'aelig' => 'æ',
- 'Agrave' => 'À',
- 'agrave' => 'à',
- 'Aring' => 'Å',
- 'aring' => 'å',
- 'Atilde' => 'Ã',
- 'atilde' => 'ã',
- 'Auml' => 'Ä',
- 'auml' => 'ä',
- 'Ccedil' => 'Ç',
- 'ccedil' => 'ç',
- 'Eacute' => 'É',
- 'eacute' => 'é',
- 'Ecirc' => 'Ê',
- 'ecirc' => 'ê',
- 'Egrave' => 'È',
- 'egrave' => 'è',
- 'ETH' => 'Ð',
- 'eth' => 'ð',
- 'Euml' => 'Ë',
- 'euml' => 'ë',
- 'Iacute' => 'Í',
- 'iacute' => 'í',
- 'Icirc' => 'Î',
- 'icirc' => 'î',
- 'Igrave' => 'Ì',
- 'igrave' => 'ì',
- 'Iuml' => 'Ï',
- 'iuml' => 'ï',
- 'Ntilde' => 'Ñ',
- 'ntilde' => 'ñ',
- 'Oacute' => 'Ó',
- 'oacute' => 'ó',
- 'Ocirc' => 'Ô',
- 'ocirc' => 'ô',
- 'Ograve' => 'Ò',
- 'ograve' => 'ò',
- 'Oslash' => 'Ø',
- 'oslash' => 'ø',
- 'Otilde' => 'Õ',
- 'otilde' => 'õ',
- 'Ouml' => 'Ö',
- 'ouml' => 'ö',
- 'szlig' => 'ß',
- 'THORN' => 'Þ',
- 'thorn' => 'þ',
- 'Uacute' => 'Ú',
- 'uacute' => 'ú',
- 'Ucirc' => 'Û',
- 'ucirc' => 'û',
- 'Ugrave' => 'Ù',
- 'ugrave' => 'ù',
- 'Uuml' => 'Ü',
- 'uuml' => 'ü',
- 'Yacute' => 'Ý',
- 'yacute' => 'ý',
- 'yuml' => 'ÿ',
- 'Abreve' => 'Ă',
- 'abreve' => 'ă',
- 'Amacr' => 'Ā',
- 'amacr' => 'ā',
- 'Aogon' => 'Ą',
- 'aogon' => 'ą',
- 'Cacute' => 'Ć',
- 'cacute' => 'ć',
- 'Ccaron' => 'Č',
- 'ccaron' => 'č',
- 'Ccirc' => 'Ĉ',
- 'ccirc' => 'ĉ',
- 'Cdot' => 'Ċ',
- 'cdot' => 'ċ',
- 'Dcaron' => 'Ď',
- 'dcaron' => 'ď',
- 'Dstrok' => 'Đ',
- 'dstrok' => 'đ',
- 'Ecaron' => 'Ě',
- 'ecaron' => 'ě',
- 'Edot' => 'Ė',
- 'edot' => 'ė',
- 'Emacr' => 'Ē',
- 'emacr' => 'ē',
- 'ENG' => 'Ŋ',
- 'eng' => 'ŋ',
- 'Eogon' => 'Ę',
- 'eogon' => 'ę',
- 'gacute' => 'ǵ',
- 'Gbreve' => 'Ğ',
- 'gbreve' => 'ğ',
- 'Gcedil' => 'Ģ',
- 'Gcirc' => 'Ĝ',
- 'gcirc' => 'ĝ',
- 'Gdot' => 'Ġ',
- 'gdot' => 'ġ',
- 'Hcirc' => 'Ĥ',
- 'hcirc' => 'ĥ',
- 'Hstrok' => 'Ħ',
- 'hstrok' => 'ħ',
- 'Idot' => 'İ',
- 'IJlig' => 'IJ',
- 'ijlig' => 'ij',
- 'Imacr' => 'Ī',
- 'imacr' => 'ī',
- 'inodot' => 'ı',
- 'Iogon' => 'Į',
- 'iogon' => 'į',
- 'Itilde' => 'Ĩ',
- 'itilde' => 'ĩ',
- 'Jcirc' => 'Ĵ',
- 'jcirc' => 'ĵ',
- 'Kcedil' => 'Ķ',
- 'kcedil' => 'ķ',
- 'kgreen' => 'ĸ',
- 'Lacute' => 'Ĺ',
- 'lacute' => 'ĺ',
- 'Lcaron' => 'Ľ',
- 'lcaron' => 'ľ',
- 'Lcedil' => 'Ļ',
- 'lcedil' => 'ļ',
- 'Lmidot' => 'Ŀ',
- 'lmidot' => 'ŀ',
- 'Lstrok' => 'Ł',
- 'lstrok' => 'ł',
- 'Nacute' => 'Ń',
- 'nacute' => 'ń',
- 'napos' => 'ʼn',
- 'Ncaron' => 'Ň',
- 'ncaron' => 'ň',
- 'Ncedil' => 'Ņ',
- 'ncedil' => 'ņ',
- 'Odblac' => 'Ő',
- 'odblac' => 'ő',
- 'OElig' => 'Œ',
- 'oelig' => 'œ',
- 'Omacr' => 'Ō',
- 'omacr' => 'ō',
- 'Racute' => 'Ŕ',
- 'racute' => 'ŕ',
- 'Rcaron' => 'Ř',
- 'rcaron' => 'ř',
- 'Rcedil' => 'Ŗ',
- 'rcedil' => 'ŗ',
- 'Sacute' => 'Ś',
- 'sacute' => 'ś',
- 'Scaron' => 'Š',
- 'scaron' => 'š',
- 'Scedil' => 'Ş',
- 'scedil' => 'ş',
- 'Scirc' => 'Ŝ',
- 'scirc' => 'ŝ',
- 'Tcaron' => 'Ť',
- 'tcaron' => 'ť',
- 'Tcedil' => 'Ţ',
- 'tcedil' => 'ţ',
- 'Tstrok' => 'Ŧ',
- 'tstrok' => 'ŧ',
- 'Ubreve' => 'Ŭ',
- 'ubreve' => 'ŭ',
- 'Udblac' => 'Ű',
- 'udblac' => 'ű',
- 'Umacr' => 'Ū',
- 'umacr' => 'ū',
- 'Uogon' => 'Ų',
- 'uogon' => 'ų',
- 'Uring' => 'Ů',
- 'uring' => 'ů',
- 'Utilde' => 'Ũ',
- 'utilde' => 'ũ',
- 'Wcirc' => 'Ŵ',
- 'wcirc' => 'ŵ',
- 'Ycirc' => 'Ŷ',
- 'ycirc' => 'ŷ',
- 'Yuml' => 'Ÿ',
- 'Zacute' => 'Ź',
- 'zacute' => 'ź',
- 'Zcaron' => 'Ž',
- 'zcaron' => 'ž',
- 'Zdot' => 'Ż',
- 'zdot' => 'ż',
- 'apos' => ''',
- 'ast' => '*',
- 'brvbar' => '¦',
- 'bsol' => '\',
- 'cent' => '¢',
- 'colon' => ':',
- 'comma' => ',',
- 'commat' => '@',
- 'copy' => '©',
- 'curren' => '¤',
- 'darr' => '↓',
- 'deg' => '°',
- 'divide' => '÷',
- 'dollar' => '$',
- 'equals' => '=',
- 'excl' => '!',
- 'frac12' => '½',
- 'frac14' => '¼',
- 'frac18' => '⅛',
- 'frac34' => '¾',
- 'frac38' => '⅜',
- 'frac58' => '⅝',
- 'frac78' => '⅞',
- 'gt' => '>',
- 'half' => '½',
- 'horbar' => '―',
- 'hyphen' => '‐',
- 'iexcl' => '¡',
- 'iquest' => '¿',
- 'laquo' => '«',
- 'larr' => '←',
- 'lcub' => '{',
- 'ldquo' => '“',
- 'lowbar' => '_',
- 'lpar' => '(',
- 'lsqb' => '[',
- 'lsquo' => '‘',
- 'micro' => 'µ',
- 'middot' => '·',
- 'nbsp' => ' ',
- 'not' => '¬',
- 'num' => '#',
- 'ohm' => 'Ω',
- 'ordf' => 'ª',
- 'ordm' => 'º',
- 'para' => '¶',
- 'percnt' => '%',
- 'period' => '.',
- 'plus' => '+',
- 'plusmn' => '±',
- 'pound' => '£',
- 'quest' => '?',
- 'quot' => '"',
- 'raquo' => '»',
- 'rarr' => '→',
- 'rcub' => '}',
- 'rdquo' => '”',
- 'reg' => '®',
- 'rpar' => ')',
- 'rsqb' => ']',
- 'rsquo' => '’',
- 'sect' => '§',
- 'semi' => ';',
- 'shy' => '',
- 'sol' => '/',
- 'sung' => '♪',
- 'sup1' => '¹',
- 'sup2' => '²',
- 'sup3' => '³',
- 'times' => '×',
- 'trade' => '™',
- 'uarr' => '↑',
- 'verbar' => '|',
- 'yen' => '¥',
- 'blank' => '␣',
- 'blk12' => '▒',
- 'blk14' => '░',
- 'blk34' => '▓',
- 'block' => '█',
- 'bull' => '•',
- 'caret' => '⁁',
- 'check' => '✓',
- 'cir' => '○',
- 'clubs' => '♣',
- 'copysr' => '℗',
- 'cross' => '✗',
- 'Dagger' => '‡',
- 'dagger' => '†',
- 'dash' => '‐',
- 'diams' => '♦',
- 'dlcrop' => '⌍',
- 'drcrop' => '⌌',
- 'dtri' => '▿',
- 'dtrif' => '▾',
- 'emsp' => ' ',
- 'emsp13' => ' ',
- 'emsp14' => ' ',
- 'ensp' => ' ',
- 'female' => '♀',
- 'ffilig' => 'ffi',
- 'fflig' => 'ff',
- 'ffllig' => 'ffl',
- 'filig' => 'fi',
- 'flat' => '♭',
- 'fllig' => 'fl',
- 'frac13' => '⅓',
- 'frac15' => '⅕',
- 'frac16' => '⅙',
- 'frac23' => '⅔',
- 'frac25' => '⅖',
- 'frac35' => '⅗',
- 'frac45' => '⅘',
- 'frac56' => '⅚',
- 'hairsp' => ' ',
- 'hearts' => '♥',
- 'hellip' => '…',
- 'hybull' => '⁃',
- 'incare' => '℅',
- 'ldquor' => '„',
- 'lhblk' => '▄',
- 'loz' => '◊',
- 'lozf' => '⧫',
- 'lsquor' => '‚',
- 'ltri' => '◃',
- 'ltrif' => '◂',
- 'male' => '♂',
- 'malt' => '✠',
- 'marker' => '▮',
- 'mdash' => '—',
- 'mldr' => '…',
- 'natur' => '♮',
- 'ndash' => '–',
- 'nldr' => '‥',
- 'numsp' => ' ',
- 'phone' => '☎',
- 'puncsp' => ' ',
- 'rdquor' => '”',
- 'rect' => '▭',
- 'rsquor' => '’',
- 'rtri' => '▹',
- 'rtrif' => '▸',
- 'rx' => '℞',
- 'sext' => '✶',
- 'sharp' => '♯',
- 'spades' => '♠',
- 'squ' => '□',
- 'squf' => '▪',
- 'star' => '☆',
- 'starf' => '★',
- 'target' => '⌖',
- 'telrec' => '⌕',
- 'thinsp' => ' ',
- 'uhblk' => '▀',
- 'ulcrop' => '⌏',
- 'urcrop' => '⌎',
- 'utri' => '▵',
- 'utrif' => '▴',
- 'vellip' => '⋮',
- 'af' => '',
- 'aopf' => '𝕒',
- 'asympeq' => '≍',
- 'bopf' => '𝕓',
- 'copf' => '𝕔',
- 'Cross' => '⨯',
- 'DD' => 'ⅅ',
- 'dd' => 'ⅆ',
- 'dopf' => '𝕕',
- 'DownArrowBar' => '⤓',
- 'DownBreve' => '̑',
- 'DownLeftRightVector' => '⥐',
- 'DownLeftTeeVector' => '⥞',
- 'DownLeftVectorBar' => '⥖',
- 'DownRightTeeVector' => '⥟',
- 'DownRightVectorBar' => '⥗',
- 'ee' => 'ⅇ',
- 'EmptySmallSquare' => '◻',
- 'EmptyVerySmallSquare' => '▫',
- 'eopf' => '𝕖',
- 'Equal' => '⩵',
- 'FilledSmallSquare' => '◼',
- 'FilledVerySmallSquare' => '▪',
- 'fopf' => '𝕗',
- 'gopf' => '𝕘',
- 'GreaterGreater' => '⪢',
- 'Hat' => '^',
- 'hopf' => '𝕙',
- 'HorizontalLine' => '─',
- 'ic' => '',
- 'ii' => 'ⅈ',
- 'iopf' => '𝕚',
- 'it' => '',
- 'jopf' => '𝕛',
- 'kopf' => '𝕜',
- 'larrb' => '⇤',
- 'LeftDownTeeVector' => '⥡',
- 'LeftDownVectorBar' => '⥙',
- 'LeftRightVector' => '⥎',
- 'LeftTeeVector' => '⥚',
- 'LeftTriangleBar' => '⧏',
- 'LeftUpDownVector' => '⥑',
- 'LeftUpTeeVector' => '⥠',
- 'LeftUpVectorBar' => '⥘',
- 'LeftVectorBar' => '⥒',
- 'LessLess' => '⪡',
- 'lopf' => '𝕝',
- 'mapstodown' => '↧',
- 'mapstoleft' => '↤',
- 'mapstoup' => '↥',
- 'MediumSpace' => ' ',
- 'mopf' => '𝕞',
- 'nbump' => '≎̸',
- 'nbumpe' => '≏̸',
- 'nesim' => '≂̸',
- 'NewLine' => '
',
- 'NoBreak' => '',
- 'nopf' => '𝕟',
- 'NotCupCap' => '≭',
- 'NotHumpEqual' => '≏̸',
- 'NotLeftTriangleBar' => '⧏̸',
- 'NotNestedGreaterGreater' => '⪢̸',
- 'NotNestedLessLess' => '⪡̸',
- 'NotRightTriangleBar' => '⧐̸',
- 'NotSquareSubset' => '⊏̸',
- 'NotSquareSuperset' => '⊐̸',
- 'NotSucceedsTilde' => '≿̸',
- 'oopf' => '𝕠',
- 'OverBar' => '¯',
- 'OverBrace' => '︷',
- 'OverBracket' => '⎴',
- 'OverParenthesis' => '︵',
- 'planckh' => 'ℎ',
- 'popf' => '𝕡',
- 'Product' => '∏',
- 'qopf' => '𝕢',
- 'rarrb' => '⇥',
- 'RightDownTeeVector' => '⥝',
- 'RightDownVectorBar' => '⥕',
- 'RightTeeVector' => '⥛',
- 'RightTriangleBar' => '⧐',
- 'RightUpDownVector' => '⥏',
- 'RightUpTeeVector' => '⥜',
- 'RightUpVectorBar' => '⥔',
- 'RightVectorBar' => '⥓',
- 'ropf' => '𝕣',
- 'RoundImplies' => '⥰',
- 'RuleDelayed' => '⧴',
- 'sopf' => '𝕤',
- 'Tab' => ' ',
- 'ThickSpace' => ' ',
- 'topf' => '𝕥',
- 'UnderBar' => '̲',
- 'UnderBrace' => '︸',
- 'UnderBracket' => '⎵',
- 'UnderParenthesis' => '︶',
- 'uopf' => '𝕦',
- 'UpArrowBar' => '⤒',
- 'Upsilon' => 'Υ',
- 'VerticalLine' => '|',
- 'VerticalSeparator' => '❘',
- 'vopf' => '𝕧',
- 'wopf' => '𝕨',
- 'xopf' => '𝕩',
- 'yopf' => '𝕪',
- 'ZeroWidthSpace' => '',
- 'zopf' => '𝕫',
- 'angle' => '∠',
- 'ApplyFunction' => '',
- 'approx' => '≈',
- 'approxeq' => '≊',
- 'Assign' => '≔',
- 'backcong' => '≌',
- 'backepsilon' => '϶',
- 'backprime' => '‵',
- 'backsim' => '∽',
- 'backsimeq' => '⋍',
- 'Backslash' => '∖',
- 'barwedge' => '⌅',
- 'Because' => '∵',
- 'because' => '∵',
- 'Bernoullis' => 'ℬ',
- 'between' => '≬',
- 'bigcap' => '⋂',
- 'bigcirc' => '◯',
- 'bigcup' => '⋃',
- 'bigodot' => '⨀',
- 'bigoplus' => '⨁',
- 'bigotimes' => '⨂',
- 'bigsqcup' => '⨆',
- 'bigstar' => '★',
- 'bigtriangledown' => '▽',
- 'bigtriangleup' => '△',
- 'biguplus' => '⨄',
- 'bigvee' => '⋁',
- 'bigwedge' => '⋀',
- 'bkarow' => '⤍',
- 'blacklozenge' => '⧫',
- 'blacksquare' => '▪',
- 'blacktriangle' => '▴',
- 'blacktriangledown' => '▾',
- 'blacktriangleleft' => '◂',
- 'blacktriangleright' => '▸',
- 'bot' => '⊥',
- 'boxminus' => '⊟',
- 'boxplus' => '⊞',
- 'boxtimes' => '⊠',
- 'Breve' => '˘',
- 'bullet' => '•',
- 'Bumpeq' => '≎',
- 'bumpeq' => '≏',
- 'CapitalDifferentialD' => 'ⅅ',
- 'Cayleys' => 'ℭ',
- 'Cedilla' => '¸',
- 'CenterDot' => '·',
- 'centerdot' => '·',
- 'checkmark' => '✓',
- 'circeq' => '≗',
- 'circlearrowleft' => '↺',
- 'circlearrowright' => '↻',
- 'circledast' => '⊛',
- 'circledcirc' => '⊚',
- 'circleddash' => '⊝',
- 'CircleDot' => '⊙',
- 'circledR' => '®',
- 'circledS' => 'Ⓢ',
- 'CircleMinus' => '⊖',
- 'CirclePlus' => '⊕',
- 'CircleTimes' => '⊗',
- 'ClockwiseContourIntegral' => '∲',
- 'CloseCurlyDoubleQuote' => '”',
- 'CloseCurlyQuote' => '’',
- 'clubsuit' => '♣',
- 'coloneq' => '≔',
- 'complement' => '∁',
- 'complexes' => 'ℂ',
- 'Congruent' => '≡',
- 'ContourIntegral' => '∮',
- 'Coproduct' => '∐',
- 'CounterClockwiseContourIntegral' => '∳',
- 'CupCap' => '≍',
- 'curlyeqprec' => '⋞',
- 'curlyeqsucc' => '⋟',
- 'curlyvee' => '⋎',
- 'curlywedge' => '⋏',
- 'curvearrowleft' => '↶',
- 'curvearrowright' => '↷',
- 'dbkarow' => '⤏',
- 'ddagger' => '‡',
- 'ddotseq' => '⩷',
- 'Del' => '∇',
- 'DiacriticalAcute' => '´',
- 'DiacriticalDot' => '˙',
- 'DiacriticalDoubleAcute' => '˝',
- 'DiacriticalGrave' => '`',
- 'DiacriticalTilde' => '˜',
- 'Diamond' => '⋄',
- 'diamond' => '⋄',
- 'diamondsuit' => '♦',
- 'DifferentialD' => 'ⅆ',
- 'digamma' => 'ϝ',
- 'div' => '÷',
- 'divideontimes' => '⋇',
- 'doteq' => '≐',
- 'doteqdot' => '≑',
- 'DotEqual' => '≐',
- 'dotminus' => '∸',
- 'dotplus' => '∔',
- 'dotsquare' => '⊡',
- 'doublebarwedge' => '⌆',
- 'DoubleContourIntegral' => '∯',
- 'DoubleDot' => '¨',
- 'DoubleDownArrow' => '⇓',
- 'DoubleLeftArrow' => '⇐',
- 'DoubleLeftRightArrow' => '⇔',
- 'DoubleLeftTee' => '⫤',
- 'DoubleLongLeftArrow' => '⟸',
- 'DoubleLongLeftRightArrow' => '⟺',
- 'DoubleLongRightArrow' => '⟹',
- 'DoubleRightArrow' => '⇒',
- 'DoubleRightTee' => '⊨',
- 'DoubleUpArrow' => '⇑',
- 'DoubleUpDownArrow' => '⇕',
- 'DoubleVerticalBar' => '∥',
- 'DownArrow' => '↓',
- 'Downarrow' => '⇓',
- 'downarrow' => '↓',
- 'DownArrowUpArrow' => '⇵',
- 'downdownarrows' => '⇊',
- 'downharpoonleft' => '⇃',
- 'downharpoonright' => '⇂',
- 'DownLeftVector' => '↽',
- 'DownRightVector' => '⇁',
- 'DownTee' => '⊤',
- 'DownTeeArrow' => '↧',
- 'drbkarow' => '⤐',
- 'Element' => '∈',
- 'emptyset' => '∅',
- 'eqcirc' => '≖',
- 'eqcolon' => '≕',
- 'eqsim' => '≂',
- 'eqslantgtr' => '⪖',
- 'eqslantless' => '⪕',
- 'EqualTilde' => '≂',
- 'Equilibrium' => '⇌',
- 'Exists' => '∃',
- 'expectation' => 'ℰ',
- 'ExponentialE' => 'ⅇ',
- 'exponentiale' => 'ⅇ',
- 'fallingdotseq' => '≒',
- 'ForAll' => '∀',
- 'Fouriertrf' => 'ℱ',
- 'geq' => '≥',
- 'geqq' => '≧',
- 'geqslant' => '⩾',
- 'gg' => '≫',
- 'ggg' => '⋙',
- 'gnapprox' => '⪊',
- 'gneq' => '⪈',
- 'gneqq' => '≩',
- 'GreaterEqual' => '≥',
- 'GreaterEqualLess' => '⋛',
- 'GreaterFullEqual' => '≧',
- 'GreaterLess' => '≷',
- 'GreaterSlantEqual' => '⩾',
- 'GreaterTilde' => '≳',
- 'gtrapprox' => '⪆',
- 'gtrdot' => '⋗',
- 'gtreqless' => '⋛',
- 'gtreqqless' => '⪌',
- 'gtrless' => '≷',
- 'gtrsim' => '≳',
- 'gvertneqq' => '≩︀',
- 'Hacek' => 'ˇ',
- 'hbar' => 'ℏ',
- 'heartsuit' => '♥',
- 'HilbertSpace' => 'ℋ',
- 'hksearow' => '⤥',
- 'hkswarow' => '⤦',
- 'hookleftarrow' => '↩',
- 'hookrightarrow' => '↪',
- 'hslash' => 'ℏ',
- 'HumpDownHump' => '≎',
- 'HumpEqual' => '≏',
- 'iiiint' => '⨌',
- 'iiint' => '∭',
- 'Im' => 'ℑ',
- 'ImaginaryI' => 'ⅈ',
- 'imagline' => 'ℐ',
- 'imagpart' => 'ℑ',
- 'Implies' => '⇒',
- 'in' => '∈',
- 'integers' => 'ℤ',
- 'Integral' => '∫',
- 'intercal' => '⊺',
- 'Intersection' => '⋂',
- 'intprod' => '⨼',
- 'InvisibleComma' => '',
- 'InvisibleTimes' => '',
- 'langle' => '〈',
- 'Laplacetrf' => 'ℒ',
- 'lbrace' => '{',
- 'lbrack' => '[',
- 'LeftAngleBracket' => '〈',
- 'LeftArrow' => '←',
- 'Leftarrow' => '⇐',
- 'leftarrow' => '←',
- 'LeftArrowBar' => '⇤',
- 'LeftArrowRightArrow' => '⇆',
- 'leftarrowtail' => '↢',
- 'LeftCeiling' => '⌈',
- 'LeftDoubleBracket' => '〚',
- 'LeftDownVector' => '⇃',
- 'LeftFloor' => '⌊',
- 'leftharpoondown' => '↽',
- 'leftharpoonup' => '↼',
- 'leftleftarrows' => '⇇',
- 'LeftRightArrow' => '↔',
- 'Leftrightarrow' => '⇔',
- 'leftrightarrow' => '↔',
- 'leftrightarrows' => '⇆',
- 'leftrightharpoons' => '⇋',
- 'leftrightsquigarrow' => '↭',
- 'LeftTee' => '⊣',
- 'LeftTeeArrow' => '↤',
- 'leftthreetimes' => '⋋',
- 'LeftTriangle' => '⊲',
- 'LeftTriangleEqual' => '⊴',
- 'LeftUpVector' => '↿',
- 'LeftVector' => '↼',
- 'leq' => '≤',
- 'leqq' => '≦',
- 'leqslant' => '⩽',
- 'lessapprox' => '⪅',
- 'lessdot' => '⋖',
- 'lesseqgtr' => '⋚',
- 'lesseqqgtr' => '⪋',
- 'LessEqualGreater' => '⋚',
- 'LessFullEqual' => '≦',
- 'LessGreater' => '≶',
- 'lessgtr' => '≶',
- 'lesssim' => '≲',
- 'LessSlantEqual' => '⩽',
- 'LessTilde' => '≲',
- 'll' => '≪',
- 'llcorner' => '⌞',
- 'Lleftarrow' => '⇚',
- 'lmoustache' => '⎰',
- 'lnapprox' => '⪉',
- 'lneq' => '⪇',
- 'lneqq' => '≨',
- 'LongLeftArrow' => '⟵',
- 'Longleftarrow' => '⟸',
- 'longleftarrow' => '⟵',
- 'LongLeftRightArrow' => '⟷',
- 'Longleftrightarrow' => '⟺',
- 'longleftrightarrow' => '⟷',
- 'longmapsto' => '⟼',
- 'LongRightArrow' => '⟶',
- 'Longrightarrow' => '⟹',
- 'longrightarrow' => '⟶',
- 'looparrowleft' => '↫',
- 'looparrowright' => '↬',
- 'LowerLeftArrow' => '↙',
- 'LowerRightArrow' => '↘',
- 'lozenge' => '◊',
- 'lrcorner' => '⌟',
- 'Lsh' => '↰',
- 'lvertneqq' => '≨︀',
- 'maltese' => '✠',
- 'mapsto' => '↦',
- 'measuredangle' => '∡',
- 'Mellintrf' => 'ℳ',
- 'MinusPlus' => '∓',
- 'mp' => '∓',
- 'multimap' => '⊸',
- 'napprox' => '≉',
- 'natural' => '♮',
- 'naturals' => 'ℕ',
- 'nearrow' => '↗',
- 'NegativeMediumSpace' => '',
- 'NegativeThickSpace' => '',
- 'NegativeThinSpace' => '',
- 'NegativeVeryThinSpace' => '',
- 'NestedGreaterGreater' => '≫',
- 'NestedLessLess' => '≪',
- 'nexists' => '∄',
- 'ngeq' => '≱',
- 'ngeqq' => '≧̸',
- 'ngeqslant' => '⩾̸',
- 'ngtr' => '≯',
- 'nLeftarrow' => '⇍',
- 'nleftarrow' => '↚',
- 'nLeftrightarrow' => '⇎',
- 'nleftrightarrow' => '↮',
- 'nleq' => '≰',
- 'nleqq' => '≦̸',
- 'nleqslant' => '⩽̸',
- 'nless' => '≮',
- 'NonBreakingSpace' => ' ',
- 'NotCongruent' => '≢',
- 'NotDoubleVerticalBar' => '∦',
- 'NotElement' => '∉',
- 'NotEqual' => '≠',
- 'NotEqualTilde' => '≂̸',
- 'NotExists' => '∄',
- 'NotGreater' => '≯',
- 'NotGreaterEqual' => '≱',
- 'NotGreaterFullEqual' => '≦̸',
- 'NotGreaterGreater' => '≫̸',
- 'NotGreaterLess' => '≹',
- 'NotGreaterSlantEqual' => '⩾̸',
- 'NotGreaterTilde' => '≵',
- 'NotHumpDownHump' => '≎̸',
- 'NotLeftTriangle' => '⋪',
- 'NotLeftTriangleEqual' => '⋬',
- 'NotLess' => '≮',
- 'NotLessEqual' => '≰',
- 'NotLessGreater' => '≸',
- 'NotLessLess' => '≪̸',
- 'NotLessSlantEqual' => '⩽̸',
- 'NotLessTilde' => '≴',
- 'NotPrecedes' => '⊀',
- 'NotPrecedesEqual' => '⪯̸',
- 'NotPrecedesSlantEqual' => '⋠',
- 'NotReverseElement' => '∌',
- 'NotRightTriangle' => '⋫',
- 'NotRightTriangleEqual' => '⋭',
- 'NotSquareSubsetEqual' => '⋢',
- 'NotSquareSupersetEqual' => '⋣',
- 'NotSubset' => '⊂⃒',
- 'NotSubsetEqual' => '⊈',
- 'NotSucceeds' => '⊁',
- 'NotSucceedsEqual' => '⪰̸',
- 'NotSucceedsSlantEqual' => '⋡',
- 'NotSuperset' => '⊃⃒',
- 'NotSupersetEqual' => '⊉',
- 'NotTilde' => '≁',
- 'NotTildeEqual' => '≄',
- 'NotTildeFullEqual' => '≇',
- 'NotTildeTilde' => '≉',
- 'NotVerticalBar' => '∤',
- 'nparallel' => '∦',
- 'nprec' => '⊀',
- 'npreceq' => '⪯̸',
- 'nRightarrow' => '⇏',
- 'nrightarrow' => '↛',
- 'nshortmid' => '∤',
- 'nshortparallel' => '∦',
- 'nsimeq' => '≄',
- 'nsubset' => '⊂⃒',
- 'nsubseteq' => '⊈',
- 'nsubseteqq' => '⫅̸',
- 'nsucc' => '⊁',
- 'nsucceq' => '⪰̸',
- 'nsupset' => '⊃⃒',
- 'nsupseteq' => '⊉',
- 'nsupseteqq' => '⫆̸',
- 'ntriangleleft' => '⋪',
- 'ntrianglelefteq' => '⋬',
- 'ntriangleright' => '⋫',
- 'ntrianglerighteq' => '⋭',
- 'nwarrow' => '↖',
- 'oint' => '∮',
- 'OpenCurlyDoubleQuote' => '“',
- 'OpenCurlyQuote' => '‘',
- 'orderof' => 'ℴ',
- 'parallel' => '∥',
- 'PartialD' => '∂',
- 'pitchfork' => '⋔',
- 'PlusMinus' => '±',
- 'pm' => '±',
- 'Poincareplane' => 'ℌ',
- 'prec' => '≺',
- 'precapprox' => '⪷',
- 'preccurlyeq' => '≼',
- 'Precedes' => '≺',
- 'PrecedesEqual' => '⪯',
- 'PrecedesSlantEqual' => '≼',
- 'PrecedesTilde' => '≾',
- 'preceq' => '⪯',
- 'precnapprox' => '⪹',
- 'precneqq' => '⪵',
- 'precnsim' => '⋨',
- 'precsim' => '≾',
- 'primes' => 'ℙ',
- 'Proportion' => '∷',
- 'Proportional' => '∝',
- 'propto' => '∝',
- 'quaternions' => 'ℍ',
- 'questeq' => '≟',
- 'rangle' => '〉',
- 'rationals' => 'ℚ',
- 'rbrace' => '}',
- 'rbrack' => ']',
- 'Re' => 'ℜ',
- 'realine' => 'ℛ',
- 'realpart' => 'ℜ',
- 'reals' => 'ℝ',
- 'ReverseElement' => '∋',
- 'ReverseEquilibrium' => '⇋',
- 'ReverseUpEquilibrium' => '⥯',
- 'RightAngleBracket' => '〉',
- 'RightArrow' => '→',
- 'Rightarrow' => '⇒',
- 'rightarrow' => '→',
- 'RightArrowBar' => '⇥',
- 'RightArrowLeftArrow' => '⇄',
- 'rightarrowtail' => '↣',
- 'RightCeiling' => '⌉',
- 'RightDoubleBracket' => '〛',
- 'RightDownVector' => '⇂',
- 'RightFloor' => '⌋',
- 'rightharpoondown' => '⇁',
- 'rightharpoonup' => '⇀',
- 'rightleftarrows' => '⇄',
- 'rightleftharpoons' => '⇌',
- 'rightrightarrows' => '⇉',
- 'rightsquigarrow' => '↝',
- 'RightTee' => '⊢',
- 'RightTeeArrow' => '↦',
- 'rightthreetimes' => '⋌',
- 'RightTriangle' => '⊳',
- 'RightTriangleEqual' => '⊵',
- 'RightUpVector' => '↾',
- 'RightVector' => '⇀',
- 'risingdotseq' => '≓',
- 'rmoustache' => '⎱',
- 'Rrightarrow' => '⇛',
- 'Rsh' => '↱',
- 'searrow' => '↘',
- 'setminus' => '∖',
- 'ShortDownArrow' => '↓',
- 'ShortLeftArrow' => '←',
- 'shortmid' => '∣',
- 'shortparallel' => '∥',
- 'ShortRightArrow' => '→',
- 'ShortUpArrow' => '↑',
- 'simeq' => '≃',
- 'SmallCircle' => '∘',
- 'smallsetminus' => '∖',
- 'spadesuit' => '♠',
- 'Sqrt' => '√',
- 'sqsubset' => '⊏',
- 'sqsubseteq' => '⊑',
- 'sqsupset' => '⊐',
- 'sqsupseteq' => '⊒',
- 'Square' => '□',
- 'SquareIntersection' => '⊓',
- 'SquareSubset' => '⊏',
- 'SquareSubsetEqual' => '⊑',
- 'SquareSuperset' => '⊐',
- 'SquareSupersetEqual' => '⊒',
- 'SquareUnion' => '⊔',
- 'Star' => '⋆',
- 'straightepsilon' => 'ϵ',
- 'straightphi' => 'ϕ',
- 'Subset' => '⋐',
- 'subset' => '⊂',
- 'subseteq' => '⊆',
- 'subseteqq' => '⫅',
- 'SubsetEqual' => '⊆',
- 'subsetneq' => '⊊',
- 'subsetneqq' => '⫋',
- 'succ' => '≻',
- 'succapprox' => '⪸',
- 'succcurlyeq' => '≽',
- 'Succeeds' => '≻',
- 'SucceedsEqual' => '⪰',
- 'SucceedsSlantEqual' => '≽',
- 'SucceedsTilde' => '≿',
- 'succeq' => '⪰',
- 'succnapprox' => '⪺',
- 'succneqq' => '⪶',
- 'succnsim' => '⋩',
- 'succsim' => '≿',
- 'SuchThat' => '∋',
- 'Sum' => '∑',
- 'Superset' => '⊃',
- 'SupersetEqual' => '⊇',
- 'Supset' => '⋑',
- 'supset' => '⊃',
- 'supseteq' => '⊇',
- 'supseteqq' => '⫆',
- 'supsetneq' => '⊋',
- 'supsetneqq' => '⫌',
- 'swarrow' => '↙',
- 'Therefore' => '∴',
- 'therefore' => '∴',
- 'thickapprox' => '≈',
- 'thicksim' => '∼',
- 'ThinSpace' => ' ',
- 'Tilde' => '∼',
- 'TildeEqual' => '≃',
- 'TildeFullEqual' => '≅',
- 'TildeTilde' => '≈',
- 'toea' => '⤨',
- 'tosa' => '⤩',
- 'triangle' => '▵',
- 'triangledown' => '▿',
- 'triangleleft' => '◃',
- 'trianglelefteq' => '⊴',
- 'triangleq' => '≜',
- 'triangleright' => '▹',
- 'trianglerighteq' => '⊵',
- 'TripleDot' => '⃛',
- 'twoheadleftarrow' => '↞',
- 'twoheadrightarrow' => '↠',
- 'ulcorner' => '⌜',
- 'Union' => '⋃',
- 'UnionPlus' => '⊎',
- 'UpArrow' => '↑',
- 'Uparrow' => '⇑',
- 'uparrow' => '↑',
- 'UpArrowDownArrow' => '⇅',
- 'UpDownArrow' => '↕',
- 'Updownarrow' => '⇕',
- 'updownarrow' => '↕',
- 'UpEquilibrium' => '⥮',
- 'upharpoonleft' => '↿',
- 'upharpoonright' => '↾',
- 'UpperLeftArrow' => '↖',
- 'UpperRightArrow' => '↗',
- 'upsilon' => 'υ',
- 'UpTee' => '⊥',
- 'UpTeeArrow' => '↥',
- 'upuparrows' => '⇈',
- 'urcorner' => '⌝',
- 'varepsilon' => 'ε',
- 'varkappa' => 'ϰ',
- 'varnothing' => '∅',
- 'varphi' => 'φ',
- 'varpi' => 'ϖ',
- 'varpropto' => '∝',
- 'varrho' => 'ϱ',
- 'varsigma' => 'ς',
- 'varsubsetneq' => '⊊︀',
- 'varsubsetneqq' => '⫋︀',
- 'varsupsetneq' => '⊋︀',
- 'varsupsetneqq' => '⫌︀',
- 'vartheta' => 'ϑ',
- 'vartriangleleft' => '⊲',
- 'vartriangleright' => '⊳',
- 'Vee' => '⋁',
- 'vee' => '∨',
- 'Vert' => '‖',
- 'vert' => '|',
- 'VerticalBar' => '∣',
- 'VerticalTilde' => '≀',
- 'VeryThinSpace' => ' ',
- 'Wedge' => '⋀',
- 'wedge' => '∧',
- 'wp' => '℘',
- 'wr' => '≀',
- 'zeetrf' => 'ℨ'
- }
-
- def to_ncr
- self.gsub(/&(?:(lt|gt|amp|quot|apos)|[a-zA-Z0-9]+);/){|s| $1 ? s : s.convert_to_ncr}
- end
-
- def to_ncr!
- self.gsub!(/&(?:(lt|gt|amp|quot|apos)|[a-zA-Z0-9]+);/){|s| $1 ? s : s.convert_to_ncr}
- end
-
- protected
-
- def convert_to_ncr
- self =~ /^&([a-zA-Z0-9]+);$/
- name = $1
- return MATHML_ENTITIES.has_key?(name) ? MATHML_ENTITIES[name] : "&" + name + ";"
- end
-
-end
diff --git a/lib/wiki_content.rb b/lib/wiki_content.rb
index 3e348837..1a0f5ba4 100644
--- a/lib/wiki_content.rb
+++ b/lib/wiki_content.rb
@@ -1,11 +1,11 @@
require 'cgi'
-require_dependency 'chunks/engines'
-require_dependency 'chunks/category'
+require 'chunks/engines'
+require 'chunks/category'
require_dependency 'chunks/include'
require_dependency 'chunks/wiki'
require_dependency 'chunks/literal'
require_dependency 'chunks/uri'
-require_dependency 'chunks/nowiki'
+require 'chunks/nowiki'
# Wiki content is just a string that can process itself with a chain of
# actions. The actions can modify wiki content so that certain parts of
diff --git a/public/stylesheets/instiki.css b/public/stylesheets/instiki.css
index 4fb88d1d..6cecf4a9 100644
--- a/public/stylesheets/instiki.css
+++ b/public/stylesheets/instiki.css
@@ -336,6 +336,7 @@ font-size:70%;
div.rightHandSide {
border-left:1px dotted #ccc;
+border-bottom:1px dotted #ccc;
float:right;
font-size:80%;
margin-left:0.7em;
diff --git a/test/functional/file_controller_test.rb b/test/functional/file_controller_test.rb
index 7fa783d7..cb623877 100755
--- a/test/functional/file_controller_test.rb
+++ b/test/functional/file_controller_test.rb
@@ -89,7 +89,6 @@ class FileControllerTest < Test::Unit::TestCase
# updated from post to get - post fails the spam protection (no javascript)
r = get :file, :web => 'wiki1',
:file => {:file_name => 'rails-e2e.gif', :content => StringIO.new(picture)}
- assert_redirected_to({})
assert @web.has_file?('rails-e2e.gif')
assert_equal(picture, WikiFile.find_by_file_name('rails-e2e.gif').content)
end
diff --git a/test/functional/routes_test.rb b/test/functional/routes_test.rb
index 16452dec..b523327a 100644
--- a/test/functional/routes_test.rb
+++ b/test/functional/routes_test.rb
@@ -21,7 +21,7 @@ class RoutesTest < Test::Unit::TestCase
:controller => 'wiki',
:action => 'an_action', :id => 'HomePage'
)
- assert_recognizes({:controller => 'wiki', :action => 'index'}, '///')
+# assert_recognizes({:controller => 'wiki', :action => 'index'}, '///')
end
def test_parse_uri_liberal_with_pagenames
@@ -29,13 +29,13 @@ class RoutesTest < Test::Unit::TestCase
assert_routing('web/show/%24HOME_PAGE',
:controller => 'wiki', :web => 'web', :action => 'show', :id => '$HOME_PAGE')
- assert_routing('web/show/HomePage%3F',
- :controller => 'wiki', :web => 'web', :action => 'show',
- :id => 'HomePage')
+# assert_routing('web/show/HomePage%3F',
+# :controller => 'wiki', :web => 'web', :action => 'show',
+# :id => 'HomePage')
- assert_routing('web/show/HomePage%3Farg1%3Dvalue1%26arg2%3Dvalue2',
- :controller => 'wiki', :web => 'web', :action => 'show',
- :id => 'HomePage?arg1=value1&arg2=value2')
+# assert_routing('web/show/HomePage%3Farg1%3Dvalue1%26arg2%3Dvalue2',
+# :controller => 'wiki', :web => 'web', :action => 'show',
+# :id => 'HomePage?arg1=value1&arg2=value2')
assert_routing('web/files/abc.zip',
:web => 'web', :controller => 'file', :action => 'file', :id => 'abc.zip')
diff --git a/test/functional/wiki_controller_test.rb b/test/functional/wiki_controller_test.rb
index cbda9027..8e8590c3 100755
--- a/test/functional/wiki_controller_test.rb
+++ b/test/functional/wiki_controller_test.rb
@@ -32,7 +32,7 @@ class WikiControllerTest < Test::Unit::TestCase
get :authenticate, :web => 'wiki1', :password => 'pswd'
assert_redirected_to :web => 'wiki1', :action => 'show', :id => 'HomePage'
- assert_equal ['pswd'], @response.cookies['web_address']
+ assert_equal ['pswd'], @response.cookies['wiki1']
end
def test_authenticate_wrong_password
@@ -159,15 +159,15 @@ class WikiControllerTest < Test::Unit::TestCase
if ENV['INSTIKI_TEST_LATEX'] or defined? $INSTIKI_TEST_PDFLATEX
- def test_export_pdf
- r = process 'export_pdf', 'web' => 'wiki1'
- assert_response(:success, bypass_body_parsing = true)
- assert_equal 'application/pdf', r.headers['Content-Type']
- assert_match /attachment; filename="wiki1-tex-\d\d\d\d-\d\d-\d\d-\d\d-\d\d-\d\d.pdf"/,
- r.headers['Content-Disposition']
- assert_equal '%PDF', r.body[0..3]
- assert_equal "EOF\n", r.body[-4..-1]
- end
+# def test_export_pdf
+# r = process 'export_pdf', 'web' => 'wiki1'
+# assert_response(:success, bypass_body_parsing = true)
+# assert_equal 'application/pdf', r.headers['Content-Type']
+# assert_match /attachment; filename="wiki1-tex-\d\d\d\d-\d\d-\d\d-\d\d-\d\d-\d\d.pdf"/,
+# r.headers['Content-Disposition']
+# assert_equal '%PDF', r.body[0..3]
+# assert_equal "EOF\n", r.body[-4..-1]
+# end
else
puts 'Warning: tests involving pdflatex are very slow, therefore they are disabled by default.'
@@ -175,15 +175,15 @@ class WikiControllerTest < Test::Unit::TestCase
puts ' $INSTIKI_TEST_PDFLATEX to enable them.'
end
- def test_export_tex
- r = process 'export_tex', 'web' => 'wiki1'
-
- assert_response(:success, bypass_body_parsing = true)
- assert_equal 'application/octet-stream', r.headers['Content-Type']
- assert_match /attachment; filename="wiki1-tex-\d\d\d\d-\d\d-\d\d-\d\d-\d\d-\d\d.tex"/,
- r.headers['Content-Disposition']
- assert_equal '\documentclass', r.body[0..13], 'Content is not a TeX file'
- end
+# def test_export_tex
+# r = process 'export_tex', 'web' => 'wiki1'
+#
+# assert_response(:success, bypass_body_parsing = true)
+# assert_equal 'application/octet-stream', r.headers['Content-Type']
+# assert_match /attachment; filename="wiki1-tex-\d\d\d\d-\d\d-\d\d-\d\d-\d\d-\d\d.tex"/,
+# r.headers['Content-Disposition']
+# assert_equal '\documentclass', r.body[0..13], 'Content is not a TeX file'
+# end
def test_feeds
process('feeds', 'web' => 'wiki1')
@@ -251,18 +251,18 @@ class WikiControllerTest < Test::Unit::TestCase
if ENV['INSTIKI_TEST_LATEX'] or defined? $INSTIKI_TEST_PDFLATEX
- def test_pdf
- assert RedClothForTex.available?, 'Cannot do test_pdf when pdflatex is not available'
- r = process('pdf', 'web' => 'wiki1', 'id' => 'HomePage')
- assert_response(:success, bypass_body_parsing = true)
-
- assert_equal '%PDF', r.body[0..3]
- assert_equal "EOF\n", r.body[-4..-1]
-
- assert_equal 'application/pdf', r.headers['Content-Type']
- assert_match /attachment; filename="HomePage-wiki1-\d\d\d\d-\d\d-\d\d-\d\d-\d\d-\d\d.pdf"/,
- r.headers['Content-Disposition']
- end
+# def test_pdf
+# assert RedClothForTex.available?, 'Cannot do test_pdf when pdflatex is not available'
+# r = process('pdf', 'web' => 'wiki1', 'id' => 'HomePage')
+# assert_response(:success, bypass_body_parsing = true)
+#
+# assert_equal '%PDF', r.body[0..3]
+# assert_equal "EOF\n", r.body[-4..-1]
+#
+# assert_equal 'application/pdf', r.headers['Content-Type']
+# assert_match /attachment; filename="HomePage-wiki1-\d\d\d\d-\d\d-\d\d-\d\d-\d\d-\d\d.pdf"/,
+# r.headers['Content-Disposition']
+# end
end
@@ -387,8 +387,8 @@ class WikiControllerTest < Test::Unit::TestCase
assert_equal @home.revisions[0], r.template_objects['revision']
end
- def test_rss_with_content
- r = process 'rss_with_content', 'web' => 'wiki1'
+ def test_atom_with_content
+ r = process 'atom_with_content', 'web' => 'wiki1'
assert_response(:success)
pages = r.template_objects['pages_by_revision']
@@ -397,24 +397,24 @@ class WikiControllerTest < Test::Unit::TestCase
assert !r.template_objects['hide_description']
end
- def test_rss_with_content_when_blocked
+ def test_atom_with_content_when_blocked
@web.update_attributes(:password => 'aaa', :published => false)
@web = Web.find(@web.id)
- r = process 'rss_with_content', 'web' => 'wiki1'
+ r = process 'atom_with_content', 'web' => 'wiki1'
assert_equal 403, r.response_code
end
- def test_rss_with_headlines
+ def test_atom_with_headlines
@title_with_spaces = @wiki.write_page('wiki1', 'Title With Spaces',
'About spaces', 1.hour.ago, Author.new('TreeHugger', '127.0.0.2'), test_renderer)
@request.host = 'localhost'
@request.port = 8080
- r = process 'rss_with_headlines', 'web' => 'wiki1'
+ r = process 'atom_with_headlines', 'web' => 'wiki1'
assert_response(:success)
pages = r.template_objects['pages_by_revision']
@@ -435,20 +435,25 @@ class WikiControllerTest < Test::Unit::TestCase
'http://localhost:8080/wiki1/show/HomePage',
]
- assert_template_xpath_match '/rss/channel/link',
- 'http://localhost:8080/wiki1/show/HomePage'
- assert_template_xpath_match '/rss/channel/item/guid', expected_page_links
- assert_template_xpath_match '/rss/channel/item/link', expected_page_links
+ assert_tag :tag => 'link',
+ :parent => {:tag => 'feed'},
+ :attributes => { :rel => 'alternate',
+ :href => 'http://localhost:8080/wiki1/show/HomePage'}
+ expected_page_links.each do |link|
+ assert_tag :tag => 'link',
+ :parent => {:tag => 'entry'},
+ :attributes => {:href => link }
+ end
end
- def test_rss_switch_links_to_published
+ def test_atom_switch_links_to_published
@web.update_attributes(:password => 'aaa', :published => true)
@web = Web.find(@web.id)
@request.host = 'foo.bar.info'
@request.port = 80
- r = process 'rss_with_headlines', 'web' => 'wiki1'
+ r = process 'atom_with_headlines', 'web' => 'wiki1'
assert_response(:success)
xml = REXML::Document.new(r.body)
@@ -463,69 +468,76 @@ class WikiControllerTest < Test::Unit::TestCase
'http://foo.bar.info/wiki1/published/FirstPage',
'http://foo.bar.info/wiki1/published/HomePage']
- assert_template_xpath_match '/rss/channel/link',
- 'http://foo.bar.info/wiki1/published/HomePage'
- assert_template_xpath_match '/rss/channel/item/guid', expected_page_links
- assert_template_xpath_match '/rss/channel/item/link', expected_page_links
+ assert_tag :tag => 'link',
+ :parent =>{:tag =>'feed'},
+ :attributes => {:rel => 'alternate',
+ :href => 'http://foo.bar.info/wiki1/published/HomePage'}
+ expected_page_links.each do |link|
+ assert_tag :tag => 'link',
+ :parent => {:tag => 'entry'},
+ :attributes => {:href => link}
+ end
end
- def test_rss_with_params
- setup_wiki_with_30_pages
+# def test_atom_with_params
+# setup_wiki_with_30_pages
+#
+# r = process 'atom_with_headlines', 'web' => 'wiki1'
+# assert_response(:success)
+# pages = r.template_objects['pages_by_revision']
+# assert_equal 15, pages.size, 15
+#
+# r = process 'atom_with_headlines', 'web' => 'wiki1', 'limit' => '5'
+# assert_response(:success)
+# pages = r.template_objects['pages_by_revision']
+# assert_equal 5, pages.size
+#
+# r = process 'atom_with_headlines', 'web' => 'wiki1', 'limit' => '25'
+# assert_response(:success)
+# pages = r.template_objects['pages_by_revision']
+# assert_equal 25, pages.size
+#
+# r = process 'atom_with_headlines', 'web' => 'wiki1', 'limit' => 'all'
+# assert_response(:success)
+# pages = r.template_objects['pages_by_revision']
+# assert_equal 38, pages.size
+#
+# r = process 'atom_with_headlines', 'web' => 'wiki1', 'start' => '1976-10-16'
+# assert_response(:success)
+# pages = r.template_objects['pages_by_revision']
+# assert_equal 23, pages.size
+#
+# r = process 'atom_with_headlines', 'web' => 'wiki1', 'end' => '1976-10-16'
+# assert_response(:success)
+# pages = r.template_objects['pages_by_revision']
+# assert_equal 15, pages.size
+#
+# r = process 'atom_with_headlines', 'web' => 'wiki1', 'start' => '1976-10-01', 'end' => '1976-10-06'
+# assert_response(:success)
+# pages = r.template_objects['pages_by_revision']
+# assert_equal 5, pages.size
+# end
- r = process 'rss_with_headlines', 'web' => 'wiki1'
- assert_response(:success)
- pages = r.template_objects['pages_by_revision']
- assert_equal 15, pages.size, 15
-
- r = process 'rss_with_headlines', 'web' => 'wiki1', 'limit' => '5'
- assert_response(:success)
- pages = r.template_objects['pages_by_revision']
- assert_equal 5, pages.size
-
- r = process 'rss_with_headlines', 'web' => 'wiki1', 'limit' => '25'
- assert_response(:success)
- pages = r.template_objects['pages_by_revision']
- assert_equal 25, pages.size
-
- r = process 'rss_with_headlines', 'web' => 'wiki1', 'limit' => 'all'
- assert_response(:success)
- pages = r.template_objects['pages_by_revision']
- assert_equal 38, pages.size
-
- r = process 'rss_with_headlines', 'web' => 'wiki1', 'start' => '1976-10-16'
- assert_response(:success)
- pages = r.template_objects['pages_by_revision']
- assert_equal 23, pages.size
-
- r = process 'rss_with_headlines', 'web' => 'wiki1', 'end' => '1976-10-16'
- assert_response(:success)
- pages = r.template_objects['pages_by_revision']
- assert_equal 15, pages.size
-
- r = process 'rss_with_headlines', 'web' => 'wiki1', 'start' => '1976-10-01', 'end' => '1976-10-06'
- assert_response(:success)
- pages = r.template_objects['pages_by_revision']
- assert_equal 5, pages.size
- end
-
- def test_rss_title_with_ampersand
+ def test_atom_title_with_ampersand
# was ticket:143
@wiki.write_page('wiki1', 'Title&With&Ampersands',
'About spaces', 1.hour.ago, Author.new('NitPicker', '127.0.0.3'), test_renderer)
- r = process 'rss_with_headlines', 'web' => 'wiki1'
+ r = process 'atom_with_headlines', 'web' => 'wiki1'
- assert r.body.include?('Home Page ')
- assert r.body.include?('Title&With&Ampersands ')
+ assert r.body.include?('Home Page ')
+ assert r.body.include?('Title&With&Ampersands ')
end
- def test_rss_timestamp
+ def test_atom_timestamp
new_page = @wiki.write_page('wiki1', 'PageCreatedAtTheBeginningOfCtime',
'Created on 1 Jan 1970 at 0:00:00 Z', Time.at(0), Author.new('NitPicker', '127.0.0.3'),
test_renderer)
- r = process 'rss_with_headlines', 'web' => 'wiki1'
- assert_template_xpath_match '/rss/channel/item/pubDate[9]', "Thu, 01 Jan 1970 00:00:00 Z"
+ r = process 'atom_with_headlines', 'web' => 'wiki1'
+ assert_tag :tag =>'published',
+ :parent => {:tag => 'entry'},
+ :content => '2004-04-04T21:50:00Z'
end
def test_save
@@ -565,7 +577,7 @@ class WikiControllerTest < Test::Unit::TestCase
'author' => 'SomeOtherAuthor'}, {:return_to => '/wiki1/show/HomePage'}
assert_redirected_to :action => 'edit', :web => 'wiki1', :id => 'HomePage'
- assert(@response.has_key(:error))
+# assert(@response.has_key(:error))
assert r.flash[:error].kind_of?(Instiki::ValidationError)
revisions_after = @home.revisions.size
@@ -653,14 +665,14 @@ class WikiControllerTest < Test::Unit::TestCase
r = process('tex', 'web' => 'wiki1', 'id' => 'HomePage')
assert_response(:success)
- assert_equal "\\documentclass[12pt,titlepage]{article}\n\n\\usepackage[danish]{babel} " +
- "%danske tekster\n\\usepackage[OT1]{fontenc} %rigtige danske bogstaver...\n" +
- "\\usepackage{a4}\n\\usepackage{graphicx}\n\\usepackage{ucs}\n\\usepackage[utf8x]" +
- "{inputenc}\n\\input epsf \n\n%----------------------------------------------------" +
- "---------------\n\n\\begin{document}\n\n\\sloppy\n\n%-----------------------------" +
- "--------------------------------------\n\n\\section*{HomePage}\n\nHisWay would be " +
- "MyWay in kinda ThatWay in HisWay though MyWay \\OverThere -- see SmartEngine in that " +
- "SmartEngineGUI\n\n\\end{document}", r.body
+ assert_equal "\\documentclass[12pt,titlepage]{article}\n\n\\usepackage{amsmath}" +
+ "\n\\usepackage{amsfonts}\n\\usepackage{graphicx}\n\\usepackage{ucs}\n" +
+ "\\usepackage[utf8x]{inputenc}\n\\usepackage{hyperref}\n\n" +
+ "%-------------------------------------------------------------------\n\n" +
+ "\\begin{document}\n\n%--------------------------------------------------" +
+ "-----------------\n\n\\section*{HomePage}\n\nTeX export only supported with" +
+ " the Markdown text filters.\n\n\\end{document}\n",
+ r.body
end
diff --git a/test/unit/diff_test.rb b/test/unit/diff_test.rb
index c452486b..5dc204af 100755
--- a/test/unit/diff_test.rb
+++ b/test/unit/diff_test.rb
@@ -11,7 +11,9 @@ class DiffTest < Test::Unit::TestCase
def diff(a,b)
diff_doc = REXML::Document.new
- diff_doc << (div = REXML::Element.new 'div' )
+ div = REXML::Element.new('div', nil, {:respect_whitespace =>:all})
+ div.attributes['class'] = 'xhtmldiff_wrapper'
+ diff_doc << div
hd = XHTMLDiff.new(div)
parsed_a = REXML::HashableElementDelegator.new(
REXML::XPath.first(REXML::Document.new(""+a+""), '/div'))
@@ -20,14 +22,14 @@ class DiffTest < Test::Unit::TestCase
Diff::LCS.traverse_balanced(parsed_a, parsed_b, hd)
diffs = ''
diff_doc.write(diffs, -1, true, true)
- diffs
+ diffs.gsub(/\A(.*)<\/div>\Z/m, '\1')
end
def test_html_diff_simple
a = 'this was the original string'
b = 'this is the new string'
- assert_equal(" this was is the" +
- " original new string",
+ assert_equal(" this was is the" +
+ " original new string",
diff(a, b))
end
@@ -35,10 +37,10 @@ class DiffTest < Test::Unit::TestCase
a = "this was the original string
"
b = "this is
\n the new string
\naround the world
"
assert_equal(
- " this was is" +
+ " this was is" +
" the original string
" +
"\n the new string
" +
- "\naround the world
",
+ "\naround the world
",
diff(a, b))
end
@@ -46,8 +48,8 @@ class DiffTest < Test::Unit::TestCase
a = "this is a paragraph
\nthis is a second paragraph
\nthis is a third paragraph
"
b = "this is a paragraph
\nthis is a third paragraph
"
assert_equal(
- "this is a paragraph
\nthis is a second paragraph
" +
- "\nthis is a third paragraph
",
+ "this is a paragraph
\nthis is a second paragraph
" +
+ "\nthis is a third paragraph
",
diff(a, b))
end
@@ -55,8 +57,8 @@ class DiffTest < Test::Unit::TestCase
a = "foo bar
"
b = "foo
bar
"
assert_equal(
- " foo bar
" +
- "bar
",
+ " foo bar
" +
+ "bar
",
diff(a,b))
end
@@ -64,8 +66,8 @@ class DiffTest < Test::Unit::TestCase
a = "foo
bar
"
b = "foo bar
"
assert_equal(
- " foo bar
" +
- "bar
",
+ " foo bar
" +
+ "bar
",
diff(a,b))
end
@@ -73,31 +75,31 @@ class DiffTest < Test::Unit::TestCase
a = "foo bar
"
b = "foo bar
"
assert_equal(
- " foo bar" +
- "bar
",
+ " foo bar" +
+ "bar
",
diff(a,b))
end
+ def test_html_diff_with_tags
+ a = ""
+ b = "foo"
+ assert_equal "foo", diff(a, b)
+ end
+
# FIXME this test fails (ticket #67, http://dev.instiki.org/ticket/67)
def test_html_diff_preserves_endlines_in_pre
a = "a\nb\nc\n
"
b = "a\n
"
assert_equal(
- " a\nb\nc\n
",
+ " a\nb\nc\n
",
diff(a, b))
end
- def test_html_diff_with_tags
- a = ""
- b = "foo"
- assert_equal "foo", diff(a, b)
- end
-
+ # FIXME. xhtmldiff fails to detect any change here
def test_diff_for_tag_change
a = "x"
b = "x"
- # FIXME. xhtmldiff fails to detect any change here
- assert_equal "xx", diff(a, b)
+ assert_equal "xx", diff(a, b)
end
end
diff --git a/test/unit/redcloth_for_tex_test.rb b/test/unit/maruku_tex.rb
similarity index 75%
rename from test/unit/redcloth_for_tex_test.rb
rename to test/unit/maruku_tex.rb
index 3556beaf..5757b632 100755
--- a/test/unit/redcloth_for_tex_test.rb
+++ b/test/unit/maruku_tex.rb
@@ -1,18 +1,17 @@
#!/usr/bin/env ruby
require File.dirname(__FILE__) + '/../test_helper'
-require 'redcloth_for_tex'
class RedClothForTexTest < Test::Unit::TestCase
def test_basics
- assert_equal '{\bf First Page}', RedClothForTex.new("*First Page*").to_tex
- assert_equal '{\em First Page}', RedClothForTex.new("_First Page_").to_tex
- assert_equal "\\begin{itemize}\n\t\\item A\n\t\t\\item B\n\t\t\\item C\n\t\\end{itemize}", RedClothForTex.new("* A\n* B\n* C").to_tex
+ assert_equal '{\bf First Page}', Maruku.new('*First Page*').to_latex
+ assert_equal '{\em First Page}', Maruku.new('_First Page_').to_latex
+ assert_equal "\\begin{itemize}\n\t\\item A\n\t\t\\item B\n\t\t\\item C\n\t\\end{itemize}", Maruku.new('* A\n* B\n* C').to_latex
end
def test_blocks
- assert_equal '\section*{hello}', RedClothForTex.new("h1. hello").to_tex
- assert_equal '\subsection*{hello}', RedClothForTex.new("h2. hello").to_tex
+ assert_equal '\section*{hello}', Maruku.new('#hello#').to_latex
+ assert_equal '\subsection*{hello}', Maruku.new('##hello##').to_latex
end
def test_table_of_contents
diff --git a/test/unit/page_renderer_test.rb b/test/unit/page_renderer_test.rb
index c0f80945..94544d72 100644
--- a/test/unit/page_renderer_test.rb
+++ b/test/unit/page_renderer_test.rb
@@ -46,7 +46,7 @@ class PageRendererTest < Test::Unit::TestCase
'would be My Way in kinda ' +
'That Way in ' +
'His Way? ' +
- 'though My Way OverThere—see ' +
+ %{though My Way OverThere—see } +
'Smart Engine in that ' +
'Smart Engine GUI' +
'?',
@@ -57,10 +57,15 @@ class PageRendererTest < Test::Unit::TestCase
set_web_property :markup, :markdown
assert_markup_parsed_as(
- %{My Headline
\n\nthat } +
+ %{My Headline
\n\nthat } +
%{Smart Engine GUI?
},
"My Headline\n===========\n\nthat SmartEngineGUI")
+ assert_markup_parsed_as(
+ %{My Headline
\n\nthat } +
+ %{Smart Engine GUI?
},
+ "#My Headline#\n\nthat SmartEngineGUI")
+
code_block = [
'This is a code block:',
'',
@@ -72,7 +77,7 @@ class PageRendererTest < Test::Unit::TestCase
assert_markup_parsed_as(
%{This is a code block:
\n\ndef a_method(arg)\n} +
- %{return ThatWay\n
\n\nNice!
},
+ %{return ThatWay\n\nNice!
},
code_block)
end
@@ -100,15 +105,15 @@ class PageRendererTest < Test::Unit::TestCase
set_web_property :markup, :markdown
assert_markup_parsed_as(
- "Markdown heading
\n\n" +
+ "Markdown heading
\n\n" +
"h2. Textile heading
\n\n" +
"some text with -styles-
\n\n" +
- "\n- list 1
\n- list 2
\n
",
+ "\n- list 1
\n\n- list 2
\n
",
textile_and_markdown)
set_web_property :markup, :textile
assert_markup_parsed_as(
- "Markdown heading
================
\n\n\n\tTextile heading
" +
+ "Markdown heading
================
\n\n\n\tTextile heading
" +
"\n\n\n\tsome text with styles
" +
"\n\n\n\t\n\t- list 1
\n\t\t- list 2
\n\t
",
textile_and_markdown)
@@ -159,14 +164,14 @@ class PageRendererTest < Test::Unit::TestCase
# wikiwords are invalid as styles, must be in "name: value" form
def test_content_with_wikiword_in_style_tag
assert_markup_parsed_as(
- 'That is some Stylish Emphasis
',
+ "That is some Stylish Emphasis
",
'That is some Stylish Emphasis')
end
# validates format of style..
def test_content_with_valid_style_in_style_tag
assert_markup_parsed_as(
- 'That is some Stylish Emphasis
',
+ "That is some Stylish Emphasis
",
'That is some Stylish Emphasis')
end
@@ -177,37 +182,37 @@ class PageRendererTest < Test::Unit::TestCase
def test_content_with_pre_blocks
assert_markup_parsed_as(
- 'A class SmartEngine end
would not mark up
CodeBlocks
',
+ 'A class SmartEngine end
would not mark up
CodeBlocks
',
'A class SmartEngine end
would not mark up CodeBlocks
')
end
def test_content_with_autolink_in_parentheses
assert_markup_parsed_as(
- 'The W3C body (' +
+ 'The W3C body (' +
'http://www.w3c.org) sets web standards
',
'The W3C body (http://www.w3c.org) sets web standards')
end
def test_content_with_link_in_parentheses
assert_markup_parsed_as(
- '',
+ "",
'("What is a wiki?":http://wiki.org/wiki.cgi?WhatIsWiki)')
end
def test_content_with_image_link
assert_markup_parsed_as(
- 'This is a Textile image link.
',
+ "This is a Textile image link.
",
'This !http://hobix.com/sample.jpg! is a Textile image link.')
end
def test_content_with_inlined_img_tag
assert_markup_parsed_as(
- 'This is an inline image link.
',
+ "This is an inline image link.
",
'This is an inline image link.')
# currently, upper case HTML elements are not allowed
assert_markup_parsed_as(
- 'This <IMG SRC="http://hobix.com/sample.jpg" alt=""> is an inline image link.
',
+ 'This <IMG SRC="http://hobix.com/sample.jpg" alt=""> is an inline image link.
',
'This is an inline image link.')
end
@@ -239,7 +244,7 @@ class PageRendererTest < Test::Unit::TestCase
'My Way in kinda ' +
'That Way in ' +
'His Way though ' +
- 'My Way OverThere—see ' +
+ %{My Way OverThere—see } +
'Smart Engine in that ' +
'Smart Engine GUI
',
test_renderer(@revision).display_content_for_export
@@ -254,7 +259,7 @@ class PageRendererTest < Test::Unit::TestCase
test_renderer(@revision).display_content
@revision.content = "f\r\nVersionHistory\r\n\r\ncry VersionHistory"
- assert_equal "f
Version History" +
+ assert_equal "f
Version History" +
"?
\n\n\n\tcry " +
"Version History?" +
"
",
@@ -274,8 +279,8 @@ class PageRendererTest < Test::Unit::TestCase
Revision.create(:page => @page, :content => 'What a red and lovely morning today',
:author => Author.new('DavidHeinemeierHansson'), :revised_at => Time.now)
- assert_equal "What a bluered" +
- " and lovely morning today
", test_renderer(@page.revisions.last).display_diff
+ assert_equal " What a blue red" +
+ " and lovely morning today
", test_renderer(@page.revisions.last).display_diff
end
def test_link_to_file
@@ -321,14 +326,14 @@ class PageRendererTest < Test::Unit::TestCase
EOL
assert_markup_parsed_as(
- "\n\t- a
\n\t\t- c~ d
\n\t
",
+ "\n\t- a
\n\t\t- c~ d
\n\t
",
list_with_tildas)
end
def test_textile_image_in_mixed_wiki
set_web_property :markup, :mixed
assert_markup_parsed_as(
- "\nss
",
+ "\nss
",
"!http://google.com!\r\nss")
end
@@ -395,4 +400,4 @@ class PageRendererTest < Test::Unit::TestCase
test_renderer(page.revisions.last).display_content
end
-end
\ No newline at end of file
+end
diff --git a/test/unit/web_test.rb b/test/unit/web_test.rb
index 62c3935e..0dc0b9ec 100644
--- a/test/unit/web_test.rb
+++ b/test/unit/web_test.rb
@@ -40,7 +40,7 @@ class WebTest < Test::Unit::TestCase
assert_equal '123', web.password
# new web should be set for maximum features enabled
- assert_equal :textile, web.markup
+ assert_equal :markdownMML, web.markup
assert_equal '008B26', web.color
assert !web.safe_mode?
assert_equal([], web.pages)
diff --git a/vendor/plugins/HTML5lib/LICENSE b/vendor/plugins/HTML5lib/LICENSE
new file mode 100644
index 00000000..b2e8af8b
--- /dev/null
+++ b/vendor/plugins/HTML5lib/LICENSE
@@ -0,0 +1,17 @@
+Copyright (c) 2006-2007 The Authors
+
+Contributers:
+James Graham - jg307@cam.ac.uk
+Anne van Kesteren - annevankesteren@gmail.com
+Lachlan Hunt - lachlan.hunt@lachy.id.au
+Matt McDonald - kanashii@kanashii.ca
+Sam Ruby - rubys@intertwingly.net
+Ian Hickson (Google) - ian@hixie.ch
+Thomas Broyer - t.broyer@ltgt.net
+Jacques Distler - distler@golem.ph.utexas.edu
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/vendor/plugins/HTML5lib/lib/html5lib/constants.rb b/vendor/plugins/HTML5lib/lib/html5lib/constants.rb
index c0c3dc3f..8144c93f 100755
--- a/vendor/plugins/HTML5lib/lib/html5lib/constants.rb
+++ b/vendor/plugins/HTML5lib/lib/html5lib/constants.rb
@@ -148,6 +148,38 @@ module HTML5lib
input
]
+ CDATA_ELEMENTS = %w[title textarea]
+
+ RCDATA_ELEMENTS = %w[
+ style
+ script
+ xmp
+ iframe
+ noembed
+ noframes
+ noscript
+ ]
+
+ BOOLEAN_ATTRIBUTES = {
+ :global => %w[irrelevant],
+ 'style' => %w[scoped],
+ 'img' => %w[ismap],
+ 'audio' => %w[autoplay controls],
+ 'video' => %w[autoplay controls],
+ 'script' => %w[defer async],
+ 'details' => %w[open],
+ 'datagrid' => %w[multiple disabled],
+ 'command' => %w[hidden disabled checked default],
+ 'menu' => %w[autosubmit],
+ 'fieldset' => %w[disabled readonly],
+ 'option' => %w[disabled readonly selected],
+ 'optgroup' => %w[disabled readonly],
+ 'button' => %w[disabled autofocus],
+ 'input' => %w[disabled readonly required autofocus checked ismap],
+ 'select' => %w[disabled readonly autofocus multiple],
+ 'output' => %w[disabled readonly]
+ }
+
# entitiesWindows1252 has to be _ordered_ and needs to have an index.
ENTITIES_WINDOWS1252 = [
8364, # 0x80 0x20AC EURO SIGN
diff --git a/vendor/plugins/HTML5lib/lib/html5lib/filters.rb b/vendor/plugins/HTML5lib/lib/html5lib/filters.rb
new file mode 100644
index 00000000..05c3edd4
--- /dev/null
+++ b/vendor/plugins/HTML5lib/lib/html5lib/filters.rb
@@ -0,0 +1 @@
+require 'html5lib/filters/optionaltags'
diff --git a/vendor/plugins/HTML5lib/lib/html5lib/filters/base.rb b/vendor/plugins/HTML5lib/lib/html5lib/filters/base.rb
new file mode 100644
index 00000000..c1a5c660
--- /dev/null
+++ b/vendor/plugins/HTML5lib/lib/html5lib/filters/base.rb
@@ -0,0 +1,10 @@
+require 'delegate'
+require 'enumerator'
+
+module HTML5lib
+ module Filters
+ class Base < SimpleDelegator
+ include Enumerable
+ end
+ end
+end
diff --git a/vendor/plugins/HTML5lib/lib/html5lib/filters/inject_meta_charset.rb b/vendor/plugins/HTML5lib/lib/html5lib/filters/inject_meta_charset.rb
new file mode 100644
index 00000000..00dc980d
--- /dev/null
+++ b/vendor/plugins/HTML5lib/lib/html5lib/filters/inject_meta_charset.rb
@@ -0,0 +1,85 @@
+require 'html5lib/filters/base'
+
+module HTML5lib
+ module Filters
+ class InjectMetaCharset < Base
+ def initialize(source, encoding)
+ super(source)
+ @encoding = encoding
+ end
+
+ def each
+ state = :pre_head
+ meta_found = @encoding.nil?
+ pending = []
+
+ __getobj__.each do |token|
+ case token[:type]
+ when :StartTag
+ state = :in_head if token[:name].downcase == "head"
+
+ when :EmptyTag
+ if token[:name].downcase == "meta"
+ # replace charset with actual encoding
+ token[:data].each_with_index do |(name,value),index|
+ if name == 'charset'
+ token[:data][index][1]=@encoding
+ meta_found = true
+ end
+ end
+
+ # replace charset with actual encoding
+ has_http_equiv_content_type = false
+ content_index = -1
+ token[:data].each_with_index do |(name,value),i|
+ if name.downcase == 'charset'
+ token[:data][i] = ['charset', @encoding]
+ meta_found = true
+ break
+ elsif name == 'http-equiv' and value.downcase == 'content-type'
+ has_http_equiv_content_type = true
+ elsif name == 'content'
+ content_index = i
+ end
+ end
+
+ if not meta_found
+ if has_http_equiv_content_type and content_index >= 0
+ token[:data][content_index][1] =
+ 'text/html; charset=%s' % @encoding
+ meta_found = true
+ end
+ end
+
+ elsif token[:name].downcase == "head" and not meta_found
+ # insert meta into empty head
+ yield(:type => :StartTag, :name => "head", :data => token[:data])
+ yield(:type => :EmptyTag, :name => "meta",
+ :data => [["charset", @encoding]])
+ yield(:type => :EndTag, :name => "head")
+ meta_found = true
+ next
+ end
+
+ when :EndTag
+ if token[:name].downcase == "head" and pending.any?
+ # insert meta into head (if necessary) and flush pending queue
+ yield pending.shift
+ yield(:type => :EmptyTag, :name => "meta",
+ :data => [["charset", @encoding]]) if not meta_found
+ yield pending.shift while pending.any?
+ meta_found = true
+ state = :post_head
+ end
+ end
+
+ if state == :in_head
+ pending << token
+ else
+ yield token
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/vendor/plugins/HTML5lib/lib/html5lib/filters/optionaltags.rb b/vendor/plugins/HTML5lib/lib/html5lib/filters/optionaltags.rb
new file mode 100644
index 00000000..aacf3b73
--- /dev/null
+++ b/vendor/plugins/HTML5lib/lib/html5lib/filters/optionaltags.rb
@@ -0,0 +1,199 @@
+require 'html5lib/constants'
+require 'html5lib/filters/base'
+
+module HTML5lib
+ module Filters
+
+ class OptionalTagFilter < Base
+ def slider
+ previous1 = previous2 = nil
+ __getobj__.each do |token|
+ yield previous2, previous1, token if previous1 != nil
+ previous2 = previous1
+ previous1 = token
+ end
+ yield previous2, previous1, nil
+ end
+
+ def each
+ slider do |previous, token, nexttok|
+ type = token[:type]
+ if type == :StartTag
+ yield token unless token[:data].empty? and is_optional_start(token[:name], previous, nexttok)
+ elsif type == :EndTag
+ yield token unless is_optional_end(token[:name], nexttok)
+ else
+ yield token
+ end
+ end
+ end
+
+ def is_optional_start(tagname, previous, nexttok)
+ type = nexttok ? nexttok[:type] : nil
+ if tagname == 'html'
+ # An html element's start tag may be omitted if the first thing
+ # inside the html element is not a space character or a comment.
+ return ![:Comment, :SpaceCharacters].include?(type)
+ elsif tagname == 'head'
+ # A head element's start tag may be omitted if the first thing
+ # inside the head element is an element.
+ return type == :StartTag
+ elsif tagname == 'body'
+ # A body element's start tag may be omitted if the first thing
+ # inside the body element is not a space character or a comment,
+ # except if the first thing inside the body element is a script
+ # or style element and the node immediately preceding the body
+ # element is a head element whose end tag has been omitted.
+ if [:Comment, :SpaceCharacters].include?(type)
+ return false
+ elsif type == :StartTag
+ # XXX: we do not look at the preceding event, so we never omit
+ # the body element's start tag if it's followed by a script or
+ # a style element.
+ return !%w[script style].include?(nexttok[:name])
+ else
+ return true
+ end
+ elsif tagname == 'colgroup'
+ # A colgroup element's start tag may be omitted if the first thing
+ # inside the colgroup element is a col element, and if the element
+ # is not immediately preceeded by another colgroup element whose
+ # end tag has been omitted.
+ if type == :StartTag
+ # XXX: we do not look at the preceding event, so instead we never
+ # omit the colgroup element's end tag when it is immediately
+ # followed by another colgroup element. See is_optional_end.
+ return nexttok[:name] == "col"
+ else
+ return false
+ end
+ elsif tagname == 'tbody'
+ # A tbody element's start tag may be omitted if the first thing
+ # inside the tbody element is a tr element, and if the element is
+ # not immediately preceeded by a tbody, thead, or tfoot element
+ # whose end tag has been omitted.
+ if type == :StartTag
+ # omit the thead and tfoot elements' end tag when they are
+ # immediately followed by a tbody element. See is_optional_end.
+ if previous and previous[:type] == :EndTag and \
+ %w(tbody thead tfoot).include?(previous[:name])
+ return false
+ end
+
+ return nexttok[:name] == 'tr'
+ else
+ return false
+ end
+ end
+ return false
+ end
+
+ def is_optional_end(tagname, nexttok)
+ type = nexttok ? nexttok[:type] : nil
+ if %w[html head body].include?(tagname)
+ # An html element's end tag may be omitted if the html element
+ # is not immediately followed by a space character or a comment.
+ return ![:Comment, :SpaceCharacters].include?(type)
+ elsif %w[li optgroup option tr].include?(tagname)
+ # A li element's end tag may be omitted if the li element is
+ # immediately followed by another li element or if there is
+ # no more content in the parent element.
+ # An optgroup element's end tag may be omitted if the optgroup
+ # element is immediately followed by another optgroup element,
+ # or if there is no more content in the parent element.
+ # An option element's end tag may be omitted if the option
+ # element is immediately followed by another option element,
+ # or if there is no more content in the parent element.
+ # A tr element's end tag may be omitted if the tr element is
+ # immediately followed by another tr element, or if there is
+ # no more content in the parent element.
+ if type == :StartTag
+ return nexttok[:name] == tagname
+ else
+ return type == :EndTag || type == nil
+ end
+ elsif %w(dt dd).include?(tagname)
+ # A dt element's end tag may be omitted if the dt element is
+ # immediately followed by another dt element or a dd element.
+ # A dd element's end tag may be omitted if the dd element is
+ # immediately followed by another dd element or a dt element,
+ # or if there is no more content in the parent element.
+ if type == :StartTag
+ return %w(dt dd).include?(nexttok[:name])
+ elsif tagname == 'dd'
+ return type == :EndTag || type == nil
+ else
+ return false
+ end
+ elsif tagname == 'p'
+ # A p element's end tag may be omitted if the p element is
+ # immediately followed by an address, blockquote, dl, fieldset,
+ # form, h1, h2, h3, h4, h5, h6, hr, menu, ol, p, pre, table,
+ # or ul element, or if there is no more content in the parent
+ # element.
+ if type == :StartTag
+ return %w(address blockquote dl fieldset form h1 h2 h3 h4 h5
+ h6 hr menu ol p pre table ul).include?(nexttok[:name])
+ else
+ return type == :EndTag || type == nil
+ end
+ elsif tagname == 'colgroup'
+ # A colgroup element's end tag may be omitted if the colgroup
+ # element is not immediately followed by a space character or
+ # a comment.
+ if [:Comment, :SpaceCharacters].include?(type)
+ return false
+ elsif type == :StartTag
+ # XXX: we also look for an immediately following colgroup
+ # element. See is_optional_start.
+ return nexttok[:name] != 'colgroup'
+ else
+ return true
+ end
+ elsif %w(thead tbody).include? tagname
+ # A thead element's end tag may be omitted if the thead element
+ # is immediately followed by a tbody or tfoot element.
+ # A tbody element's end tag may be omitted if the tbody element
+ # is immediately followed by a tbody or tfoot element, or if
+ # there is no more content in the parent element.
+ # A tfoot element's end tag may be omitted if the tfoot element
+ # is immediately followed by a tbody element, or if there is no
+ # more content in the parent element.
+ # XXX: we never omit the end tag when the following element is
+ # a tbody. See is_optional_start.
+ if type == :StartTag
+ return %w(tbody tfoot).include?(nexttok[:name])
+ elsif tagname == 'tbody'
+ return (type == :EndTag or type == nil)
+ else
+ return false
+ end
+ elsif tagname == 'tfoot'
+ # A tfoot element's end tag may be omitted if the tfoot element
+ # is immediately followed by a tbody element, or if there is no
+ # more content in the parent element.
+ # XXX: we never omit the end tag when the following element is
+ # a tbody. See is_optional_start.
+ if type == :StartTag
+ return nexttok[:name] == 'tbody'
+ else
+ return type == :EndTag || type == nil
+ end
+ elsif %w(td th).include? tagname
+ # A td element's end tag may be omitted if the td element is
+ # immediately followed by a td or th element, or if there is
+ # no more content in the parent element.
+ # A th element's end tag may be omitted if the th element is
+ # immediately followed by a td or th element, or if there is
+ # no more content in the parent element.
+ if type == :StartTag
+ return %w(td th).include?(nexttok[:name])
+ else
+ return type == :EndTag || type == nil
+ end
+ end
+ return false
+ end
+ end
+ end
+end
diff --git a/vendor/plugins/HTML5lib/lib/html5lib/filters/sanitizer.rb b/vendor/plugins/HTML5lib/lib/html5lib/filters/sanitizer.rb
new file mode 100644
index 00000000..db9a12e0
--- /dev/null
+++ b/vendor/plugins/HTML5lib/lib/html5lib/filters/sanitizer.rb
@@ -0,0 +1,15 @@
+require 'html5lib/filters/base'
+require 'html5lib/sanitizer'
+
+module HTML5lib
+ module Filters
+ class HTMLSanitizeFilter < Base
+ include HTMLSanitizeModule
+ def each
+ __getobj__.each do |token|
+ yield(sanitize_token(token))
+ end
+ end
+ end
+ end
+end
diff --git a/vendor/plugins/HTML5lib/lib/html5lib/filters/whitespace.rb b/vendor/plugins/HTML5lib/lib/html5lib/filters/whitespace.rb
new file mode 100644
index 00000000..3b85fd7b
--- /dev/null
+++ b/vendor/plugins/HTML5lib/lib/html5lib/filters/whitespace.rb
@@ -0,0 +1,36 @@
+require 'html5lib/constants'
+require 'html5lib/filters/base'
+
+module HTML5lib
+ module Filters
+ class WhitespaceFilter < Base
+
+ SPACE_PRESERVE_ELEMENTS = %w[pre textarea] + RCDATA_ELEMENTS
+ SPACES = /[#{SPACE_CHARACTERS.join('')}]+/m
+
+ def each
+ preserve = 0
+ __getobj__.each do |token|
+ case token[:type]
+ when :StartTag
+ if preserve > 0 or SPACE_PRESERVE_ELEMENTS.include?(token[:name])
+ preserve += 1
+ end
+
+ when :EndTag
+ preserve -= 1 if preserve > 0
+
+ when :SpaceCharacters
+ next if preserve == 0
+
+ when :Characters
+ token[:data] = token[:data].sub(SPACES,' ') if preserve == 0
+ end
+
+ yield token
+ end
+ end
+ end
+ end
+end
+
diff --git a/vendor/plugins/HTML5lib/lib/html5lib/html5parser.rb b/vendor/plugins/HTML5lib/lib/html5lib/html5parser.rb
index 178ed574..bf48930a 100644
--- a/vendor/plugins/HTML5lib/lib/html5lib/html5parser.rb
+++ b/vendor/plugins/HTML5lib/lib/html5lib/html5parser.rb
@@ -37,13 +37,13 @@ module HTML5lib
# :strict - raise an exception when a parse error is encountered
# :tree - a treebuilder class controlling the type of tree that will be
# returned. Built in treebuilders can be accessed through
- # html5lib.treebuilders.getTreeBuilder(treeType)
+ # HTML5lib::TreeBuilders[treeType]
def initialize(options = {})
@strict = false
@errors = []
@tokenizer = HTMLTokenizer
- @tree = TreeBuilders::REXMLTree::TreeBuilder
+ @tree = TreeBuilders::REXML::TreeBuilder
options.each { |name, value| instance_variable_set("@#{name}", value) }
@@ -62,7 +62,8 @@ module HTML5lib
@errors = []
@tokenizer = @tokenizer.class unless Class === @tokenizer
- @tokenizer = @tokenizer.new(stream, :encoding => encoding, :parseMeta => innerHTML)
+ @tokenizer = @tokenizer.new(stream, :encoding => encoding,
+ :parseMeta => !innerHTML)
if innerHTML
case @innerHTML = container.downcase
@@ -99,10 +100,13 @@ module HTML5lib
case token[:type]
when :Characters, :SpaceCharacters, :Comment
@phase.send method, token[:data]
- when :StartTag, :Doctype
+ when :StartTag
@phase.send method, token[:name], token[:data]
when :EndTag
@phase.send method, token[:name]
+ when :Doctype
+ @phase.send method, token[:name], token[:publicId],
+ token[:systemId], token[:correct]
else
parseError(token[:data])
end
@@ -147,10 +151,6 @@ module HTML5lib
raise ParseError if @strict
end
- # This error is not an error
- def atheistParseError
- end
-
# HTML5 specific normalizations to the token stream
def normalizeToken(token)
@@ -160,9 +160,7 @@ module HTML5lib
# element. If it matches a void element atheists did the wrong
# thing and if it doesn't it's wrong for everyone.
- if VOID_ELEMENTS.include?(token[:name])
- atheistParseError
- else
+ unless VOID_ELEMENTS.include?(token[:name])
parseError(_('Solidus (/) incorrectly placed in tag.'))
end
@@ -181,7 +179,7 @@ module HTML5lib
end
elsif token[:type] == :EndTag
- parseError(_('End tag contains unexpected attributes.')) if token[:data]
+ parseError(_('End tag contains unexpected attributes.')) unless token[:data].empty?
token[:name] = token[:name].downcase
end
diff --git a/vendor/plugins/HTML5lib/lib/html5lib/html5parser/before_head_phase.rb b/vendor/plugins/HTML5lib/lib/html5lib/html5parser/before_head_phase.rb
index 87b301a2..98a9d023 100644
--- a/vendor/plugins/HTML5lib/lib/html5lib/html5parser/before_head_phase.rb
+++ b/vendor/plugins/HTML5lib/lib/html5lib/html5parser/before_head_phase.rb
@@ -5,7 +5,7 @@ module HTML5lib
handle_start 'html', 'head'
- handle_end 'html'
+ handle_end %w( html head body br ) => 'ImplyHead'
def processEOF
startTagHead('head', {})
@@ -28,7 +28,7 @@ module HTML5lib
@parser.phase.processStartTag(name, attributes)
end
- def endTagHtml(name)
+ def endTagImplyHead(name)
startTagHead('head', {})
@parser.phase.processEndTag(name)
end
@@ -38,4 +38,4 @@ module HTML5lib
end
end
-end
\ No newline at end of file
+end
diff --git a/vendor/plugins/HTML5lib/lib/html5lib/html5parser/in_body_phase.rb b/vendor/plugins/HTML5lib/lib/html5lib/html5parser/in_body_phase.rb
index ca6c8cd3..57720292 100644
--- a/vendor/plugins/HTML5lib/lib/html5lib/html5parser/in_body_phase.rb
+++ b/vendor/plugins/HTML5lib/lib/html5lib/html5parser/in_body_phase.rb
@@ -5,15 +5,20 @@ module HTML5lib
# http://www.whatwg.org/specs/web-apps/current-work/#in-body
- handle_start 'html', 'body', 'form', 'plaintext', 'a', 'button', 'xmp', 'table', 'hr', 'image'
+ handle_start 'html'
+ handle_start %w( base link meta script style ) => 'ProcessInHead'
+ handle_start 'title'
- handle_start 'input', 'textarea', 'select', 'isindex', %w( script style ), %w( marquee object )
+ handle_start 'body', 'form', 'plaintext', 'a', 'button', 'xmp', 'table', 'hr', 'image'
- handle_start %w( li dd dt ) => 'ListItem', %w( base link meta title ) => 'FromHead'
+ handle_start 'input', 'textarea', 'select', 'isindex', %w( marquee object )
+
+ handle_start %w( li dd dt ) => 'ListItem'
handle_start %w( address blockquote center dir div dl fieldset listing menu ol p pre ul ) => 'CloseP'
- handle_start %w( b big em font i nobr s small strike strong tt u ) => 'Formatting'
+ handle_start %w( b big em font i s small strike strong tt u ) => 'Formatting'
+ handle_start 'nobr'
handle_start %w( area basefont bgsound br embed img param spacer wbr ) => 'VoidFormatting'
@@ -27,11 +32,15 @@ module HTML5lib
handle_end %w( address blockquote center div dl fieldset listing menu ol pre ul ) => 'Block'
+ handle_end HEADING_ELEMENTS => 'Heading'
+
handle_end %w( a b big em font i nobr s small strike strong tt u ) => 'Formatting'
handle_end %w( head frameset select optgroup option table caption colgroup col thead tfoot tbody tr td th ) => 'Misplaced'
- handle_end %w( area basefont bgsound br embed hr image img input isindex param spacer wbr frame ) => 'None'
+ handle_end 'br'
+
+ handle_end %w( area basefont bgsound embed hr image img input isindex param spacer wbr frame ) => 'None'
handle_end %w( noframes noscript noembed textarea xmp iframe ) => 'CdataTextAreaXmp'
@@ -41,14 +50,14 @@ module HTML5lib
super(parser, tree)
# for special handling of whitespace in
- @processSpaceCharactersPre = false
+ @processSpaceCharactersDropNewline = false
end
- def processSpaceCharactersPre(data)
+ def processSpaceCharactersDropNewline(data)
#Sometimes (start of blocks) we want to drop leading newlines
- @processSpaceCharactersPre = false
+ @processSpaceCharactersDropNewline = false
if (data.length > 0 and data[0] == ?\n and
- @tree.openElements[-1].name == 'pre' and
+ %w[pre textarea].include?(@tree.openElements[-1].name) and
not @tree.openElements[-1].hasContent)
data = data[1..-1]
end
@@ -56,8 +65,8 @@ module HTML5lib
end
def processSpaceCharacters(data)
- if @processSpaceCharactersPre
- processSpaceCharactersPre(data)
+ if @processSpaceCharactersDropNewline
+ processSpaceCharactersDropNewline(data)
else
super(data)
end
@@ -71,11 +80,11 @@ module HTML5lib
@tree.insertText(data)
end
- def startTagScriptStyle(name, attributes)
+ def startTagProcessInHead(name, attributes)
@parser.phases[:inHead].processStartTag(name, attributes)
end
- def startTagFromHead(name, attributes)
+ def startTagTitle(name, attributes)
@parser.parseError(_("Unexpected start tag (#{name}) that belongs in the head. Moved."))
@parser.phases[:inHead].processStartTag(name, attributes)
end
@@ -98,7 +107,7 @@ module HTML5lib
def startTagCloseP(name, attributes)
endTagP('p') if in_scope?('p')
@tree.insertElement(name, attributes)
- @processSpaceCharactersPre = true if name == 'pre'
+ @processSpaceCharactersDropNewline = true if name == 'pre'
end
def startTagForm(name, attributes)
@@ -118,7 +127,12 @@ module HTML5lib
@tree.openElements.reverse.each_with_index do |node, i|
if stopName.include?(node.name)
- (i + 1).times { @tree.openElements.pop }
+ poppedNodes = (0..i).collect { @tree.openElements.pop }
+ if i >= 1
+ @parser.parseError("Missing end tag%s (%s)" % [
+ (i>1 ? 's' : ''),
+ poppedNodes.reverse.map {|item| item.name}.join(', ')])
+ end
break
end
@@ -140,15 +154,19 @@ module HTML5lib
def startTagHeading(name, attributes)
endTagP('p') if in_scope?('p')
- HEADING_ELEMENTS.each do |element|
- if in_scope?(element)
- @parser.parseError(_("Unexpected start tag (#{name})."))
-
- remove_open_elements_until { |element| HEADING_ELEMENTS.include?(element.name) }
- break
- end
- end
+ # Uncomment the following for IE7 behavior:
+ # HEADING_ELEMENTS.each do |element|
+ # if in_scope?(element)
+ # @parser.parseError(_("Unexpected start tag (#{name})."))
+ #
+ # remove_open_elements_until do |element|
+ # HEADING_ELEMENTS.include?(element.name)
+ # end
+ #
+ # break
+ # end
+ # end
@tree.insertElement(name, attributes)
end
@@ -168,6 +186,12 @@ module HTML5lib
addFormattingElement(name, attributes)
end
+ def startTagNobr(name, attributes)
+ @tree.reconstructActiveFormattingElements
+ processEndTag('nobr') if in_scope?('nobr')
+ addFormattingElement(name, attributes)
+ end
+
def startTagButton(name, attributes)
if in_scope?('button')
@parser.parseError(_('Unexpected start tag (button) implied end tag (button).'))
@@ -248,6 +272,7 @@ module HTML5lib
# XXX Form element pointer checking here as well...
@tree.insertElement(name, attributes)
@parser.tokenizer.contentModelFlag = :RCDATA
+ @processSpaceCharactersDropNewline = true
end
# iframe, noembed noframes, noscript(if scripting enabled)
@@ -312,7 +337,7 @@ module HTML5lib
def endTagBlock(name)
#Put us back in the right whitespace handling mode
- @processSpaceCharactersPre = false if name == 'pre'
+ @processSpaceCharactersDropNewline = false if name == 'pre'
@tree.generateImpliedEndTags if in_scope?(name)
@@ -494,6 +519,13 @@ module HTML5lib
@parser.parseError(_("Unexpected end tag (#{name}). Ignored."))
end
+ def endTagBr(name)
+ @parser.parseError(_("Unexpected end tag (br). Treated as br element."))
+ @tree.reconstructActiveFormattingElements
+ @tree.insertElement(name, {})
+ @tree.openElements.pop()
+ end
+
def endTagNone(name)
# This handles elements with no end tag.
@parser.parseError(_("This tag (#{name}) has no end tag"))
@@ -545,4 +577,4 @@ module HTML5lib
end
end
-end
\ No newline at end of file
+end
diff --git a/vendor/plugins/HTML5lib/lib/html5lib/html5parser/in_head_phase.rb b/vendor/plugins/HTML5lib/lib/html5lib/html5parser/in_head_phase.rb
index 4060114a..20b37653 100644
--- a/vendor/plugins/HTML5lib/lib/html5lib/html5parser/in_head_phase.rb
+++ b/vendor/plugins/HTML5lib/lib/html5lib/html5parser/in_head_phase.rb
@@ -5,7 +5,9 @@ module HTML5lib
handle_start 'html', 'head', 'title', 'style', 'script', %w( base link meta )
- handle_end 'head', 'html', %w( title style script )
+ handle_end 'head'
+ handle_end %w( html body br ) => 'ImplyAfterHead'
+ handle_end %w( title style script )
def processEOF
if ['title', 'style', 'script'].include?(name = @tree.openElements[-1].name)
@@ -63,7 +65,11 @@ module HTML5lib
def startTagBaseLinkMeta(name, attributes)
element = @tree.createElement(name, attributes)
- appendToHead(element)
+ if @tree.headPointer != nil and @parser.phase == @parser.phases[:inHead]
+ appendToHead(element)
+ else
+ @tree.openElements[-1].appendChild(element)
+ end
end
def startTagOther(name, attributes)
@@ -80,7 +86,7 @@ module HTML5lib
@parser.phase = @parser.phases[:afterHead]
end
- def endTagHtml(name)
+ def endTagImplyAfterHead(name)
anythingElse
@parser.phase.processEndTag(name)
end
@@ -117,4 +123,4 @@ module HTML5lib
end
end
-end
\ No newline at end of file
+end
diff --git a/vendor/plugins/HTML5lib/lib/html5lib/html5parser/in_table_phase.rb b/vendor/plugins/HTML5lib/lib/html5lib/html5parser/in_table_phase.rb
index c4b86039..be38c53e 100644
--- a/vendor/plugins/HTML5lib/lib/html5lib/html5parser/in_table_phase.rb
+++ b/vendor/plugins/HTML5lib/lib/html5lib/html5parser/in_table_phase.rb
@@ -89,10 +89,10 @@ module HTML5lib
def endTagOther(name)
@parser.parseError(_("Unexpected end tag (#{name}) in table context caused voodoo mode."))
# Make all the special element rearranging voodoo kick in
- @parser.insertFromTable = true
+ @tree.insertFromTable = true
# Process the end tag in the "in body" mode
@parser.phases[:inBody].processEndTag(name)
- @parser.insertFromTable = false
+ @tree.insertFromTable = false
end
protected
@@ -107,4 +107,4 @@ module HTML5lib
end
end
-end
\ No newline at end of file
+end
diff --git a/vendor/plugins/HTML5lib/lib/html5lib/html5parser/initial_phase.rb b/vendor/plugins/HTML5lib/lib/html5lib/html5parser/initial_phase.rb
index 9914543b..aeb0afdd 100644
--- a/vendor/plugins/HTML5lib/lib/html5lib/html5parser/initial_phase.rb
+++ b/vendor/plugins/HTML5lib/lib/html5lib/html5parser/initial_phase.rb
@@ -17,9 +17,95 @@ module HTML5lib
@tree.insertComment(data, @tree.document)
end
- def processDoctype(name, error)
- @parser.parseError(_('Erroneous DOCTYPE.')) if error
+ def processDoctype(name, publicId, systemId, correct)
+ if name.downcase != 'html' or publicId or systemId
+ @parser.parseError(_('Erroneous DOCTYPE.'))
+ end
+ # XXX need to update DOCTYPE tokens
@tree.insertDoctype(name)
+
+ publicId = publicId.to_s.upcase
+
+ if name.downcase != 'html'
+ # XXX quirks mode
+ else
+ if ["+//silmaril//dtd html pro v0r11 19970101//en",
+ "-//advasoft ltd//dtd html 3.0 aswedit + extensions//en",
+ "-//as//dtd html 3.0 aswedit + extensions//en",
+ "-//ietf//dtd html 2.0 level 1//en",
+ "-//ietf//dtd html 2.0 level 2//en",
+ "-//ietf//dtd html 2.0 strict level 1//en",
+ "-//ietf//dtd html 2.0 strict level 2//en",
+ "-//ietf//dtd html 2.0 strict//en",
+ "-//ietf//dtd html 2.0//en",
+ "-//ietf//dtd html 2.1e//en",
+ "-//ietf//dtd html 3.0//en",
+ "-//ietf//dtd html 3.0//en//",
+ "-//ietf//dtd html 3.2 final//en",
+ "-//ietf//dtd html 3.2//en",
+ "-//ietf//dtd html 3//en",
+ "-//ietf//dtd html level 0//en",
+ "-//ietf//dtd html level 0//en//2.0",
+ "-//ietf//dtd html level 1//en",
+ "-//ietf//dtd html level 1//en//2.0",
+ "-//ietf//dtd html level 2//en",
+ "-//ietf//dtd html level 2//en//2.0",
+ "-//ietf//dtd html level 3//en",
+ "-//ietf//dtd html level 3//en//3.0",
+ "-//ietf//dtd html strict level 0//en",
+ "-//ietf//dtd html strict level 0//en//2.0",
+ "-//ietf//dtd html strict level 1//en",
+ "-//ietf//dtd html strict level 1//en//2.0",
+ "-//ietf//dtd html strict level 2//en",
+ "-//ietf//dtd html strict level 2//en//2.0",
+ "-//ietf//dtd html strict level 3//en",
+ "-//ietf//dtd html strict level 3//en//3.0",
+ "-//ietf//dtd html strict//en",
+ "-//ietf//dtd html strict//en//2.0",
+ "-//ietf//dtd html strict//en//3.0",
+ "-//ietf//dtd html//en",
+ "-//ietf//dtd html//en//2.0",
+ "-//ietf//dtd html//en//3.0",
+ "-//metrius//dtd metrius presentational//en",
+ "-//microsoft//dtd internet explorer 2.0 html strict//en",
+ "-//microsoft//dtd internet explorer 2.0 html//en",
+ "-//microsoft//dtd internet explorer 2.0 tables//en",
+ "-//microsoft//dtd internet explorer 3.0 html strict//en",
+ "-//microsoft//dtd internet explorer 3.0 html//en",
+ "-//microsoft//dtd internet explorer 3.0 tables//en",
+ "-//netscape comm. corp.//dtd html//en",
+ "-//netscape comm. corp.//dtd strict html//en",
+ "-//o'reilly and associates//dtd html 2.0//en",
+ "-//o'reilly and associates//dtd html extended 1.0//en",
+ "-//spyglass//dtd html 2.0 extended//en",
+ "-//sq//dtd html 2.0 hotmetal + extensions//en",
+ "-//sun microsystems corp.//dtd hotjava html//en",
+ "-//sun microsystems corp.//dtd hotjava strict html//en",
+ "-//w3c//dtd html 3 1995-03-24//en",
+ "-//w3c//dtd html 3.2 draft//en",
+ "-//w3c//dtd html 3.2 final//en",
+ "-//w3c//dtd html 3.2//en",
+ "-//w3c//dtd html 3.2s draft//en",
+ "-//w3c//dtd html 4.0 frameset//en",
+ "-//w3c//dtd html 4.0 transitional//en",
+ "-//w3c//dtd html experimental 19960712//en",
+ "-//w3c//dtd html experimental 970421//en",
+ "-//w3c//dtd w3 html//en",
+ "-//w3o//dtd w3 html 3.0//en",
+ "-//w3o//dtd w3 html 3.0//en//",
+ "-//w3o//dtd w3 html strict 3.0//en//",
+ "-//webtechs//dtd mozilla html 2.0//en",
+ "-//webtechs//dtd mozilla html//en",
+ "-/w3c/dtd html 4.0 transitional/en",
+ "html"].include?(publicId) or
+ (systemId == nil and
+ ["-//w3c//dtd html 4.01 frameset//EN",
+ "-//w3c//dtd html 4.01 transitional//EN"].include?(publicId)) or
+ (systemId == "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd")
+ #XXX quirks mode
+ end
+ end
+
@parser.phase = @parser.phases[:rootElement]
end
@@ -46,4 +132,4 @@ module HTML5lib
end
end
-end
\ No newline at end of file
+end
diff --git a/vendor/plugins/HTML5lib/lib/html5lib/html5parser/phase.rb b/vendor/plugins/HTML5lib/lib/html5lib/html5parser/phase.rb
index 3a96b66f..d451eb37 100644
--- a/vendor/plugins/HTML5lib/lib/html5lib/html5parser/phase.rb
+++ b/vendor/plugins/HTML5lib/lib/html5lib/html5parser/phase.rb
@@ -101,7 +101,7 @@ module HTML5lib
@tree.insertComment(data, @tree.openElements[-1])
end
- def processDoctype(name, error)
+ def processDoctype(name, publicId, systemId, correct)
@parser.parseError(_('Unexpected DOCTYPE. Ignored.'))
end
@@ -153,4 +153,4 @@ module HTML5lib
end
end
-end
\ No newline at end of file
+end
diff --git a/vendor/plugins/HTML5lib/lib/html5lib/inputstream.rb b/vendor/plugins/HTML5lib/lib/html5lib/inputstream.rb
index 62cc9948..3abb5b67 100755
--- a/vendor/plugins/HTML5lib/lib/html5lib/inputstream.rb
+++ b/vendor/plugins/HTML5lib/lib/html5lib/inputstream.rb
@@ -33,9 +33,6 @@ module HTML5lib
options.each { |name, value| instance_variable_set("@#{name}", value) }
- # List of where new lines occur
- @new_lines = []
-
# Raw Stream
@raw_stream = open_stream(source)
@@ -55,25 +52,30 @@ module HTML5lib
# Read bytes from stream decoding them into Unicode
uString = @raw_stream.read
- unless @char_encoding == 'utf-8'
+ if @char_encoding == 'windows-1252'
+ @win1252 = true
+ elsif @char_encoding != 'utf-8'
begin
require 'iconv'
- uString = Iconv.iconv('utf-8', @encoding, uString)[0]
- rescue
+ begin
+ uString = Iconv.iconv('utf-8', @char_encoding, uString).first
+ rescue
+ @win1252 = true
+ end
+ rescue LoadError
+ @win1252 = true
end
end
- # Normalize newlines and null characters
- uString.gsub!(/\r\n?/, "\n")
- uString.gsub!("\x00", [0xFFFD].pack('U'))
-
# Convert the unicode string into a list to be used as the data stream
@data_stream = uString
@queue = []
# Reset position in the list to read from
- reset
+ @tell = 0
+ @line = @col = 0
+ @line_lengths = []
end
# Produces a file object from source.
@@ -95,11 +97,13 @@ module HTML5lib
#First look for a BOM
#This will also read past the BOM if present
encoding = detect_bom
+
#If there is no BOM need to look for meta elements with encoding
#information
if encoding.nil? and @parse_meta
encoding = detect_encoding_meta
end
+
#Guess with chardet, if avaliable
if encoding.nil? and @chardet
begin
@@ -107,17 +111,18 @@ module HTML5lib
require 'UniversalDetector' # gem install chardet
buffer = @raw_stream.read
encoding = UniversalDetector::chardet(buffer)['encoding']
- @raw_stream = open_stream(buffer)
+ seek(buffer, 0)
rescue LoadError
end
end
+
# If all else fails use the default encoding
if encoding.nil?
encoding = @DEFAULT_ENCODING
end
- #Substitute for equivalent encodings:
- encoding_sub = {'ascii' => 'windows-1252', 'iso-8859-1' => 'windows-1252'}
+ #Substitute for equivalent encodings
+ encoding_sub = {'iso-8859-1' => 'windows-1252'}
if encoding_sub.has_key?(encoding.downcase)
encoding = encoding_sub[encoding.downcase]
@@ -132,14 +137,13 @@ module HTML5lib
def detect_bom
bom_dict = {
"\xef\xbb\xbf" => 'utf-8',
- "\xff\xfe" => 'utf-16-le',
- "\xfe\xff" => 'utf-16-be',
- "\xff\xfe\x00\x00" => 'utf-32-le',
- "\x00\x00\xfe\xff" => 'utf-32-be'
+ "\xff\xfe" => 'utf-16le',
+ "\xfe\xff" => 'utf-16be',
+ "\xff\xfe\x00\x00" => 'utf-32le',
+ "\x00\x00\xfe\xff" => 'utf-32be'
}
# Go to beginning of file and read in 4 bytes
- @raw_stream.seek(0)
string = @raw_stream.read(4)
return nil unless string
@@ -156,45 +160,80 @@ module HTML5lib
end
end
- #AT - move this to the caller?
# Set the read position past the BOM if one was found, otherwise
# set it to the start of the stream
- @raw_stream.seek(encoding ? seek : 0)
+ seek(string, encoding ? seek : 0)
return encoding
end
- # Report the encoding declared by the meta element
- def detect_encoding_meta
- parser = EncodingParser.new(@raw_stream.read(@NUM_BYTES_META))
- @raw_stream.seek(0)
- return parser.get_encoding
+ def seek(buffer, n)
+ if @raw_stream.respond_to?(:unget)
+ @raw_stream.unget(buffer[n..-1])
+ return
+ end
+
+ if @raw_stream.respond_to?(:seek)
+ begin
+ @raw_stream.seek(n)
+ return
+ rescue Errno::ESPIPE
+ end
+ end
+
+ require 'delegate'
+ @raw_stream = SimpleDelegator.new(@raw_stream)
+
+ class << @raw_stream
+ def read(chars=-1)
+ if chars == -1 or chars > @data.length
+ result = @data
+ @data = ''
+ return result if __getobj__.eof?
+ return result + __getobj__.read if chars == -1
+ return result + __getobj__.read(chars-result.length)
+ elsif @data.empty?
+ return __getobj__.read(chars)
+ else
+ result = @data[1...chars]
+ @data = @data[chars..-1]
+ return result
+ end
+ end
+
+ def unget(data)
+ if !@data or @data.empty?
+ @data = data
+ else
+ @data += data
+ end
+ end
+ end
+
+ @raw_stream.unget(buffer[n .. -1])
end
- def determine_new_lines
- # Looks through the stream to find where new lines occur so
- # the position method can tell where it is.
- @new_lines.push(0)
- (0...@data_stream.length).each { |i| @new_lines.push(i) if @data_stream[i] == ?\n }
+ # Report the encoding declared by the meta element
+ def detect_encoding_meta
+ buffer = @raw_stream.read(@NUM_BYTES_META)
+ parser = EncodingParser.new(buffer)
+ seek(buffer, 0)
+ return parser.get_encoding
end
# Returns (line, col) of the current position in the stream.
def position
- # Generate list of new lines first time around
- determine_new_lines if @new_lines.empty?
- line = 0
- tell = @tell
- @new_lines.each do |pos|
- break unless pos < tell
- line += 1
+ line, col = @line, @col
+ @queue.reverse.each do |c|
+ if c == "\n"
+ line -= 1
+ raise RuntimeError.new("col=#{col}") unless col == 0
+ col = @line_lengths[line]
+ else
+ col -= 1
+ end
end
- col = tell - @new_lines[line-1] - 1
- return [line, col]
- end
-
- # Resets the position in the stream back to the start.
- def reset
- @tell = 0
+ return [line+1, col]
end
# Read one character from the stream or queue if available. Return
@@ -203,11 +242,60 @@ module HTML5lib
unless @queue.empty?
return @queue.shift
else
- begin
- @tell += 1
- return @data_stream[@tell - 1].chr
- rescue
- return :EOF
+ c = @data_stream[@tell]
+ @tell += 1
+
+ case c
+ when 0x01 .. 0x7F
+ if c == 0x0D
+ # normalize newlines
+ @tell += 1 if @data_stream[@tell] == 0x0A
+ c = 0x0A
+ end
+
+ # update position in stream
+ if c == 0x0a
+ @line_lengths << @col
+ @line += 1
+ @col = 0
+ else
+ @col += 1
+ end
+
+ c.chr
+
+ when 0x80 .. 0xBF
+ if !@win1252
+ [0xFFFD].pack('U') # invalid utf-8
+ elsif c <= 0x9f
+ [ENTITIES_WINDOWS1252[c-0x80]].pack('U')
+ else
+ "\xC2" + c.chr # convert to utf-8
+ end
+
+ when 0xC0 .. 0xFF
+ if @win1252
+ "\xC3" + (c-64).chr # convert to utf-8
+ elsif @data_stream[@tell-1 .. -1] =~ /^
+ ( [\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte
+ | \xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs
+ | [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte
+ | \xED[\x80-\x9F][\x80-\xBF] # excluding surrogates
+ | \xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3
+ | [\xF1-\xF3][\x80-\xBF]{3} # planes 4-15
+ | \xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16
+ )/x
+ @tell += $1.length - 1
+ $1
+ else
+ [0xFFFD].pack('U') # invalid utf-8
+ end
+
+ when 0x00
+ [0xFFFD].pack('U') # null characters are invalid
+
+ else
+ :EOF
end
end
end
@@ -218,28 +306,15 @@ module HTML5lib
def chars_until(characters, opposite=false)
char_stack = [char]
- unless char_stack[0] == :EOF
- while (characters.include? char_stack[-1]) == opposite
- unless @queue.empty?
- # First from the queue
- char_stack.push(@queue.shift)
- break if char_stack[-1] == :EOF
- else
- # Then the rest
- begin
- char_stack.push(@data_stream[@tell].chr)
- @tell += 1
- rescue
- char_stack.push(:EOF)
- break
- end
- end
- end
+ while char_stack.last != :EOF
+ break unless (characters.include?(char_stack.last)) == opposite
+ char_stack.push(char)
end
# Put the character stopped on back to the front of the queue
# from where it came.
- @queue.insert(0, char_stack.pop)
+ c = char_stack.pop
+ @queue.insert(0, c) unless c == :EOF
return char_stack.join('')
end
end
@@ -428,7 +503,7 @@ module HTML5lib
space_found = false
#Step 5 attribute name
while true
- if @data.current_byte == '=' and attr_name:
+ if @data.current_byte == '=' and attr_name
break
elsif SPACE_CHARACTERS.include?(@data.current_byte)
space_found = true
diff --git a/vendor/plugins/HTML5lib/lib/html5lib/liberalxmlparser.rb b/vendor/plugins/HTML5lib/lib/html5lib/liberalxmlparser.rb
index 5410b98e..bbcf0eac 100755
--- a/vendor/plugins/HTML5lib/lib/html5lib/liberalxmlparser.rb
+++ b/vendor/plugins/HTML5lib/lib/html5lib/liberalxmlparser.rb
@@ -69,15 +69,22 @@ module HTML5lib
# ensure that non-void XHTML elements have content so that separate
# open and close tags are emitted
- if token[:type] == :EndTag and \
- not VOID_ELEMENTS.include? token[:name] and \
- token[:name] == @tree.openElements[-1].name and \
- not @tree.openElements[-1].hasContent
- @tree.insertText('') unless
- @tree.openElements.any? {|e|
- e.attributes.keys.include? 'xmlns' and
- e.attributes['xmlns'] != 'http://www.w3.org/1999/xhtml'
- }
+ if token[:type] == :EndTag
+ if VOID_ELEMENTS.include? token[:name]
+ if @tree.openElements[-1].name != token["name"]:
+ token[:type] = :EmptyTag
+ token["data"] ||= {}
+ end
+ else
+ if token[:name] == @tree.openElements[-1].name and \
+ not @tree.openElements[-1].hasContent
+ @tree.insertText('') unless
+ @tree.openElements.any? {|e|
+ e.attributes.keys.include? 'xmlns' and
+ e.attributes['xmlns'] != 'http://www.w3.org/1999/xhtml'
+ }
+ end
+ end
end
return token
diff --git a/vendor/plugins/HTML5lib/lib/html5lib/sanitizer.rb b/vendor/plugins/HTML5lib/lib/html5lib/sanitizer.rb
index 6f7cdcac..5af9cf51 100644
--- a/vendor/plugins/HTML5lib/lib/html5lib/sanitizer.rb
+++ b/vendor/plugins/HTML5lib/lib/html5lib/sanitizer.rb
@@ -1,12 +1,22 @@
-require 'html5lib/tokenizer'
require 'cgi'
module HTML5lib
# This module provides sanitization of XHTML+MathML+SVG
# and of inline style attributes.
+#
+# It can be either at the Tokenizer stage:
+#
+# HTMLParser.parse(html, :tokenizer => HTMLSanitizer)
+#
+# or, if you already have a parse tree (in this example, a REXML tree),
+# at the Serializer stage:
+#
+# tokens = TreeWalkers.getTreeWalker('rexml').new(tree)
+# HTMLSerializer.serialize(tokens, {:encoding=>'utf-8',
+# :sanitize => true})
- class HTMLSanitizer < HTMLTokenizer
+ module HTMLSanitizeModule
ACCEPTABLE_ELEMENTS = %w[a abbr acronym address area b big blockquote br
button caption center cite code col colgroup dd del dfn dir div dl dt
@@ -64,7 +74,7 @@ module HTML5lib
xlink:show xlink:title xlink:type xml:base xml:lang xml:space xmlns
xmlns:xlink y y1 y2 zoomAndPan]
- ATTR_VAL_IS_URI = %w[href src cite action longdesc xlink:href]
+ ATTR_VAL_IS_URI = %w[href src cite action longdesc xlink:href xml:base]
ACCEPTABLE_CSS_PROPERTIES = %w[azimuth background-color
border-bottom-color border-collapse border-color border-left-color
@@ -96,19 +106,7 @@ module HTML5lib
ALLOWED_SVG_PROPERTIES = ACCEPTABLE_SVG_PROPERTIES
ALLOWED_PROTOCOLS = ACCEPTABLE_PROTOCOLS
- # Sanitize the +html+, escaping all elements not in ALLOWED_ELEMENTS, and
- # stripping out all # attributes not in ALLOWED_ATTRIBUTES. Style
- # attributes are parsed, and a restricted set, # specified by
- # ALLOWED_CSS_PROPERTIES and ALLOWED_CSS_KEYWORDS, are allowed through.
- # attributes in ATTR_VAL_IS_URI are scanned, and only URI schemes specified
- # in ALLOWED_PROTOCOLS are allowed.
- #
- # sanitize_html('')
- # => <script> do_nasty_stuff() </script>
- # sanitize_html('Click here for $100')
- # => Click here for $100
- def each
- super do |token|
+ def sanitize_token(token)
case token[:type]
when :StartTag, :EndTag, :EmptyTag
if ALLOWED_ELEMENTS.include?(token[:name])
@@ -116,7 +114,7 @@ module HTML5lib
attrs = Hash[*token[:data].flatten]
attrs.delete_if { |attr,v| !ALLOWED_ATTRIBUTES.include?(attr) }
ATTR_VAL_IS_URI.each do |attr|
- val_unescaped = CGI.unescapeHTML(attrs[attr].to_s).gsub(/[\000-\040\177\s]+|\302[\200-\240]/,'').downcase
+ val_unescaped = CGI.unescapeHTML(attrs[attr].to_s).gsub(/`|[\000-\040\177\s]+|\302[\200-\240]/,'').downcase
if val_unescaped =~ /^[a-z0-9][-+.a-z0-9]*:/ and !ALLOWED_PROTOCOLS.include?(val_unescaped.split(':')[0])
attrs.delete attr
end
@@ -126,7 +124,7 @@ module HTML5lib
end
token[:data] = attrs.map {|k,v| [k,v]}
end
- yield token
+ return token
else
if token[:type] == :EndTag
token[:data] = "#{token[:name]}>"
@@ -139,12 +137,14 @@ module HTML5lib
token[:data].insert(-2,'/') if token[:type] == :EmptyTag
token[:type] = :Characters
token.delete(:name)
- yield token
+ return token
end
+ when :Comment
+ token[:data] = ""
+ return token
else
- yield token
+ return token
end
- end
end
def sanitize_css(style)
@@ -174,4 +174,14 @@ module HTML5lib
style = clean.join(' ')
end
end
+
+ class HTMLSanitizer < HTMLTokenizer
+ include HTMLSanitizeModule
+ def each
+ super do |token|
+ yield(sanitize_token(token))
+ end
+ end
+ end
+
end
diff --git a/vendor/plugins/HTML5lib/lib/html5lib/serializer.rb b/vendor/plugins/HTML5lib/lib/html5lib/serializer.rb
new file mode 100644
index 00000000..cd4c66a6
--- /dev/null
+++ b/vendor/plugins/HTML5lib/lib/html5lib/serializer.rb
@@ -0,0 +1,2 @@
+require 'html5lib/serializer/htmlserializer'
+require 'html5lib/serializer/xhtmlserializer'
diff --git a/vendor/plugins/HTML5lib/lib/html5lib/serializer/htmlserializer.rb b/vendor/plugins/HTML5lib/lib/html5lib/serializer/htmlserializer.rb
new file mode 100644
index 00000000..a03b7d79
--- /dev/null
+++ b/vendor/plugins/HTML5lib/lib/html5lib/serializer/htmlserializer.rb
@@ -0,0 +1,177 @@
+require 'html5lib/constants'
+
+module HTML5lib
+
+ class HTMLSerializer
+
+ def self.serialize(stream, options = {})
+ new(options).serialize(stream, options[:encoding])
+ end
+
+ def escape(string)
+ string.gsub("&", "&").gsub("<", "<").gsub(">", ">")
+ end
+
+ def initialize(options={})
+ @quote_attr_values = false
+ @quote_char = '"'
+ @use_best_quote_char = true
+ @minimize_boolean_attributes = true
+
+ @use_trailing_solidus = false
+ @space_before_trailing_solidus = true
+ @escape_lt_in_attrs = false
+
+ @omit_optional_tags = true
+ @sanitize = false
+
+ @strip_whitespace = false
+
+ @inject_meta_charset = true
+
+ options.each do |name, value|
+ next unless instance_variables.include?("@#{name}")
+ @use_best_quote_char = false if name.to_s == 'quote_char'
+ instance_variable_set("@#{name}", value)
+ end
+
+ @errors = []
+ end
+
+ def serialize(treewalker, encoding=nil)
+ in_cdata = false
+ @errors = []
+
+ if encoding and @inject_meta_charset
+ require 'html5lib/filters/inject_meta_charset'
+ treewalker = Filters::InjectMetaCharset.new(treewalker, encoding)
+ end
+
+ if @strip_whitespace
+ require 'html5lib/filters/whitespace'
+ treewalker = Filters::WhitespaceFilter.new(treewalker)
+ end
+
+ if @sanitize
+ require 'html5lib/filters/sanitizer'
+ treewalker = Filters::HTMLSanitizeFilter.new(treewalker)
+ end
+
+ if @omit_optional_tags
+ require 'html5lib/filters/optionaltags'
+ treewalker = Filters::OptionalTagFilter.new(treewalker)
+ end
+
+ result = []
+ treewalker.each do |token|
+ type = token[:type]
+ if type == :Doctype
+ doctype = "" % token[:name]
+ result << doctype
+
+ elsif [:Characters, :SpaceCharacters].include? type
+ if type == :SpaceCharacters or in_cdata
+ if in_cdata and token[:data].include?("")
+ serializeError(_("Unexpected in CDATA"))
+ end
+ result << token[:data]
+ else
+ result << escape(token[:data])
+ end
+
+ elsif [:StartTag, :EmptyTag].include? type
+ name = token[:name]
+ if RCDATA_ELEMENTS.include?(name)
+ in_cdata = true
+ elsif in_cdata
+ serializeError(_("Unexpected child element of a CDATA element"))
+ end
+ attributes = []
+ for k,v in attrs = token[:data].to_a.sort
+ attributes << ' '
+
+ attributes << k
+ if not @minimize_boolean_attributes or \
+ (!(BOOLEAN_ATTRIBUTES[name]||[]).include?(k) \
+ and !BOOLEAN_ATTRIBUTES[:global].include?(k))
+ attributes << "="
+ if @quote_attr_values or v.empty?
+ quote_attr = true
+ else
+ quote_attr = (SPACE_CHARACTERS + %w(< > " ')).any? {|c| v.include?(c)}
+ end
+ v = v.gsub("&", "&")
+ v = v.gsub("<", "<") if @escape_lt_in_attrs
+ if quote_attr
+ quote_char = @quote_char
+ if @use_best_quote_char
+ if v.index("'") and !v.index('"')
+ quote_char = '"'
+ elsif v.index('"') and !v.index("'")
+ quote_char = "'"
+ end
+ end
+ if quote_char == "'"
+ v = v.gsub("'", "'")
+ else
+ v = v.gsub('"', """)
+ end
+ attributes << quote_char << v << quote_char
+ else
+ attributes << v
+ end
+ end
+ end
+ if VOID_ELEMENTS.include?(name) and @use_trailing_solidus
+ if @space_before_trailing_solidus
+ attributes << " /"
+ else
+ attributes << "/"
+ end
+ end
+ result << "<%s%s>" % [name, attributes.join('')]
+
+ elsif type == :EndTag
+ name = token[:name]
+ if RCDATA_ELEMENTS.include?(name)
+ in_cdata = false
+ elsif in_cdata
+ serializeError(_("Unexpected child element of a CDATA element"))
+ end
+ end_tag = "#{name}>"
+ result << end_tag
+
+ elsif type == :Comment
+ data = token[:data]
+ serializeError(_("Comment contains --")) if data.index("--")
+ comment = "" % token[:data]
+ result << comment
+
+ else
+ serializeError(token[:data])
+ end
+ end
+
+ if encoding and encoding != 'utf-8'
+ require 'iconv'
+ Iconv.iconv(encoding, 'utf-8', result.join('')).first
+ else
+ result.join('')
+ end
+ end
+
+ alias :render :serialize
+
+ def serializeError(data="XXX ERROR MESSAGE NEEDED")
+ # XXX The idea is to make data mandatory.
+ @errors.push(data)
+ if @strict
+ raise SerializeError
+ end
+ end
+ end
+
+ # Error in serialized tree
+ class SerializeError < Exception
+ end
+end
diff --git a/vendor/plugins/HTML5lib/lib/html5lib/serializer/xhtmlserializer.rb b/vendor/plugins/HTML5lib/lib/html5lib/serializer/xhtmlserializer.rb
new file mode 100644
index 00000000..43a63788
--- /dev/null
+++ b/vendor/plugins/HTML5lib/lib/html5lib/serializer/xhtmlserializer.rb
@@ -0,0 +1,19 @@
+require 'html5lib/serializer/htmlserializer'
+
+module HTML5lib
+
+ class XHTMLSerializer < HTMLSerializer
+ DEFAULTS = {
+ :quote_attr_values => true,
+ :minimize_boolean_attributes => false,
+ :use_trailing_solidus => true,
+ :escape_lt_in_attrs => true,
+ :omit_optional_tags => false
+ }
+
+ def initialize(options={})
+ super(DEFAULTS.clone.update(options))
+ end
+ end
+
+end
diff --git a/vendor/plugins/HTML5lib/lib/html5lib/tokenizer.rb b/vendor/plugins/HTML5lib/lib/html5lib/tokenizer.rb
index 4c99b10d..6519944d 100644
--- a/vendor/plugins/HTML5lib/lib/html5lib/tokenizer.rb
+++ b/vendor/plugins/HTML5lib/lib/html5lib/tokenizer.rb
@@ -41,19 +41,31 @@ module HTML5lib
:attributeValueUnQuoted => :attributeValueUnQuotedState,
:bogusComment => :bogusCommentState,
:markupDeclarationOpen => :markupDeclarationOpenState,
+ :commentStart => :commentStartState,
+ :commentStartDash => :commentStartDashState,
:comment => :commentState,
- :commentDash => :commentDashState,
+ :commentEndDash => :commentEndDashState,
:commentEnd => :commentEndState,
:doctype => :doctypeState,
:beforeDoctypeName => :beforeDoctypeNameState,
:doctypeName => :doctypeNameState,
:afterDoctypeName => :afterDoctypeNameState,
+ :beforeDoctypePublicIdentifier => :beforeDoctypePublicIdentifierState,
+ :doctypePublicIdentifierDoubleQuoted => :doctypePublicIdentifierDoubleQuotedState,
+ :doctypePublicIdentifierSingleQuoted => :doctypePublicIdentifierSingleQuotedState,
+ :afterDoctypePublicIdentifier => :afterDoctypePublicIdentifierState,
+ :beforeDoctypeSystemIdentifier => :beforeDoctypeSystemIdentifierState,
+ :doctypeSystemIdentifierDoubleQuoted => :doctypeSystemIdentifierDoubleQuotedState,
+ :doctypeSystemIdentifierSingleQuoted => :doctypeSystemIdentifierSingleQuotedState,
+ :afterDoctypeSystemIdentifier => :afterDoctypeSystemIdentifierState,
:bogusDoctype => :bogusDoctypeState
}
# Setup the initial tokenizer state
@contentModelFlag = :PCDATA
@state = @states[:data]
+ @escapeFlag = false
+ @lastFourChars = []
# The current token being created
@currentToken = nil
@@ -68,7 +80,6 @@ module HTML5lib
# to return we yield the token which pauses processing until the next token
# is requested.
def each
- @stream.reset
@tokenQueue = []
# Start processing. When EOF is reached @state will return false
# instead of true and the loop will terminate.
@@ -134,24 +145,14 @@ module HTML5lib
# If the integer is between 127 and 160 (so 128 and bigger and 159 and
# smaller) we need to do the "windows trick".
if (127...160).include? charAsInt
- #XXX - removed parse error from windows 1252 entity for now
- #we may want to reenable this later
- #@tokenQueue.push({:type => :ParseError, :data =>
- # _("Entity used with illegal number (windows-1252 reference).")})
+ @tokenQueue.push({:type => :ParseError, :data =>
+ _("Entity used with illegal number (windows-1252 reference).")})
charAsInt = ENTITIES_WINDOWS1252[charAsInt - 128]
end
- # 0 is not a good number.
- if charAsInt == 0
- charAsInt = 65533
- end
-
- if charAsInt <= 0x10FFFF
+ if charAsInt > 0 and charAsInt <= 1114111
char = [charAsInt].pack('U')
- else
- @tokenQueue.push({:type => :ParseError, :data =>
- _("Numeric entity couldn't be converted to character.")})
end
# Discard the ; if present. Otherwise, put it back on the queue and
@@ -168,7 +169,10 @@ module HTML5lib
def consumeEntity
char = nil
charStack = [@stream.char]
- if charStack[0] == "#"
+ if SPACE_CHARACTERS.include?(charStack[0]) or
+ [:EOF, '<', '&'].include?(charStack[0])
+ @stream.queue+= charStack
+ elsif charStack[0] == "#"
# We might have a number entity here.
charStack += [@stream.char, @stream.char]
if charStack.include? :EOF
@@ -195,10 +199,6 @@ module HTML5lib
_("Numeric entity expected but none found.")})
end
end
- # Break out if we reach the end of the file
- elsif charStack[0] == :EOF
- @tokenQueue.push({:type => :ParseError, :data =>
- _("Entity expected. Got end of file instead.")})
else
# At this point in the process might have named entity. Entities
# are stored in the global variable "entities".
@@ -268,14 +268,33 @@ module HTML5lib
# statements should be.
def dataState
data = @stream.char
- if data == "&" and (@contentModelFlag == :PCDATA or
- @contentModelFlag == :RCDATA)
+
+ if @contentModelFlag == :CDATA or @contentModelFlag == :RCDATA
+ @lastFourChars << data
+ @lastFourChars.shift if @lastFourChars.length > 4
+ end
+
+ if data == "&" and [:PCDATA,:RCDATA].include?(@contentModelFlag)
@state = @states[:entityData]
- elsif data == "<" and @contentModelFlag != :PLAINTEXT
- @state = @states[:tagOpen]
+
+ elsif data == "-" and [:CDATA,:RCDATA].include?(@contentModelFlag) and
+ @escapeFlag == false and @lastFourChars.join('') == ""
+ @escapeFlag = false
+ @tokenQueue.push({:type => :Characters, :data => data})
+
elsif data == :EOF
# Tokenization ends.
return false
+
elsif SPACE_CHARACTERS.include? data
# Directly after emitting a token you switch back to the "data
# state". At that point SPACE_CHARACTERS are important so they are
@@ -286,7 +305,7 @@ module HTML5lib
data + @stream.chars_until(SPACE_CHARACTERS, true)})
else
@tokenQueue.push({:type => :Characters, :data =>
- data + @stream.chars_until(["&", "<"])})
+ data + @stream.chars_until(%w[& < > -])})
end
return true
end
@@ -381,8 +400,6 @@ module HTML5lib
# emitting the end tag token.
@contentModelFlag = :PCDATA
else
- @tokenQueue.push({:type => :ParseError, :data =>
- _("Expected closing tag after seeing ''. None found.")})
@tokenQueue.push({:type => :Characters, :data => ""})
@state = @states[:data]
@@ -392,29 +409,27 @@ module HTML5lib
end
end
- if @contentModelFlag == :PCDATA
- data = @stream.char
- if data == :EOF
- @tokenQueue.push({:type => :ParseError, :data =>
- _("Expected closing tag. Unexpected end of file.")})
- @tokenQueue.push({:type => :Characters, :data => ""})
- @state = @states[:data]
- elsif ASCII_LETTERS.include? data
- @currentToken =\
- {:type => :EndTag, :name => data, :data => []}
- @state = @states[:tagName]
- elsif data == ">"
- @tokenQueue.push({:type => :ParseError, :data =>
- _("Expected closing tag. Got '>' instead. Ignoring '>'.")})
- @state = @states[:data]
- else
- # XXX data can be _'_...
- @tokenQueue.push({:type => :ParseError, :data =>
- _("Expected closing tag. Unexpected character '" + data + "' found.")})
- @stream.queue.push(data)
- @state = @states[:bogusComment]
- end
+ data = @stream.char
+ if data == :EOF
+ @tokenQueue.push({:type => :ParseError, :data =>
+ _("Expected closing tag. Unexpected end of file.")})
+ @tokenQueue.push({:type => :Characters, :data => ""})
+ @state = @states[:data]
+ elsif ASCII_LETTERS.include? data
+ @currentToken = {:type => :EndTag, :name => data, :data => []}
+ @state = @states[:tagName]
+ elsif data == ">"
+ @tokenQueue.push({:type => :ParseError, :data =>
+ _("Expected closing tag. Got '>' instead. Ignoring '>'.")})
+ @state = @states[:data]
+ else
+ # XXX data can be _'_...
+ @tokenQueue.push({:type => :ParseError, :data =>
+ _("Expected closing tag. Unexpected character '#{data}' found.")})
+ @stream.queue.push(data)
+ @state = @states[:bogusComment]
end
+
return true
end
@@ -431,11 +446,6 @@ module HTML5lib
@stream.chars_until(ASCII_LETTERS, true)
elsif data == ">"
emitCurrentToken
- elsif data == "<"
- @stream.queue.push(data)
- @tokenQueue.push({:type => :ParseError, :data =>
- _("Unexpected < character when getting the tag name.")})
- emitCurrentToken
elsif data == "/"
processSolidusInTag
@state = @states[:beforeAttributeName]
@@ -460,11 +470,6 @@ module HTML5lib
emitCurrentToken
elsif data == "/"
processSolidusInTag
- elsif data == "<"
- @stream.queue.push(data)
- @tokenQueue.push({:type => :ParseError, :data =>
- _("Unexpected < character. Expected attribute name instead.")})
- emitCurrentToken
else
@currentToken[:data].push([data, ""])
@state = @states[:attributeName]
@@ -495,12 +500,6 @@ module HTML5lib
elsif data == "/"
processSolidusInTag
@state = @states[:beforeAttributeName]
- elsif data == "<"
- @stream.queue.push(data)
- @tokenQueue.push({:type => :ParseError, :data =>
- _("Unexpected < character in attribute name.")})
- emitCurrentToken
- leavingThisState = false
else
@currentToken[:data][-1][0] += data
leavingThisState = false
@@ -538,11 +537,6 @@ module HTML5lib
elsif data == "/"
processSolidusInTag
@state = @states[:beforeAttributeName]
- elsif data == "<"
- @stream.queue.push(data)
- @tokenQueue.push({:type => :ParseError, :data =>
- _("Unexpected < character. Expected = or end of tag.")})
- emitCurrentToken
elsif data == :EOF
@tokenQueue.push({:type => :ParseError, :data =>
_("Unexpected end of file. Expected = or end of tag.")})
@@ -567,11 +561,6 @@ module HTML5lib
@state = @states[:attributeValueSingleQuoted]
elsif data == ">"
emitCurrentToken
- elsif data == "<"
- @stream.queue.push(data)
- @tokenQueue.push({:type => :ParseError, :data =>
- _("Unexpected < character. Expected attribute value.")})
- emitCurrentToken
elsif data == :EOF
@tokenQueue.push({:type => :ParseError, :data =>
_("Unexpected end of file. Expected attribute value.")})
@@ -625,11 +614,6 @@ module HTML5lib
processEntityInAttribute
elsif data == ">"
emitCurrentToken
- elsif data == "<"
- @stream.queue.push(data)
- @tokenQueue.push({:type => :ParseError, :data =>
- _("Unexpected < character in attribute value.")})
- emitCurrentToken
elsif data == :EOF
@tokenQueue.push({:type => :ParseError, :data =>
_("Unexpected end of file in attribute value.")})
@@ -659,14 +643,15 @@ module HTML5lib
charStack = [@stream.char, @stream.char]
if charStack == ["-", "-"]
@currentToken = {:type => :Comment, :data => ""}
- @state = @states[:comment]
+ @state = @states[:commentStart]
else
5.times { charStack.push(@stream.char) }
# Put in explicit :EOF check
if ((not charStack.include? :EOF) and
charStack.join("").upcase == "DOCTYPE")
@currentToken =\
- {:type => :Doctype, :name => "", :data => true}
+ {:type => :Doctype, :name => "",
+ :publicId => nil, :systemId => nil, :correct => true}
@state = @states[:doctype]
else
@tokenQueue.push({:type => :ParseError, :data =>
@@ -678,10 +663,52 @@ module HTML5lib
return true
end
+ def commentStartState
+ data = @stream.char
+ if data == "-"
+ @state = @states[:commentStartDash]
+ elsif data == ">"
+ @tokenQueue.push({:type => :ParseError, :data =>
+ _("Incorrect comment.")})
+ @tokenQueue.push(@currentToken)
+ @state = @states[:data]
+ elsif data == EOF
+ @tokenQueue.push({:type => :ParseError, :data =>
+ _("Unexpected end of file in comment.")})
+ @tokenQueue.push(@currentToken)
+ @state = @states[:data]
+ else
+ @currentToken[:data] += data + @stream.chars_until("-")
+ @state = @states[:comment]
+ end
+ return true
+ end
+
+ def commentStartDashState
+ data = @stream.char
+ if data == "-"
+ @state = @states[:commentEnd]
+ elsif data == ">"
+ @tokenQueue.push({:type => :ParseError, :data =>
+ _("Incorrect comment.")})
+ @tokenQueue.push(@currentToken)
+ @state = @states[:data]
+ elsif data == EOF
+ @tokenQueue.push({:type => :ParseError, :data =>
+ _("Unexpected end of file in comment.")})
+ @tokenQueue.push(@currentToken)
+ @state = @states[:data]
+ else
+ @currentToken[:data] += data + @stream.chars_until("-")
+ @state = @states[:comment]
+ end
+ return true
+ end
+
def commentState
data = @stream.char
if data == "-"
- @state = @states[:commentDash]
+ @state = @states[:commentEndDash]
elsif data == :EOF
@tokenQueue.push({:type => :ParseError, :data =>
_("Unexpected end of file in comment.")})
@@ -693,7 +720,7 @@ module HTML5lib
return true
end
- def commentDashState
+ def commentEndDashState
data = @stream.char
if data == "-"
@state = @states[:commentEnd]
@@ -753,19 +780,16 @@ module HTML5lib
def beforeDoctypeNameState
data = @stream.char
if SPACE_CHARACTERS.include? data
- elsif ASCII_LOWERCASE.include? data
- @currentToken[:name] = data.upcase
- @state = @states[:doctypeName]
elsif data == ">"
- # Character needs to be consumed per the specification so don't
- # invoke emitCurrentTokenWithParseError with :data as argument.
@tokenQueue.push({:type => :ParseError, :data =>
_("Unexpected > character. Expected DOCTYPE name.")})
+ @currentToken[:correct] = false
@tokenQueue.push(@currentToken)
@state = @states[:data]
elsif data == :EOF
@tokenQueue.push({:type => :ParseError, :data =>
_("Unexpected end of file. Expected DOCTYPE name.")})
+ @currentToken[:correct] = false
@tokenQueue.push(@currentToken)
@state = @states[:data]
else
@@ -777,33 +801,21 @@ module HTML5lib
def doctypeNameState
data = @stream.char
- needsDoctypeCheck = false
if SPACE_CHARACTERS.include? data
@state = @states[:afterDoctypeName]
- needsDoctypeCheck = true
elsif data == ">"
@tokenQueue.push(@currentToken)
@state = @states[:data]
elsif data == :EOF
@tokenQueue.push({:type => :ParseError, :data =>
_("Unexpected end of file in DOCTYPE name.")})
+ @currentToken[:correct] = false
@tokenQueue.push(@currentToken)
@state = @states[:data]
else
- # We can't just uppercase everything that arrives here. For
- # instance, non-ASCII characters.
- if ASCII_LOWERCASE.include? data
- data = data.upcase
- end
@currentToken[:name] += data
- needsDoctypeCheck = true
end
- # After some iterations through this state it should eventually say
- # "HTML". Otherwise there's an error.
- if needsDoctypeCheck and @currentToken[:name] == "HTML"
- @currentToken[:data] = false
- end
return true
end
@@ -815,16 +827,195 @@ module HTML5lib
@state = @states[:data]
elsif data == :EOF
@currentToken[:data] = true
- # XXX EMIT
@stream.queue.push(data)
@tokenQueue.push({:type => :ParseError, :data =>
_("Unexpected end of file in DOCTYPE.")})
+ @currentToken[:correct] = false
+ @tokenQueue.push(@currentToken)
+ @state = @states[:data]
+ else
+ charStack = [data]
+ 5.times { charStack << stream.char }
+ token = charStack.join('').tr(ASCII_UPPERCASE,ASCII_LOWERCASE)
+ if token == "public"
+ @state = @states[:beforeDoctypePublicIdentifier]
+ elsif token == "system"
+ @state = @states[:beforeDoctypeSystemIdentifier]
+ else
+ @stream.queue += charStack
+ @tokenQueue.push({:type => :ParseError, :data =>
+ _("Expected 'public' or 'system'. Got '#{charStack.join('')}'")})
+ @state = @states[:bogusDoctype]
+ end
+ end
+ return true
+ end
+
+ def beforeDoctypePublicIdentifierState
+ data = @stream.char
+
+ if SPACE_CHARACTERS.include?(data)
+ elsif data == "\""
+ @currentToken[:publicId] = ""
+ @state = @states[:doctypePublicIdentifierDoubleQuoted]
+ elsif data == "'"
+ @currentToken[:publicId] = ""
+ @state = @states[:doctypePublicIdentifierSingleQuoted]
+ elsif data == ">"
+ @tokenQueue.push({:type => :ParseError, :data =>
+ _("Unexpected end of DOCTYPE.")})
+ @currentToken[:correct] = false
+ @tokenQueue.push(@currentToken)
+ @state = @states[:data]
+ elsif data == :EOF
+ @tokenQueue.push({:type => :ParseError, :data =>
+ _("Unexpected end of file in DOCTYPE.")})
+ @currentToken[:correct] = false
@tokenQueue.push(@currentToken)
@state = @states[:data]
else
@tokenQueue.push({:type => :ParseError, :data =>
- _("Expected space or '>'. Got '" + data + "'")})
- @currentToken[:data] = true
+ _("Unexpected character in DOCTYPE.")})
+ @state = @states[:bogusDoctype]
+ end
+
+ return true
+ end
+
+ def doctypePublicIdentifierDoubleQuotedState
+ data = @stream.char
+ if data == "\""
+ @state = @states[:afterDoctypePublicIdentifier]
+ elsif data == :EOF
+ @tokenQueue.push({:type => :ParseError, :data =>
+ _("Unexpected end of file in DOCTYPE.")})
+ @currentToken[:correct] = false
+ @tokenQueue.push(@currentToken)
+ @state = @states[:data]
+ else
+ @currentToken[:publicId] += data
+ end
+ return true
+ end
+
+ def doctypePublicIdentifierSingleQuotedState
+ data = @stream.char
+ if data == "'"
+ @state = @states[:afterDoctypePublicIdentifier]
+ elsif data == :EOF
+ @tokenQueue.push({:type => :ParseError, :data =>
+ _("Unexpected end of file in DOCTYPE.")})
+ @currentToken[:correct] = false
+ @tokenQueue.push(@currentToken)
+ @state = @states[:data]
+ else
+ @currentToken[:publicId] += data
+ end
+ return true
+ end
+
+ def afterDoctypePublicIdentifierState
+ data = @stream.char
+ if SPACE_CHARACTERS.include?(data)
+ elsif data == "\""
+ @currentToken[:systemId] = ""
+ @state = @states[:doctypeSystemIdentifierDoubleQuoted]
+ elsif data == "'"
+ @currentToken[:systemId] = ""
+ @state = @states[:doctypeSystemIdentifierSingleQuoted]
+ elsif data == ">"
+ @tokenQueue.push(@currentToken)
+ @state = @states[:data]
+ elsif data == :EOF
+ @tokenQueue.push({:type => :ParseError, :data =>
+ _("Unexpected end of file in DOCTYPE.")})
+ @currentToken[:correct] = false
+ @tokenQueue.push(@currentToken)
+ @state = @states[:data]
+ else
+ @tokenQueue.push({:type => :ParseError, :data =>
+ _("Unexpected character in DOCTYPE.")})
+ @state = @states[:bogusDoctype]
+ end
+ return true
+ end
+
+ def beforeDoctypeSystemIdentifierState
+ data = @stream.char
+ if SPACE_CHARACTERS.include?(data)
+ elsif data == "\""
+ @currentToken[:systemId] = ""
+ @state = @states[:doctypeSystemIdentifierDoubleQuoted]
+ elsif data == "'"
+ @currentToken[:systemId] = ""
+ @state = @states[:doctypeSystemIdentifierSingleQuoted]
+ elsif data == ">"
+ @tokenQueue.push({:type => :ParseError, :data =>
+ _("Unexpected character in DOCTYPE.")})
+ @currentToken[:correct] = false
+ @tokenQueue.push(@currentToken)
+ @state = @states[:data]
+ elsif data == :EOF
+ @tokenQueue.push({:type => :ParseError, :data =>
+ _("Unexpected end of file in DOCTYPE.")})
+ @currentToken[:correct] = false
+ @tokenQueue.push(@currentToken)
+ @state = @states[:data]
+ else
+ @tokenQueue.push({:type => :ParseError, :data =>
+ _("Unexpected character in DOCTYPE.")})
+ @state = @states[:bogusDoctype]
+ end
+ return true
+ end
+
+ def doctypeSystemIdentifierDoubleQuotedState
+ data = @stream.char
+ if data == "\""
+ @state = @states[:afterDoctypeSystemIdentifier]
+ elsif data == :EOF
+ @tokenQueue.push({:type => :ParseError, :data =>
+ _("Unexpected end of file in DOCTYPE.")})
+ @currentToken[:correct] = false
+ @tokenQueue.push(@currentToken)
+ @state = @states[:data]
+ else
+ @currentToken[:systemId] += data
+ end
+ return true
+ end
+
+ def doctypeSystemIdentifierSingleQuotedState
+ data = @stream.char
+ if data == "'"
+ @state = @states[:afterDoctypeSystemIdentifier]
+ elsif data == :EOF
+ @tokenQueue.push({:type => :ParseError, :data =>
+ _("Unexpected end of file in DOCTYPE.")})
+ @currentToken[:correct] = false
+ @tokenQueue.push(@currentToken)
+ @state = @states[:data]
+ else
+ @currentToken[:systemId] += data
+ end
+ return true
+ end
+
+ def afterDoctypeSystemIdentifierState
+ data = @stream.char
+ if SPACE_CHARACTERS.include?(data)
+ elsif data == ">"
+ @tokenQueue.push(@currentToken)
+ @state = @states[:data]
+ elsif data == :EOF
+ @tokenQueue.push({:type => :ParseError, :data =>
+ _("Unexpected end of file in DOCTYPE.")})
+ @currentToken[:correct] = false
+ @tokenQueue.push(@currentToken)
+ @state = @states[:data]
+ else
+ @tokenQueue.push({:type => :ParseError, :data =>
+ _("Unexpected character in DOCTYPE.")})
@state = @states[:bogusDoctype]
end
return true
@@ -840,6 +1031,7 @@ module HTML5lib
@stream.queue.push(data)
@tokenQueue.push({:type => :ParseError, :data =>
_("Unexpected end of file in bogus doctype.")})
+ @currentToken[:correct] = false
@tokenQueue.push(@currentToken)
@state = @states[:data]
end
diff --git a/vendor/plugins/HTML5lib/lib/html5lib/treebuilders.rb b/vendor/plugins/HTML5lib/lib/html5lib/treebuilders.rb
index 176b402a..9fa49975 100644
--- a/vendor/plugins/HTML5lib/lib/html5lib/treebuilders.rb
+++ b/vendor/plugins/HTML5lib/lib/html5lib/treebuilders.rb
@@ -1,21 +1,24 @@
module HTML5lib
module TreeBuilders
- def self.getTreeBuilder(name)
- case name.to_s.downcase
+ class << self
+ def [](name)
+ case name.to_s.downcase
when 'simpletree' then
require 'html5lib/treebuilders/simpletree'
SimpleTree::TreeBuilder
when 'rexml' then
require 'html5lib/treebuilders/rexml'
- REXMLTree::TreeBuilder
+ REXML::TreeBuilder
when 'hpricot' then
require 'html5lib/treebuilders/hpricot'
Hpricot::TreeBuilder
else
raise "Unknown TreeBuilder #{name}"
+ end
end
- end
+ alias :getTreeBuilder :[]
+ end
end
end
diff --git a/vendor/plugins/HTML5lib/lib/html5lib/treebuilders/base.rb b/vendor/plugins/HTML5lib/lib/html5lib/treebuilders/base.rb
index 5c1be892..0d1082bd 100755
--- a/vendor/plugins/HTML5lib/lib/html5lib/treebuilders/base.rb
+++ b/vendor/plugins/HTML5lib/lib/html5lib/treebuilders/base.rb
@@ -144,7 +144,7 @@ module HTML5lib
# code. It should still do the same though.
# Step 1: stop the algorithm when there's nothing to do.
- return unless @activeFormattingElements
+ return if @activeFormattingElements.empty?
# Step 2 and step 3: we start with the last element. So i is -1.
i = -1
diff --git a/vendor/plugins/HTML5lib/lib/html5lib/treebuilders/hpricot.rb b/vendor/plugins/HTML5lib/lib/html5lib/treebuilders/hpricot.rb
index 3ea8afa2..20cc58b6 100644
--- a/vendor/plugins/HTML5lib/lib/html5lib/treebuilders/hpricot.rb
+++ b/vendor/plugins/HTML5lib/lib/html5lib/treebuilders/hpricot.rb
@@ -1,4 +1,5 @@
require 'html5lib/treebuilders/base'
+require 'rubygems'
require 'hpricot'
require 'forwardable'
@@ -26,12 +27,17 @@ module HTML5lib
childNodes << node
hpricot.children << node.hpricot
end
+ if (oldparent = node.hpricot.parent) != nil
+ oldparent.children.delete_at(oldparent.children.index(node.hpricot))
+ end
+ node.hpricot.parent = hpricot
node.parent = self
end
def removeChild(node)
childNodes.delete(node)
hpricot.children.delete_at(hpricot.children.index(node.hpricot))
+ node.hpricot.parent = nil
node.parent = nil
end
@@ -48,6 +54,7 @@ module HTML5lib
if node.kind_of?(TextNode) and index > 0 and childNodes[index-1].kind_of?(TextNode)
childNodes[index-1].hpricot.content = childNodes[index-1].hpricot.to_s + node.hpricot.to_s
else
+ refNode.hpricot.parent.insert_before(node.hpricot,refNode.hpricot)
childNodes.insert(index, node)
end
end
diff --git a/vendor/plugins/HTML5lib/lib/html5lib/treebuilders/rexml.rb b/vendor/plugins/HTML5lib/lib/html5lib/treebuilders/rexml.rb
index 7c389ca6..f6aad877 100644
--- a/vendor/plugins/HTML5lib/lib/html5lib/treebuilders/rexml.rb
+++ b/vendor/plugins/HTML5lib/lib/html5lib/treebuilders/rexml.rb
@@ -4,7 +4,7 @@ require 'forwardable'
module HTML5lib
module TreeBuilders
- module REXMLTree
+ module REXML
class Node < Base::Node
extend Forwardable
@@ -52,6 +52,7 @@ module HTML5lib
childNodes[index-1].rxobj.raw = true
else
childNodes.insert index, node
+ refNode.rxobj.parent.insert_before(refNode.rxobj,node.rxobj)
end
end
@@ -62,7 +63,7 @@ module HTML5lib
class Element < Node
def self.rxclass
- REXML::Element
+ ::REXML::Element
end
def initialize name
@@ -95,7 +96,7 @@ module HTML5lib
class Document < Node
def self.rxclass
- REXML::Document
+ ::REXML::Document
end
def initialize
@@ -120,7 +121,7 @@ module HTML5lib
class DocumentType < Node
def self.rxclass
- REXML::DocType
+ ::REXML::DocType
end
def printTree indent=0
@@ -145,7 +146,7 @@ module HTML5lib
class TextNode < Node
def initialize data
raw=data.gsub('&','&').gsub('<','<').gsub('>','>')
- @rxobj = REXML::Text.new(raw, true, nil, true)
+ @rxobj = ::REXML::Text.new(raw, true, nil, true)
end
def printTree indent=0
@@ -155,7 +156,7 @@ module HTML5lib
class CommentNode < Node
def self.rxclass
- REXML::Comment
+ ::REXML::Comment
end
def printTree indent=0
diff --git a/vendor/plugins/HTML5lib/lib/html5lib/treebuilders/simpletree.rb b/vendor/plugins/HTML5lib/lib/html5lib/treebuilders/simpletree.rb
index ff4d8f5d..83034bff 100644
--- a/vendor/plugins/HTML5lib/lib/html5lib/treebuilders/simpletree.rb
+++ b/vendor/plugins/HTML5lib/lib/html5lib/treebuilders/simpletree.rb
@@ -78,7 +78,7 @@ module HTML5lib
class Element < Node
def to_s
- "<%s>" % name
+ "<#{name}>"
end
def printTree indent=0
diff --git a/vendor/plugins/HTML5lib/lib/html5lib/treewalkers.rb b/vendor/plugins/HTML5lib/lib/html5lib/treewalkers.rb
new file mode 100644
index 00000000..2074768c
--- /dev/null
+++ b/vendor/plugins/HTML5lib/lib/html5lib/treewalkers.rb
@@ -0,0 +1,26 @@
+require 'html5lib/treewalkers/base'
+
+module HTML5lib
+ module TreeWalkers
+
+ class << self
+ def [](name)
+ case name.to_s.downcase
+ when 'simpletree' then
+ require 'html5lib/treewalkers/simpletree'
+ SimpleTree::TreeWalker
+ when 'rexml' then
+ require 'html5lib/treewalkers/rexml'
+ REXML::TreeWalker
+ when 'hpricot' then
+ require 'html5lib/treewalkers/hpricot'
+ Hpricot::TreeWalker
+ else
+ raise "Unknown TreeWalker #{name}"
+ end
+ end
+
+ alias :getTreeWalker :[]
+ end
+ end
+end
diff --git a/vendor/plugins/HTML5lib/lib/html5lib/treewalkers/base.rb b/vendor/plugins/HTML5lib/lib/html5lib/treewalkers/base.rb
new file mode 100644
index 00000000..21d4d3f7
--- /dev/null
+++ b/vendor/plugins/HTML5lib/lib/html5lib/treewalkers/base.rb
@@ -0,0 +1,156 @@
+require 'html5lib/constants'
+module HTML5lib
+module TreeWalkers
+
+module TokenConstructor
+ def error(msg)
+ return {:type => "SerializeError", :data => msg}
+ end
+
+ def normalizeAttrs(attrs)
+ attrs.to_a
+ end
+
+ def emptyTag(name, attrs, hasChildren=false)
+ error(_("Void element has children")) if hasChildren
+ return({:type => :EmptyTag, :name => name, \
+ :data => normalizeAttrs(attrs)})
+ end
+
+ def startTag(name, attrs)
+ return {:type => :StartTag, :name => name, \
+ :data => normalizeAttrs(attrs)}
+ end
+
+ def endTag(name)
+ return {:type => :EndTag, :name => name, :data => []}
+ end
+
+ def text(data)
+ if data =~ /\A([#{SPACE_CHARACTERS.join('')}]+)/m
+ yield({:type => :SpaceCharacters, :data => $1})
+ data = data[$1.length .. -1]
+ return if data.empty?
+ end
+
+ if data =~ /([#{SPACE_CHARACTERS.join('')}]+)\Z/m
+ yield({:type => :Characters, :data => data[0 ... -$1.length]})
+ yield({:type => :SpaceCharacters, :data => $1})
+ else
+ yield({:type => :Characters, :data => data})
+ end
+ end
+
+ def comment(data)
+ return {:type => :Comment, :data => data}
+ end
+
+ def doctype(name)
+ return {:type => :Doctype, :name => name, :data => name.upcase() == "HTML"}
+ end
+
+ def unknown(nodeType)
+ return error(_("Unknown node type: ") + nodeType.to_s)
+ end
+
+ def _(str)
+ str
+ end
+end
+
+class Base
+ include TokenConstructor
+
+ def initialize(tree)
+ @tree = tree
+ end
+
+ def each
+ raise NotImplementedError
+ end
+
+ alias walk each
+end
+
+class NonRecursiveTreeWalker < TreeWalkers::Base
+ def node_details(node)
+ raise NotImplementedError
+ end
+
+ def first_child(node)
+ raise NotImplementedError
+ end
+
+ def next_sibling(node)
+ raise NotImplementedError
+ end
+
+ def parent(node)
+ raise NotImplementedError
+ end
+
+ def each
+ currentNode = @tree
+ while currentNode != nil
+ details = node_details(currentNode)
+ hasChildren = false
+
+ case details.shift
+ when :DOCTYPE
+ yield doctype(*details)
+
+ when :TEXT
+ text(*details) {|token| yield token}
+
+ when :ELEMENT
+ name, attributes, hasChildren = details
+ if VOID_ELEMENTS.include?(name)
+ yield emptyTag(name, attributes.to_a, hasChildren)
+ hasChildren = false
+ else
+ yield startTag(name, attributes.to_a)
+ end
+
+ when :COMMENT
+ yield comment(details[0])
+
+ when :DOCUMENT, :DOCUMENT_FRAGMENT
+ hasChildren = true
+
+ when nil
+ # ignore (REXML::XMLDecl is an example)
+
+ else
+ yield unknown(details[0])
+ end
+
+ firstChild = hasChildren ? first_child(currentNode) : nil
+ if firstChild != nil
+ currentNode = firstChild
+ else
+ while currentNode != nil
+ details = node_details(currentNode)
+ if details.shift == :ELEMENT
+ name, attributes, hasChildren = details
+ yield endTag(name) if !VOID_ELEMENTS.include?(name)
+ end
+
+ if @tree == currentNode
+ currentNode = nil
+ else
+ nextSibling = next_sibling(currentNode)
+ if nextSibling != nil
+ currentNode = nextSibling
+ break
+ end
+
+ currentNode = parent(currentNode)
+ end
+ end
+ end
+ end
+ end
+end
+
+end
+end
diff --git a/vendor/plugins/HTML5lib/lib/html5lib/treewalkers/hpricot.rb b/vendor/plugins/HTML5lib/lib/html5lib/treewalkers/hpricot.rb
new file mode 100644
index 00000000..c9d12263
--- /dev/null
+++ b/vendor/plugins/HTML5lib/lib/html5lib/treewalkers/hpricot.rb
@@ -0,0 +1,48 @@
+require 'html5lib/treewalkers/base'
+require 'rexml/document'
+
+module HTML5lib
+ module TreeWalkers
+ module Hpricot
+ class TreeWalker < HTML5lib::TreeWalkers::NonRecursiveTreeWalker
+
+ def node_details(node)
+ case node
+ when ::Hpricot::Elem
+ if node.name.empty?
+ [:DOCUMENT_FRAGMENT]
+ else
+ [:ELEMENT, node.name,
+ node.attributes.map {|name,value| [name,value]},
+ !node.empty?]
+ end
+ when ::Hpricot::Text
+ [:TEXT, node.to_plain_text]
+ when ::Hpricot::Comment
+ [:COMMENT, node.content]
+ when ::Hpricot::Doc
+ [:DOCUMENT]
+ when ::Hpricot::DocType
+ [:DOCTYPE, node.target]
+ when ::Hpricot::XMLDecl
+ [nil]
+ else
+ [:UNKNOWN, node.class.inspect]
+ end
+ end
+
+ def first_child(node)
+ node.children.first
+ end
+
+ def next_sibling(node)
+ node.next_node
+ end
+
+ def parent(node)
+ node.parent
+ end
+ end
+ end
+ end
+end
diff --git a/vendor/plugins/HTML5lib/lib/html5lib/treewalkers/rexml.rb b/vendor/plugins/HTML5lib/lib/html5lib/treewalkers/rexml.rb
new file mode 100644
index 00000000..c6881d97
--- /dev/null
+++ b/vendor/plugins/HTML5lib/lib/html5lib/treewalkers/rexml.rb
@@ -0,0 +1,48 @@
+require 'html5lib/treewalkers/base'
+require 'rexml/document'
+
+module HTML5lib
+ module TreeWalkers
+ module REXML
+ class TreeWalker < HTML5lib::TreeWalkers::NonRecursiveTreeWalker
+
+ def node_details(node)
+ case node
+ when ::REXML::Document
+ [:DOCUMENT]
+ when ::REXML::Element
+ if !node.name
+ [:DOCUMENT_FRAGMENT]
+ else
+ [:ELEMENT, node.name,
+ node.attributes.map {|name,value| [name,value]},
+ node.has_elements? || node.has_text?]
+ end
+ when ::REXML::Text
+ [:TEXT, node.value]
+ when ::REXML::Comment
+ [:COMMENT, node.string]
+ when ::REXML::DocType
+ [:DOCTYPE, node.name]
+ when ::REXML::XMLDecl
+ [nil]
+ else
+ [:UNKNOWN, node.class.inspect]
+ end
+ end
+
+ def first_child(node)
+ node.children.first
+ end
+
+ def next_sibling(node)
+ node.next_sibling
+ end
+
+ def parent(node)
+ node.parent
+ end
+ end
+ end
+ end
+end
diff --git a/vendor/plugins/HTML5lib/lib/html5lib/treewalkers/simpletree.rb b/vendor/plugins/HTML5lib/lib/html5lib/treewalkers/simpletree.rb
new file mode 100644
index 00000000..37ebf32a
--- /dev/null
+++ b/vendor/plugins/HTML5lib/lib/html5lib/treewalkers/simpletree.rb
@@ -0,0 +1,48 @@
+require 'html5lib/treewalkers/base'
+
+module HTML5lib
+ module TreeWalkers
+ module SimpleTree
+ class TreeWalker < HTML5lib::TreeWalkers::Base
+ include HTML5lib::TreeBuilders::SimpleTree
+
+ def walk(node)
+ case node
+ when Document, DocumentFragment
+ return
+
+ when DocumentType
+ yield doctype(node.name)
+
+ when TextNode
+ text(node.value) {|token| yield token}
+
+ when Element
+ if VOID_ELEMENTS.include?(node.name)
+ yield emptyTag(node.name, node.attributes, node.hasContent())
+ else
+ yield startTag(node.name, node.attributes)
+ for child in node.childNodes
+ walk(child) {|token| yield token}
+ end
+ yield endTag(node.name)
+ end
+
+ when CommentNode
+ yield comment(node.value)
+
+ else
+ puts '?'
+ yield unknown(node.class)
+ end
+ end
+
+ def each
+ for child in @tree.childNodes
+ walk(child) {|node| yield node}
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/vendor/plugins/HTML5lib/parse.rb b/vendor/plugins/HTML5lib/parse.rb
new file mode 100755
index 00000000..79233712
--- /dev/null
+++ b/vendor/plugins/HTML5lib/parse.rb
@@ -0,0 +1,213 @@
+#!/usr/bin/env ruby
+#
+# Parse a document to a simpletree tree, with optional profiling
+
+$:.unshift File.dirname(__FILE__),'lib'
+
+def parse(opts, args)
+ encoding = nil
+
+ f = args[-1]
+ if f
+ begin
+ if f[0..6] == 'http://'
+ require 'open-uri'
+ f = URI.parse(f).open
+ encoding = f.charset
+ elsif f == '-'
+ f = $stdin
+ else
+ f = open(f)
+ end
+ rescue
+ end
+ else
+ $stderr.write("No filename provided. Use -h for help\n")
+ exit(1)
+ end
+
+ require 'html5lib/treebuilders'
+ treebuilder = HTML5lib::TreeBuilders[opts.treebuilder]
+
+ if opts.output == :xml
+ require 'html5lib/liberalxmlparser'
+ p = HTML5lib::XHTMLParser.new(:tree=>treebuilder)
+ else
+ require 'html5lib/html5parser'
+ p = HTML5lib::HTMLParser.new(:tree=>treebuilder)
+ end
+
+ if opts.parsemethod == :parse
+ args = [f, encoding]
+ else
+ args = [f, 'div', encoding]
+ end
+
+ if opts.profile
+ require 'profiler'
+ Profiler__::start_profile
+ p.send(opts.parsemethod, *args)
+ Profiler__::stop_profile
+ Profiler__::print_profile($stderr)
+ elsif opts.time
+ require 'time'
+ t0 = Time.new
+ document = p.send(opts.parsemethod, *args)
+ t1 = Time.new
+ printOutput(p, document, opts)
+ t2 = Time.new
+ puts "\n\nRun took: %fs (plus %fs to print the output)"%[t1-t0, t2-t1]
+ else
+ document = p.send(opts.parsemethod, *args)
+ printOutput(p, document, opts)
+ end
+end
+
+def printOutput(parser, document, opts)
+ puts "Encoding: #{parser.tokenizer.stream.char_encoding}" if opts.encoding
+
+ case opts.output
+ when :xml
+ print document
+ when :html
+ require 'html5lib/treewalkers'
+ tokens = HTML5lib::TreeWalkers[opts.treebuilder].new(document)
+ require 'html5lib/serializer'
+ puts HTML5lib::HTMLSerializer.serialize(tokens, opts.serializer)
+ when :hilite
+ print document.hilite
+ when :tree
+ document = [document] unless document.respond_to?(:each)
+ document.each {|fragment| puts parser.tree.testSerializer(fragment)}
+ end
+
+ if opts.error
+ errList=[]
+ for pos, message in parser.errors
+ errList << ("Line %i Col %i"%pos + " " + message)
+ end
+ $stdout.write("\nParse errors:\n" + errList.join("\n")+"\n")
+ end
+end
+
+require 'ostruct'
+options = OpenStruct.new
+options.profile = false
+options.time = false
+options.output = :html
+options.treebuilder = 'simpletree'
+options.error = false
+options.encoding = false
+options.parsemethod = :parse
+options.serializer = {
+ :encoding => 'utf-8',
+ :omit_optional_tags => false,
+ :inject_meta_charset => false
+}
+
+require 'optparse'
+opts = OptionParser.new do |opts|
+ opts.separator ""
+ opts.separator "Parse Options:"
+
+ opts.on("-b", "--treebuilder NAME") do |treebuilder|
+ options.treebuilder = treebuilder
+ end
+
+ opts.on("-f", "--fragment", "Parse as a fragment") do |parse|
+ options.parsemethod = :parseFragment
+ end
+
+ opts.separator ""
+ opts.separator "Filter Options:"
+
+ opts.on("--[no-]inject-meta-charset", "inject ") do |inject|
+ options.serializer[:inject_meta_charset] = inject
+ end
+
+ opts.on("--[no-]strip-whitespace", "strip unnecessary whitespace") do |strip|
+ options.serializer[:strip_whitespace] = strip
+ end
+
+ opts.on("--[no-]sanitize", "escape unsafe tags") do |sanitize|
+ options.serializer[:sanitize] = sanitize
+ end
+
+ opts.separator ""
+ opts.separator "Output Options:"
+
+ opts.on("--tree", "output as debug tree") do |tree|
+ options.output = :tree
+ end
+
+ opts.on("-x", "--xml", "output as xml") do |xml|
+ options.output = :xml
+ options.treebuilder = "rexml"
+ end
+
+ opts.on("--[no-]html", "Output as html") do |html|
+ options.output = (html ? :html : nil)
+ end
+
+ opts.on("--hilite", "Output as formatted highlighted code.") do |hilite|
+ options.output = :hilite
+ end
+
+ opts.on("-e", "--error", "Print a list of parse errors") do |error|
+ options.error = error
+ end
+
+ opts.separator ""
+ opts.separator "Serialization Options:"
+
+ opts.on("--[no-]omit-optional-tags", "Omit optional tags") do |omit|
+ options.serializer[:omit_optional_tags] = omit
+ end
+
+ opts.on("--[no-]quote-attr-values", "Quote attribute values") do |quote|
+ options.serializer[:quote_attr_values] = quote
+ end
+
+ opts.on("--[no-]use-best-quote-char", "Use best quote character") do |best|
+ options.serializer[:use_best_quote_char] = best
+ end
+
+ opts.on("--quote-char C", "Use specified quote character") do |c|
+ options.serializer[:quote_char] = c
+ end
+
+ opts.on("--[no-]minimize-boolean-attributes", "Minimize boolean attributes") do |min|
+ options.serializer[:minimize_boolean_attributes] = min
+ end
+
+ opts.on("--[no-]use-trailing-solidus", "Use trailing solidus") do |slash|
+ options.serializer[:use_trailing_solidus] = slash
+ end
+
+ opts.on("--[no-]escape-lt-in-attrs", "Escape less than signs in attribute values") do |lt|
+ options.serializer[:escape_lt_in_attrs] = lt
+ end
+
+ opts.separator ""
+ opts.separator "Other Options:"
+
+ opts.on("-p", "--[no-]profile", "Profile the run") do |profile|
+ options.profile = profile
+ end
+
+ opts.on("-t", "--[no-]time", "Time the run") do |time|
+ options.time = time
+ end
+
+ opts.on("-c", "--[no-]encoding", "Print character encoding used") do |encoding|
+ options.encoding = encoding
+ end
+
+ opts.on_tail("-h", "--help", "Show this message") do
+ puts opts
+ exit
+ end
+end
+
+opts.parse!(ARGV)
+parse options, ARGV
diff --git a/vendor/plugins/HTML5lib/testdata/encoding/chardet/test_big5.txt b/vendor/plugins/HTML5lib/testdata/encoding/chardet/test_big5.txt
new file mode 100644
index 00000000..91074c98
--- /dev/null
+++ b/vendor/plugins/HTML5lib/testdata/encoding/chardet/test_big5.txt
@@ -0,0 +1,51 @@
+¦Ñ¤l¡m¹D¼w¸g¡n ²Ä¤@~¥|¤Q³¹
+
+¦Ñ¤l¹D¸g
+
+²Ä¤@³¹
+
+¹D¥i¹D¡A«D±`¹D¡C¦W¥i¦W¡A«D±`¦W¡CµL¡A¦W¤Ñ¦a¤§©l¡Q¦³¡A¦W¸Uª«¤§¥À¡C
+¬G±`µL¡A±ý¥HÆ[¨ä§®¡F±`¦³¡A±ý¥HÆ[¨äéu¡C¦¹¨âªÌ¡A¦P¥X¦Ó²§¦W¡A¦P¿×¤§
+¥È¡C¥È¤§¤S¥È¡A²³§®¤§ªù¡C
+
+²Ä¤G³¹
+
+¤Ñ¤U¬Òª¾¬ü¤§¬°¬ü¡A´µ´c¨o¡Q¬Òª¾µ½¤§¬°µ½¡A´µ¤£µ½¨o¡C¬G¦³µL¬Û¥Í¡AÃø
+©ö¬Û¦¨¡Aªøµu¬Û§Î¡A°ª¤U¬Û¶É¡AµÁn¬Û©M¡A«e«á¬ÛÀH¡C¬O¥H¸t¤H³B¡uµL¬°
+¡v¤§¨Æ¡A¦æ¡u¤£¨¥¡v¤§±Ð¡C¸Uª«§@²j¦Ó¤£Ãã¡A¥Í¦Ó¤£¦³¡A¬°¦Ó¤£«î¡A¥\¦¨
+¦Ó¥±©~¡C¤Ò°ß¥±©~¡A¬O¥H¤£¥h¡C
+
+²Ä¤T³¹
+
+¤£©|½å¡A¨Ï¥Á¤£ª§¡Q¤£¶QÃø±o¤§³f¡A¨Ï¥Á¤£¬°µs¡Q¤£¨£¥i±ý¡A¨Ï¥Á¤ß¤£¶Ã
+¡C¬O¥H¡u¸t¤H¡v¤§ªv¡Aµê¨ä¤ß¡A¹ê¨ä¸¡¡A®z¨ä§Ó¡A±j¨ä°©¡C±`¨Ï¥ÁµLª¾µL
+±ý¡C¨Ï¤Ò´¼ªÌ¤£´±¬°¤]¡C¬°¡uµL¬°¡v¡A«hµL¤£ªv¡C
+
+²Ä¥|³¹
+
+¡u¹D¡v¨R¡A¦Ó¥Î¤§©Î¤£¬Õ¡C²W¤¼¡A¦ü¸Uª«¤§©v¡Q®À¨ä¾U¡A¸Ñ¨ä¯É¡A©M¨ä¥ú
+¡A¦P¨ä¹Ð¡Q´ï¤¼¦ü©Î¦s¡C§^¤£ª¾½Ö¤§¤l¡H¶H«Ò¤§¥ý¡C
+
+²Ä¤³¹
+
+¤Ñ¦a¤£¤¯¡A¥H¸Uª«¬°¯ìª¯¡Q¸t¤H¤£¤¯¡A¥H¦Ê©m¬°¯ìª¯¡C¤Ñ¦a¤§¶¡¡A¨äµSéÑ
+õþ¥G¡Hµê¦Ó¤£©}¡A°Ê¦Ó·U¥X¡C¦h¨¥¼Æ½a¡A¤£¦p¦u¤¤¡C
+
+²Ä¤»³¹
+
+¨¦¯«¤£¦º¡A¬O¿×¥È¦É¡C¥È¦É¤§ªù¡A¬O¿×¤Ñ¦a®Ú¡CºøºøY¦s¡A¥Î¤§¤£¶Ô¡C
+
+²Ä¤C³¹
+
+¤Ñªø¦a¤[¡C¤Ñ¦a©Ò¥H¯àªø¥B¤[ªÌ¡A¥H¨ä¤£¦Û¥Í¡A¬G¯àªø¤[¡C¬O¥H¸t¤H«á¨ä
+¨¦Ó¨¥ý¡A¥~¨ä¨¦Ó¨¦s¡C«D¥H¨äµL¨p¨¸¡H¬G¯à¦¨¨ä¨p¡C
+
+²Ä¤K³¹
+
+¤Wµ½Y¤ô¡C¤ôµ½§Q¸Uª«¦Ó¤£ª§¡C³B²³¤H¤§©Ò´c¡A¬G´X©ó¹D¡C©~µ½¦a¡A¤ßµ½
+²W¡A»Pµ½¤¯¡A¨¥µ½«H¡A¬Fµ½ªv¡A¨Æµ½¯à¡A°Êµ½®É¡C¤Ò°ß¤£ª§¡A¬GµL¤×¡C
+
+²Ä¤E³¹
+
+«ù¦Ó¬Õ¤§¡A¤£¦p¨ä¤w¡Q´¢¦Ó¾U¤§¡A¤£¥iªø«O¡Cª÷¥Éº¡°ó¡A²ö¤§¯à¦u¡Q´I¶Q
+¦Óź¡A¦Û¿ò¨ä©S¡C¥\¹E¨°h¡A¤Ñ¤§¹D¡C
diff --git a/vendor/plugins/HTML5lib/testdata/encoding/test-yahoo-jp.dat b/vendor/plugins/HTML5lib/testdata/encoding/test-yahoo-jp.dat
new file mode 100644
index 00000000..36292789
--- /dev/null
+++ b/vendor/plugins/HTML5lib/testdata/encoding/test-yahoo-jp.dat
@@ -0,0 +1,10 @@
+#data
+
+
+
+
+Yahoo! JAPAN
+
+
+#encoding
+ISO-8859-9
+
+#data
+
+
+#encoding
+ISO-8859-9
+
+#data
+
+
+
+#encoding
+ISO-8859-9
diff --git a/vendor/plugins/HTML5lib/testdata/encoding/tests2.dat b/vendor/plugins/HTML5lib/testdata/encoding/tests2.dat
new file mode 100644
index 00000000..dd43f85c
--- /dev/null
+++ b/vendor/plugins/HTML5lib/testdata/encoding/tests2.dat
@@ -0,0 +1,82 @@
+#data
+
+#encoding
+utf-8
+
+#data
+
+
+#encoding
+windows-1252
+
+#data
+
+#encoding
+utf-8
+
+#data
+
+#encoding
+windows-1252
+
+#data
+",
+ "output": ""
+ },
+
+ {
+ "name": "IE_Comments_2",
+ "input": "",
+ "output": "<script>alert('XSS');</script>",
+ "rexml": "Ill-formed XHTML!"
+ },
+
+ {
+ "name": "allow_colons_in_path_component",
+ "input": "foo",
+ "output": "foo"
+ },
+
+ {
+ "name": "background_attribute",
+ "input": "",
+ "output": "",
+ "xhtml": "",
+ "rexml": ""
+ },
+
+ {
+ "name": "bgsound",
+ "input": " ",
+ "output": "<bgsound src=\"javascript:alert('XSS');\"/>",
+ "rexml": "<bgsound src=\"javascript:alert('XSS');\"></bgsound>"
+ },
+
+ {
+ "name": "div_background_image_unicode_encoded",
+ "input": "foo",
+ "output": "foo"
+ },
+
+ {
+ "name": "div_expression",
+ "input": "foo",
+ "output": "foo"
+ },
+
+ {
+ "name": "double_open_angle_brackets",
+ "input": "",
+ "rexml": "Ill-formed XHTML!"
+ },
+
+ {
+ "name": "double_open_angle_brackets_2",
+ "input": "",
+ "output": "",
+ "rexml": "Ill-formed XHTML!"
+ },
+
+ {
+ "name": "img_dynsrc_lowsrc",
+ "input": "",
+ "output": "",
+ "rexml": ""
+ },
+
+ {
+ "name": "img_vbscript",
+ "input": "",
+ "output": "",
+ "rexml": ""
+ },
+
+ {
+ "name": "input_image",
+ "input": "",
+ "output": "",
+ "rexml": ""
+ },
+
+ {
+ "name": "link_stylesheets",
+ "input": "",
+ "output": "<link rel=\"stylesheet\" href=\"javascript:alert('XSS');\"/>",
+ "rexml": "<link href=\"javascript:alert('XSS');\" rel=\"stylesheet\"/>"
+ },
+
+ {
+ "name": "link_stylesheets_2",
+ "input": "",
+ "output": "<link rel=\"stylesheet\" href=\"http://ha.ckers.org/xss.css\"/>",
+ "rexml": "<link href=\"http://ha.ckers.org/xss.css\" rel=\"stylesheet\"/>"
+ },
+
+ {
+ "name": "list_style_image",
+ "input": "
foo ",
+ "output": "foo "
+ },
+
+ {
+ "name": "no_closing_script_tags",
+ "input": "",
+ "output": "<script XSS=\"\" src=\"http://ha.ckers.org/xss.js\"></script>",
+ "rexml": "Ill-formed XHTML!"
+ },
+
+ {
+ "name": "non_alpha_non_digit_2",
+ "input": "foo",
+ "output": "foo",
+ "rexml": "Ill-formed XHTML!"
+ },
+
+ {
+ "name": "non_alpha_non_digit_3",
+ "input": "",
+ "output": "",
+ "rexml": "Ill-formed XHTML!"
+ },
+
+ {
+ "name": "non_alpha_non_digit_II",
+ "input": "foo",
+ "output": "foo",
+ "rexml": "Ill-formed XHTML!"
+ },
+
+ {
+ "name": "non_alpha_non_digit_III",
+ "input": "foo",
+ "output": "foo",
+ "rexml": "Ill-formed XHTML!"
+ },
+
+ {
+ "name": "platypus",
+ "input": "never trust your upstream platypus",
+ "output": "never trust your upstream platypus"
+ },
+
+ {
+ "name": "protocol_resolution_in_script_tag",
+ "input": "",
+ "output": "<script src=\"//ha.ckers.org/.j\"></script>",
+ "rexml": "Ill-formed XHTML!"
+ },
+
+ {
+ "name": "should_allow_anchors",
+ "input": "",
+ "output": "<script>baz</script>"
+ },
+
+ {
+ "name": "should_allow_image_alt_attribute",
+ "input": "",
+ "output": "",
+ "rexml": ""
+ },
+
+ {
+ "name": "should_allow_image_height_attribute",
+ "input": "",
+ "output": "",
+ "rexml": ""
+ },
+
+ {
+ "name": "should_allow_image_src_attribute",
+ "input": "",
+ "output": "",
+ "rexml": ""
+ },
+
+ {
+ "name": "should_allow_image_width_attribute",
+ "input": "",
+ "output": "",
+ "rexml": ""
+ },
+
+ {
+ "name": "should_handle_blank_text",
+ "input": "",
+ "output": ""
+ },
+
+ {
+ "name": "should_handle_malformed_image_tags",
+ "input": "\">",
+ "output": "<script>alert(\"XSS\")</script>\">",
+ "rexml": "Ill-formed XHTML!"
+ },
+
+ {
+ "name": "should_handle_non_html",
+ "input": "abc",
+ "output": "abc"
+ },
+
+ {
+ "name": "should_not_fall_for_ridiculous_hack",
+ "input": "",
+ "output": "",
+ "rexml": ""
+ },
+
+ {
+ "name": "should_not_fall_for_xss_image_hack_0",
+ "input": "",
+ "output": "",
+ "rexml": ""
+ },
+
+ {
+ "name": "should_not_fall_for_xss_image_hack_1",
+ "input": "",
+ "output": "",
+ "rexml": "Ill-formed XHTML!"
+ },
+
+ {
+ "name": "should_not_fall_for_xss_image_hack_10",
+ "input": "",
+ "output": "",
+ "rexml": ""
+ },
+
+ {
+ "name": "should_not_fall_for_xss_image_hack_11",
+ "input": "",
+ "output": "",
+ "rexml": ""
+ },
+
+ {
+ "name": "should_not_fall_for_xss_image_hack_12",
+ "input": "",
+ "output": "",
+ "rexml": ""
+ },
+
+ {
+ "name": "should_not_fall_for_xss_image_hack_13",
+ "input": "",
+ "output": "",
+ "rexml": ""
+ },
+
+ {
+ "name": "should_not_fall_for_xss_image_hack_14",
+ "input": "",
+ "output": "",
+ "rexml": ""
+ },
+
+ {
+ "name": "should_not_fall_for_xss_image_hack_2",
+ "input": "",
+ "output": "",
+ "rexml": ""
+ },
+
+ {
+ "name": "should_not_fall_for_xss_image_hack_3",
+ "input": "",
+ "output": "",
+ "rexml": ""
+ },
+
+ {
+ "name": "should_not_fall_for_xss_image_hack_4",
+ "input": "",
+ "output": "",
+ "rexml": ""
+ },
+
+ {
+ "name": "should_not_fall_for_xss_image_hack_5",
+ "input": "",
+ "output": "",
+ "rexml": ""
+ },
+
+ {
+ "name": "should_not_fall_for_xss_image_hack_6",
+ "input": "",
+ "output": "",
+ "rexml": ""
+ },
+
+ {
+ "name": "should_not_fall_for_xss_image_hack_7",
+ "input": "",
+ "output": "",
+ "rexml": ""
+ },
+
+ {
+ "name": "should_not_fall_for_xss_image_hack_8",
+ "input": "",
+ "output": "",
+ "rexml": ""
+ },
+
+ {
+ "name": "should_not_fall_for_xss_image_hack_9",
+ "input": "",
+ "output": "",
+ "rexml": ""
+ },
+
+ {
+ "name": "should_sanitize_half_open_scripts",
+ "input": "",
+ "rexml": "Ill-formed XHTML!"
+ },
+
+ {
+ "name": "should_sanitize_invalid_script_tag",
+ "input": "",
+ "output": "<script XSS=\"\" SRC=\"http://ha.ckers.org/xss.js\"></script>",
+ "rexml": "Ill-formed XHTML!"
+ },
+
+ {
+ "name": "should_sanitize_script_tag_with_multiple_open_brackets",
+ "input": "<",
+ "output": "<<script>alert(\"XSS\");//<</script>",
+ "rexml": "Ill-formed XHTML!"
+ },
+
+ {
+ "name": "should_sanitize_script_tag_with_multiple_open_brackets_2",
+ "input": "