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 %>
- +
as <%= text_field_tag :author, h(@author.delete("\x01-\x08\x0B\x0C\x0E-\x1F")), diff --git a/app/views/wiki/export.rhtml b/app/views/wiki/export.rhtml index 2e15ebcf..2b7dcd7c 100644 --- a/app/views/wiki/export.rhtml +++ b/app/views/wiki/export.rhtml @@ -5,8 +5,4 @@ diff --git a/app/views/wiki/new.rhtml b/app/views/wiki/new.rhtml index f557766d..967fa1d4 100644 --- a/app/views/wiki/new.rhtml +++ b/app/views/wiki/new.rhtml @@ -13,7 +13,7 @@ <% form_tag({ :action => 'save', :web => @web.address, :id => @page_name }, { 'id' => 'editForm', 'method' => 'post', 'onsubmit' => 'cleanAuthorName();', 'accept-charset' => 'utf-8' }) do %> - +
as <%= text_field_tag :author, @author, diff --git a/app/views/wiki/page.rhtml b/app/views/wiki/page.rhtml index 9a488a4e..277c1cae 100644 --- a/app/views/wiki/page.rhtml +++ b/app/views/wiki/page.rhtml @@ -35,15 +35,10 @@ <%= link_to('Print', { :web => @web.address, :action => 'print', :id => @page.name }, { :accesskey => 'p', :id => 'view_print' }) %> - <% if defined? RedClothForTex and RedClothForTex.available? and @web.markup == :textile or @web.markup == :markdownMML %> + <% if @web.markup == :markdownMML or @web.markup == :markdown %> | <%= link_to 'TeX', {:web => @web.address, :action => 'tex', :id => @page.name}, {:id => 'view_tex'} %> - <% if OPTIONS[:pdflatex] %> - | - <%= link_to 'PDF', {:web => @web.address, :action => 'pdf', :id => @page.name}, - {:id => 'view_pdf'} %> - <% end %> <% if WikiReference.pages_in_category(@web, 'S5-slideshow').map.include?(@page.name) %> | <%= link_to 'S5', {:web => @web.address, :action => 's5', :id => @page.name}, diff --git a/app/views/wiki/tex.rhtml b/app/views/wiki/tex.rhtml index 47113df4..2f2e5e52 100644 --- a/app/views/wiki/tex.rhtml +++ b/app/views/wiki/tex.rhtml @@ -2,11 +2,17 @@ \usepackage{amsmath} \usepackage{amsfonts} +\usepackage{amssymb} \usepackage{graphicx} \usepackage{ucs} \usepackage[utf8x]{inputenc} \usepackage{hyperref} +%----Macros---------- +\newcommand{\gt}{>} +\newcommand{\lt}{<} +\newcommand{\qed}{\blacksquare} + %------------------------------------------------------------------- \begin{document} diff --git a/lib/chunks/engines.rb b/lib/chunks/engines.rb index c870541a..d4f583d2 100644 --- a/lib/chunks/engines.rb +++ b/lib/chunks/engines.rb @@ -24,10 +24,10 @@ module Engines end class Textile < AbstractEngine - require_dependency 'sanitize' + require 'sanitize' include Sanitize def mask - require_dependency 'redcloth' + require 'redcloth' redcloth = RedCloth.new(@content, [:hard_breaks] + @content.options[:engine_opts]) redcloth.filter_html = false redcloth.no_span_caps = false @@ -37,33 +37,34 @@ module Engines end class Markdown < AbstractEngine - require_dependency 'sanitize' + require 'sanitize' include Sanitize def mask - require_dependency 'maruku' - require_dependency 'maruku/ext/math' - html = Maruku.new(@content.delete("\r\x01-\x08\x0B\x0C\x0E-\x1F"), {:math_enabled => false}).to_html - sanitize_xhtml(html.to_ncr) + require 'maruku' + require 'maruku/ext/math' + html = sanitize_rexml(Maruku.new(@content.delete("\r\x01-\x08\x0B\x0C\x0E-\x1F"), + {:math_enabled => false}).to_html_tree) + html.gsub(/\A
\n?(.*?)\n?<\/div>\Z/m, '\1') end end class MarkdownMML < AbstractEngine - require_dependency 'sanitize' + require 'sanitize' include Sanitize def mask - require_dependency 'maruku' - require_dependency 'maruku/ext/math' - html = Maruku.new(@content.delete("\r\x01-\x08\x0B\x0C\x0E-\x1F"), - {:math_enabled => true, :math_numbered => ['\\[','\\begin{equation}']}).to_html - sanitize_xhtml(html.to_ncr) + require 'maruku' + require 'maruku/ext/math' + html = sanitize_rexml(Maruku.new(@content.delete("\r\x01-\x08\x0B\x0C\x0E-\x1F"), + {:math_enabled => true, :math_numbered => ['\\[','\\begin{equation}']}).to_html_tree) + html.gsub(/\A
\n?(.*?)\n?<\/div>\Z/m, '\1') end end class Mixed < AbstractEngine - require_dependency 'sanitize' + require 'sanitize' include Sanitize def mask - require_dependency 'redcloth' + require 'redcloth' redcloth = RedCloth.new(@content, @content.options[:engine_opts]) redcloth.filter_html = false redcloth.no_span_caps = false @@ -73,7 +74,7 @@ module Engines end class RDoc < AbstractEngine - require_dependency 'sanitize' + require 'sanitize' include Sanitize def mask require_dependency 'rdocsupport' diff --git a/lib/page_renderer.rb b/lib/page_renderer.rb index 26fbd06e..d1d44ffa 100644 --- a/lib/page_renderer.rb +++ b/lib/page_renderer.rb @@ -1,4 +1,5 @@ require 'xhtmldiff' + # Temporary class containing all rendering stuff from a Revision # I want to shift all rendering loguc to the controller eventually @@ -40,10 +41,12 @@ class PageRenderer previous_revision = @revision.page.previous_revision(@revision) if previous_revision - previous_content = "
\n" + WikiContent.new(previous_revision, @@url_generator).render!.to_s + "\n
" - current_content = "
\n" + display_content.to_s + "\n
" + previous_content = "
" + WikiContent.new(previous_revision, @@url_generator).render!.to_s + "
" + current_content = "
" + display_content.to_s + "
" 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_previous_revision = REXML::HashableElementDelegator.new( @@ -54,7 +57,7 @@ class PageRenderer diffs = '' diff_doc.write(diffs, -1, true, true) - diffs + diffs.gsub(/\A
(.*)<\/div>\Z/m, '\1') else display_content end diff --git a/lib/redcloth_for_tex.rb b/lib/redcloth_for_tex.rb deleted file mode 100644 index 6bfb6a0f..00000000 --- a/lib/redcloth_for_tex.rb +++ /dev/null @@ -1,736 +0,0 @@ -# This is RedCloth (http://www.whytheluckystiff.net/ruby/redcloth/) -# converted by David Heinemeier Hansson to emit Tex - -class String - # Flexible HTML escaping - def texesc!( mode ) - gsub!( '&', '\\\\&' ) - gsub!( '%', '\%' ) - gsub!( '$', '\$' ) - gsub!( '~', '$\sim$' ) - end -end - - -def table_of_contents(text, pages) - text.gsub( /^([#*]+? .*?)$(?![^#*])/m ) do |match| - lines = match.split( /\n/ ) - last_line = -1 - depth = [] - lines.each_with_index do |line, line_id| - if line =~ /^([#*]+) (.*)$/m - tl,content = $~[1..2] - content.gsub! /[\[\]]/, "" - content.strip! - - if depth.last - if depth.last.length > tl.length - (depth.length - 1).downto(0) do |i| - break if depth[i].length == tl.length - lines[line_id - 1] << "" # "\n\t\\end{#{ lT( depth[i] ) }}\n\t" - depth.pop - end - end - if !depth.last.nil? && !tl.length.nil? && depth.last.length == tl.length - lines[line_id - 1] << '' - end - end - - depth << tl unless depth.last == tl - - subsection_depth = [depth.length - 1, 2].min - - lines[line_id] = "\n\\#{ "sub" * subsection_depth }section{#{ content }}" - lines[line_id] += "\n#{pages[content]}" if pages.keys.include?(content) - - lines[line_id] = "\\pagebreak\n#{lines[line_id]}" if subsection_depth == 0 - - last_line = line_id - - elsif line =~ /^\s+\S/ - last_line = line_id - elsif line_id - last_line < 2 and line =~ /^\S/ - last_line = line_id - end - if line_id - last_line > 1 or line_id == lines.length - 1 - depth.delete_if do |v| - lines[last_line] << "" # "\n\t\\end{#{ lT( v ) }}" - end - end - end - lines.join( "\n" ) - end -end - -class RedClothForTex < String - - VERSION = '2.0.7' - - # - # Mapping of 8-bit ASCII codes to HTML numerical entity equivalents. - # (from PyTextile) - # - TEXTILE_TAGS = - - [[128, 8364], [129, 0], [130, 8218], [131, 402], [132, 8222], [133, 8230], - [134, 8224], [135, 8225], [136, 710], [137, 8240], [138, 352], [139, 8249], - [140, 338], [141, 0], [142, 0], [143, 0], [144, 0], [145, 8216], [146, 8217], - [147, 8220], [148, 8221], [149, 8226], [150, 8211], [151, 8212], [152, 732], - [153, 8482], [154, 353], [155, 8250], [156, 339], [157, 0], [158, 0], [159, 376]]. - - collect! do |a, b| - [a.chr, ( b.zero? and "" or "&#{ b };" )] - end - - # - # Regular expressions to convert to HTML. - # - A_HLGN = /(?:(?:<>|<|>|\=|[()]+)+)/ - A_VLGN = /[\-^~]/ - C_CLAS = '(?:\([^)]+\))' - C_LNGE = '(?:\[[^\]]+\])' - C_STYL = '(?:\{[^}]+\})' - S_CSPN = '(?:\\\\\d+)' - S_RSPN = '(?:/\d+)' - A = "(?:#{A_HLGN}?#{A_VLGN}?|#{A_VLGN}?#{A_HLGN}?)" - S = "(?:#{S_CSPN}?#{S_RSPN}|#{S_RSPN}?#{S_CSPN}?)" - C = "(?:#{C_CLAS}?#{C_STYL}?#{C_LNGE}?|#{C_STYL}?#{C_LNGE}?#{C_CLAS}?|#{C_LNGE}?#{C_STYL}?#{C_CLAS}?)" - # PUNCT = Regexp::quote( '!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~' ) - PUNCT = Regexp::quote( '!"#$%&\'*+,-./:;=?@\\^_`|~' ) - HYPERLINK = '(\S+?)([^\w\s/;=\?]*?)(\s|$)' - - GLYPHS = [ - # [ /([^\s\[{(>])?\'([dmst]\b|ll\b|ve\b|\s|:|$)/, '\1’\2' ], # single closing - [ /([^\s\[{(>])\'/, '\1’' ], # single closing - [ /\'(?=\s|s\b|[#{PUNCT}])/, '’' ], # single closing - [ /\'/, '‘' ], # single opening - # [ /([^\s\[{(])?"(\s|:|$)/, '\1”\2' ], # double closing - [ /([^\s\[{(>])"/, '\1”' ], # double closing - [ /"(?=\s|[#{PUNCT}])/, '”' ], # double closing - [ /"/, '“' ], # double opening - [ /\b( )?\.{3}/, '\1…' ], # ellipsis - [ /\b([A-Z][A-Z0-9]{2,})\b(?:[(]([^)]*)[)])/, '\1' ], # 3+ uppercase acronym - [ /(^|[^"][>\s])([A-Z][A-Z0-9 ]{2,})([^\2\3' ], # 3+ uppercase caps - [ /(\.\s)?\s?--\s?/, '\1—' ], # em dash - [ /\s->\s/, ' → ' ], # en dash - [ /\s-\s/, ' – ' ], # en dash - [ /(\d+) ?x ?(\d+)/, '\1×\2' ], # dimension sign - [ /\b ?[(\[]TM[\])]/i, '™' ], # trademark - [ /\b ?[(\[]R[\])]/i, '®' ], # registered - [ /\b ?[(\[]C[\])]/i, '©' ] # copyright - ] - - I_ALGN_VALS = { - '<' => 'left', - '=' => 'center', - '>' => 'right' - } - - H_ALGN_VALS = { - '<' => 'left', - '=' => 'center', - '>' => 'right', - '<>' => 'justify' - } - - V_ALGN_VALS = { - '^' => 'top', - '-' => 'middle', - '~' => 'bottom' - } - - QTAGS = [ - ['**', 'bf'], - ['*', 'bf'], - ['??', 'cite'], - ['-', 'del'], - ['__', 'underline'], - ['_', 'em'], - ['%', 'span'], - ['+', 'ins'], - ['^', 'sup'], - ['~', 'sub'] - ] - - def self.available? - if not defined? @@available - begin - @@available = system "pdflatex -version" - rescue Errno::ENOENT - @@available = false - end - end - @@available - end - - # - # Two accessor for setting security restrictions. - # - # This is a nice thing if you're using RedCloth for - # formatting in public places (e.g. Wikis) where you - # don't want users to abuse HTML for bad things. - # - # If +:filter_html+ is set, HTML which wasn't - # created by the Textile processor will be escaped. - # - # If +:filter_styles+ is set, it will also disable - # the style markup specifier. ('{color: red}') - # - attr_accessor :filter_html, :filter_styles - - # - # Accessor for toggling line folding. - # - # If +:fold_lines+ is set, single newlines will - # not be converted to break tags. - # - attr_accessor :fold_lines - - def initialize( string, restrictions = [] ) - restrictions.each { |r| method( "#{ r }=" ).call( true ) } - super( string ) - end - - # - # Generate tex. - # - def to_tex( lite = false ) - - # make our working copy - text = self.dup - - @urlrefs = {} - @shelf = [] - - # incoming_entities text - fix_entities text - clean_white_space text - - get_refs text - - no_textile text - - unless lite - lists text - table text - end - - glyphs text - - unless lite - fold text - block text - end - - retrieve text - encode_entities text - - text.gsub!(/\[\[(.*?)\]\]/, "\\1") - text.gsub!(/_/, "\\_") - text.gsub!( /<\/?notextile>/, '' ) - # text.gsub!( /x%x%/, '&' ) - # text.gsub!( /
/, "
\n" ) - text.strip! - text - - end - - def pgl( text ) - GLYPHS.each do |re, resub| - text.gsub! re, resub - end - end - - def pba( text_in, element = "" ) - - return '' unless text_in - - style = [] - text = text_in.dup - if element == 'td' - colspan = $1 if text =~ /\\(\d+)/ - rowspan = $1 if text =~ /\/(\d+)/ - style << "vertical-align:#{ v_align( $& ) };" if text =~ A_VLGN - end - - style << "#{ $1 };" if not @filter_styles and - text.sub!( /\{([^}]*)\}/, '' ) - - lang = $1 if - text.sub!( /\[([^)]+?)\]/, '' ) - - cls = $1 if - text.sub!( /\(([^()]+?)\)/, '' ) - - style << "padding-left:#{ $1.length }em;" if - text.sub!( /([(]+)/, '' ) - - style << "padding-right:#{ $1.length }em;" if text.sub!( /([)]+)/, '' ) - - style << "text-align:#{ h_align( $& ) };" if text =~ A_HLGN - - cls, id = $1, $2 if cls =~ /^(.*?)#(.*)$/ - - atts = '' - atts << " style=\"#{ style.join }\"" unless style.empty? - atts << " class=\"#{ cls }\"" unless cls.to_s.empty? - atts << " lang=\"#{ lang }\"" if lang - atts << " id=\"#{ id }\"" if id - atts << " colspan=\"#{ colspan }\"" if colspan - atts << " rowspan=\"#{ rowspan }\"" if rowspan - - atts - end - - def table( text ) - text << "\n\n" - text.gsub!( /^(?:table(_?#{S}#{A}#{C})\. ?\n)?^(#{A}#{C}\.? ?\|.*?\|)\n\n/m ) do |matches| - - tatts, fullrow = $~[1..2] - tatts = pba( tatts, 'table' ) - rows = [] - - fullrow. - split( /\|$/m ). - delete_if { |x| x.empty? }. - each do |row| - - ratts, row = pba( $1, 'tr' ), $2 if row =~ /^(#{A}#{C}\. )(.*)/m - - cells = [] - row.split( '|' ).each do |cell| - ctyp = 'd' - ctyp = 'h' if cell =~ /^_/ - - catts = '' - catts, cell = pba( $1, 'td' ), $2 if cell =~ /^(_?#{S}#{A}#{C}\. )(.*)/ - - unless cell.strip.empty? - cells << "\t\t\t#{ cell }" - end - end - rows << "\t\t\n#{ cells.join( "\n" ) }\n\t\t" - end - "\t\n#{ rows.join( "\n" ) }\n\t\n\n" - end - end - - def lists( text ) - text.gsub!( /^([#*]+?#{C} .*?)$(?![^#*])/m ) do |match| - lines = match.split( /\n/ ) - last_line = -1 - depth = [] - lines.each_with_index do |line, line_id| - if line =~ /^([#*]+)(#{A}#{C}) (.*)$/m - tl,atts,content = $~[1..3] - if depth.last - if depth.last.length > tl.length - (depth.length - 1).downto(0) do |i| - break if depth[i].length == tl.length - lines[line_id - 1] << "\n\t\\end{#{ lT( depth[i] ) }}\n\t" - depth.pop - end - end - if !depth.last.nil? && !tl.length.nil? && depth.last.length == tl.length - lines[line_id - 1] << '' - end - end - unless depth.last == tl - depth << tl - atts = pba( atts ) - lines[line_id] = "\t\\begin{#{ lT(tl) }}\n\t\\item #{ content }" - else - lines[line_id] = "\t\t\\item #{ content }" - end - last_line = line_id - - elsif line =~ /^\s+\S/ - last_line = line_id - elsif line_id - last_line < 2 and line =~ /^\S/ - last_line = line_id - end - if line_id - last_line > 1 or line_id == lines.length - 1 - depth.delete_if do |v| - lines[last_line] << "\n\t\\end{#{ lT( v ) }}" - end - end - end - lines.join( "\n" ) - end - end - - def lT( text ) - text =~ /\#$/ ? 'enumerate' : 'itemize' - end - - def fold( text ) - text.gsub!( /(.+)\n(?![#*\s|])/, "\\1\\\\\\\\" ) - # text.gsub!( /(.+)\n(?![#*\s|])/, "\\1#{ @fold_lines ? ' ' : '
' }" ) - end - - def block( text ) - pre = false - find = ['bq','h[1-6]','fn\d+'] - - regexp_cue = [] - - lines = text.split( /\n/ ) + [' '] - new_text = - lines.collect do |line| - pre = true if line =~ /<(pre|notextile)>/i - find.each do |tag| - line.gsub!( /^(#{ tag })(#{A}#{C})\.(?::(\S+))? (.*)$/ ) do |m| - tag,atts,cite,content = $~[1..4] - - atts = pba( atts ) - - if tag =~ /fn(\d+)/ - # tag = 'p'; - # atts << " id=\"fn#{ $1 }\"" - regexp_cue << [ /footnote\{#{$1}}/, "footnote{#{content}}" ] - content = "" - end - - if tag =~ /h([1-6])/ - section_type = "sub" * [$1.to_i - 1, 2].min - start = "\t\\#{section_type}section*{" - tend = "}" - end - - if tag == "bq" - cite = check_refs( cite ) - cite = " cite=\"#{ cite }\"" if cite - start = "\t\\begin{quotation}\n\\noindent {\\em "; - tend = "}\n\t\\end{quotation}"; - end - - "#{ start }#{ content }#{ tend }" - end unless pre - end - - #line.gsub!( /^(?!\t|<\/?pre|<\/?notextile|<\/?code|$| )(.*)/, "\t

