require "execjs" require "nokogiri" require "net/http" require "json" require "cgi" class KatexRenderer def initialize(source) @context = ExecJS.compile(source) @inline_cache = {} @display_cache = {} end def render(display, string) cache = display ? @display_cache : @inline_cache comment = display ? "display" : string string = CGI.unescapeHTML string cache.fetch(string) do puts " Rendering #{comment}" options = { "throwOnError" => false, "displayMode" => display } cache[string] = @context.call("katex.renderToString", string, options) end end def substitute(content) rendered = content.gsub /\\\(((?:[^\\]|\\[^\)])*)\\\)/ do |match| render(false, $~[1]) end rendered = rendered.gsub /\$\$((?:[^\$]|$[^\$])*)\$\$/ do |match| render(true, $~[1]) end return rendered end end ExecJS.runtime = ExecJS::Runtimes::Duktape renderer = KatexRenderer.new(Net::HTTP.get(URI("https://static.danilafe.com/katex/katex.min.js"))) files = ARGV[0..-1] files.each do |file| puts "Rendering file: #{file}" document = Nokogiri::HTML.parse(File.open(file)) document.search('//*[not(ancestor-or-self::code or ancestor-or-self::script)]/text()').each do |t| t.replace(renderer.substitute(t.content)) end File.write(file, document.to_html(encoding: 'UTF-8')) end