\\1

" ) - - #line.gsub!( "
", "\n" ) if pre - # pre = false if line =~ /<\/(pre|notextile)>/i - - line - end.join( "\n" ) - text.replace( new_text ) - regexp_cue.each { |pair| text.gsub!(pair.first, pair.last) } - end - - def span( text ) - QTAGS.each do |tt, ht| - ttr = Regexp::quote( tt ) - text.gsub!( - - /(^|\s|\>|[#{PUNCT}{(\[]) - #{ttr} - (#{C}) - (?::(\S+?))? - ([^\s#{ttr}]+?(?:[^\n]|\n(?!\n))*?) - ([#{PUNCT}]*?) - #{ttr} - (?=[\])}]|[#{PUNCT}]+?|<|\s|$)/xm - - ) do |m| - - start,atts,cite,content,tend = $~[1..5] - atts = pba( atts ) - atts << " cite=\"#{ cite }\"" if cite - - "#{ start }{\\#{ ht } #{ content }#{ tend }}" - - end - end - end - - def links( text ) - text.gsub!( / - ([\s\[{(]|[#{PUNCT}])? # $pre - " # start - (#{C}) # $atts - ([^"]+?) # $text - \s? - (?:\(([^)]+?)\)(?="))? # $title - ": - (\S+?) # $url - (\/)? # $slash - ([^\w\/;]*?) # $post - (?=\s|$) - /x ) do |m| - pre,atts,text,title,url,slash,post = $~[1..7] - - url.gsub!(/(\\)(.)/, '\2') - url = check_refs( url ) - - atts = pba( atts ) - atts << " title=\"#{ title }\"" if title - atts = shelve( atts ) if atts - - "#{ pre }\\textit{#{ text }} \\footnote{\\texttt{\\textless #{ url }#{ slash }" + - "\\textgreater}#{ post }}" - end - end - - def get_refs( text ) - text.gsub!( /(^|\s)\[(.+?)\]((?:http:\/\/|javascript:|ftp:\/\/|\/)\S+?)(?=\s|$)/ ) do |m| - flag, url = $~[1..2] - @urlrefs[flag] = url - end - end - - def check_refs( text ) - @urlrefs[text] || text - end - - def image( text ) - text.gsub!( / - \! # opening - (\<|\=|\>)? # optional alignment atts - (#{C}) # optional style,class atts - (?:\. )? # optional dot-space - ([^\s(!]+?) # presume this is the src - \s? # optional space - (?:\(((?:[^\(\)]|\([^\)]+\))+?)\))? # optional title - \! # closing - (?::#{ HYPERLINK })? # optional href - /x ) do |m| - algn,atts,url,title,href,href_a1,href_a2 = $~[1..7] - atts = pba( atts ) - atts << " align=\"#{ i_align( algn ) }\"" if algn - atts << " title=\"#{ title }\"" if title - atts << " alt=\"#{ title }\"" - # size = @getimagesize($url); - # if($size) $atts.= " $size[3]"; - - href = check_refs( href ) if href - url = check_refs( url ) - - out = '' - out << "" if href - out << "" - out << "#{ href_a1 }#{ href_a2 }" if href - - out - end - end - - def code( text ) - text.gsub!( / - (?:^|([\s\(\[{])) # 1 open bracket? - @ # opening - (?:\|(\w+?)\|)? # 2 language - (\S(?:[^\n]|\n(?!\n))*?) # 3 code - @ # closing - (?:$|([\]})])| - (?=[#{PUNCT}]{1,2}| - \s)) # 4 closing bracket? - /x ) do |m| - before,lang,code,after = $~[1..4] - lang = " language=\"#{ lang }\"" if lang - "#{ before }#{ 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\2\3' ) - end - - def footnote_ref( text ) - text.gsub!( /\[([0-9]+?)\](\s)?/, - '\footnote{\1}\2') - #'\1\2' ) - end - - def inline( text ) - image text - links text - code text - span text - end - - def glyphs_deep( text ) - codepre = 0 - offtags = /(?:code|pre|kbd|notextile)/ - if text !~ /<.*>/ - # pgl text - footnote_ref text - else - used_offtags = {} - text.gsub!( /(?:[^<].*?(?=<[^\n]*?>|$)|<[^\n]*?>+)/m ) do |line| - tagline = ( line =~ /^<.*>/ ) - - ## matches are off if we're between ,
 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

\n

around the world

" assert_equal( - "

this was is" + + "

this was is" + " the original string

" + "\n

the new string

" + - "\n

around the world

", + "\n

around the world

", diff(a, b)) end @@ -46,8 +48,8 @@ class DiffTest < Test::Unit::TestCase a = "

this is a paragraph

\n

this is a second paragraph

\n

this is a third paragraph

" b = "

this is a paragraph

\n

this is a third paragraph

" assert_equal( - "

this is a paragraph

\n

this is a second paragraph

" + - "\n

this is a third paragraph

", + "

this is a paragraph

\n

this is a second paragraph

" + + "\n

this 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\n

that } + + %{

My Headline

\n\n

that } + %{Smart Engine GUI?

}, "My Headline\n===========\n\nthat SmartEngineGUI") + assert_markup_parsed_as( + %{

My Headline

\n\n

that } + + %{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\n
def a_method(arg)\n} +
-        %{return ThatWay\n
\n\n

Nice!

}, + %{return ThatWay
\n\n

Nice!

}, 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\t

Textile heading

" + + "

Markdown heading
================

\n\n\n\t

Textile heading

" + "\n\n\n\t

some 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?)

', + "

(What is a wiki?)

", '("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\t

cry " + "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] = ""
@@ -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?(" " ')).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 = ""
+          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 ' :Characters, :data => " :ParseError, :data =>
-            _("Expected closing tag. Unexpected end of file.")})
-          @tokenQueue.push({:type => :Characters, :data => " :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 => " :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": "<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": "foo", + "output": "foo", + "rexml": "foo" + }, + + { + "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": " +#errors +9: missing document type declaration +9: unexpected strong element end tag +13: unexpected b element end tag +18: unexpected em element end tag +22: unexpected i element end tag +26: unexpected u element end tag +35: unexpected strike element end tag +39: unexpected s element end tag +47: unexpected blink element end tag +52: unexpected tt element end tag +58: unexpected pre element end tag +64: unexpected big element end tag +72: unexpected small element end tag +79: unexpected font element end tag +88: unexpected select element end tag +93: unexpected h1 element end tag +98: unexpected h2 element end tag +103: unexpected h3 element end tag +108: unexpected h4 element end tag +113: unexpected h5 element end tag +118: unexpected h6 element end tag +125: unexpected body element end tag +130: unexpected br element end tag +134: unexpected a element end tag +140: unexpected img element end tag +148: unexpected title element end tag +155: unexpected span element end tag +163: unexpected style element end tag +172: unexpected script element end tag +180: unexpected table element end tag +185: unexpected th element end tag +190: unexpected td element end tag +195: unexpected tr element end tag +203: unexpected frame element end tag +210: unexpected area element end tag +217: unexpected link element end tag +225: unexpected param element end tag +230: unexpected hr element end tag +238: unexpected input element end tag +244: unexpected col element end tag +251: unexpected base element end tag +258: unexpected meta element end tag +269: unexpected basefont element end tag +279: unexpected bgsound element end tag +287: unexpected embed element end tag +296: unexpected spacer element end tag +300: unexpected p element end tag +305: unexpected dd element end tag +310: unexpected dt element end tag +320: unexpected caption element end tag +331: unexpected colgroup element end tag +339: unexpected tbody element end tag +347: unexpected tfoot element end tag +355: unexpected thead element end tag +365: unexpected address element end tag +378: unexpected blockquote element end tag +387: unexpected center element end tag +393: unexpected dir element end tag +399: unexpected div element end tag +404: unexpected dl element end tag +415: unexpected fieldset element end tag +425: unexpected listing element end tag +432: unexpected menu element end tag +437: unexpected ol element end tag +442: unexpected ul element end tag +447: unexpected li element end tag +454: unexpected nobr element end tag +460: unexpected wbr element end tag +467: unexpected form element end tag +476: unexpected button element end tag +486: unexpected marquee element end tag +495: unexpected object element end tag +513: unexpected node at end of document +513: unexpected node after body element end tag +513: unexpected frameset element end tag +520: unexpected head element end tag +529: mismatched special end tag iframe +537: unexpected image end tag (that element has no end tag, ever) +547: unexpected isindex end tag (that element has no end tag, ever) +557: mismatched special end tag noembed +568: mismatched special end tag noframes +579: mismatched special end tag noscript +590: unexpected optgroup element end tag +599: unexpected option element end tag +611: unexpected plaintext element end tag +622: mismatched special end tag textarea +#document +| +| +| +|
+ +#data +

+#errors +7: missing document type declaration +20: unexpected node in table context +20: mismatched strong element end tag (no matching start tag) +24: unexpected node in table context +24: mismatched b element end tag (no matching start tag) +29: unexpected node in table context +29: mismatched em element end tag (no matching start tag) +33: unexpected node in table context +33: mismatched i element end tag (no matching start tag) +37: unexpected node in table context +37: mismatched u element end tag (no matching start tag) +46: unexpected node in table context +46: mismatched strike element end tag (no matching start tag) +50: unexpected node in table context +50: mismatched s element end tag (no matching start tag) +58: unexpected node in table context +58: unexpected blink element end tag +63: unexpected node in table context +63: mismatched tt element end tag (no matching start tag) +69: unexpected node in table context +69: mismatched pre element end tag +75: unexpected node in table context +75: mismatched big element end tag (no matching start tag) +83: unexpected node in table context +83: mismatched small element end tag (no matching start tag) +90: unexpected node in table context +90: mismatched font element end tag (no matching start tag) +99: unexpected node in table context +99: mismatched special end tag select +104: unexpected node in table context +104: mismatched h1 element end tag +109: unexpected node in table context +109: mismatched h2 element end tag +114: unexpected node in table context +114: mismatched h3 element end tag +119: unexpected node in table context +119: mismatched h4 element end tag +124: unexpected node in table context +124: mismatched h5 element end tag +129: unexpected node in table context +129: mismatched h6 element end tag +136: unexpected body element end tag +141: unexpected node in table context +141: unexpected br end tag (that element has no end tag, ever) +145: unexpected node in table context +145: mismatched a element end tag (no matching start tag) +151: unexpected node in table context +151: unexpected img end tag (that element has no end tag, ever) +159: unexpected node in table context +159: unexpected title element end tag +166: unexpected node in table context +166: unexpected span element end tag +174: unexpected node in table context +174: unexpected style element end tag +183: unexpected node in table context +183: unexpected script element end tag +196: unexpected th element end tag +201: unexpected td element end tag +206: unexpected tr element end tag +214: unexpected frame element end tag +221: unexpected area end tag (that element has no end tag, ever) +228: unexpected link element end tag +236: unexpected param end tag (that element has no end tag, ever) +241: unexpected hr end tag (that element has no end tag, ever) +249: unexpected input end tag (that element has no end tag, ever) +255: unexpected col element end tag +262: unexpected base element end tag +269: unexpected meta element end tag +280: unexpected basefont end tag (that element has no end tag, ever) +290: unexpected bgsound end tag (that element has no end tag, ever) +298: unexpected embed end tag (that element has no end tag, ever) +307: unexpected spacer end tag (that element has no end tag, ever) +311: mismatched p element end tag +316: mismatched dd element end tag +321: mismatched dt element end tag +331: unexpected caption element end tag +342: unexpected colgroup element end tag +350: unexpected tbody element end tag +358: unexpected tfoot element end tag +366: unexpected thead element end tag +376: mismatched address element end tag +389: mismatched blockquote element end tag +398: mismatched center element end tag +404: mismatched dir element end tag +410: mismatched div element end tag +415: mismatched dl element end tag +426: mismatched fieldset element end tag +436: mismatched listing element end tag +443: mismatched menu element end tag +448: mismatched ol element end tag +453: mismatched ul element end tag +458: mismatched li element end tag +465: mismatched nobr element end tag (no matching start tag) +471: unexpected wbr end tag (that element has no end tag, ever) +478: mismatched form element end tag +524: unexpected node at end of document +524: unexpected node after body element end tag +524: unexpected frameset element end tag +531: unexpected head element end tag +540: mismatched special end tag iframe +548: unexpected image end tag (that element has no end tag, ever) +558: unexpected isindex end tag (that element has no end tag, ever) +568: mismatched special end tag noembed +579: mismatched special end tag noframes +590: mismatched special end tag noscript +601: unexpected optgroup element end tag +610: unexpected option element end tag +622: unexpected plaintext element end tag +633: mismatched special end tag textarea +#document +| +| +| +|
+| +| +| diff --git a/vendor/plugins/HTML5lib/testdata/tree-construction/tests2.dat b/vendor/plugins/HTML5lib/testdata/tree-construction/tests2.dat new file mode 100755 index 00000000..fdf8356a --- /dev/null +++ b/vendor/plugins/HTML5lib/testdata/tree-construction/tests2.dat @@ -0,0 +1,779 @@ +#data +Test +#errors +#document +| +| +| +| +| "Test" + +#data + +#errors +#document +| +| +| +| +| +#errors +#document +| +| +| +| +| ->x --> x +#errors +No DOCTYPE +#document +| +| +| +| ->x --> " +| "x" + +#data +x +#errors +No DOCTYPE +Unexpected end of file +#document +| +| +|