require 'js' require 'json' require 'uri' # 処理中画面の表示/非表示 # @param f [Boolean] def in_progress(&block) @in_progress ||= [] @in_progress.push true JSrb.timeout(0.3) do Document.get_element_by_id('in-progress').style.display = 'block' unless @in_progress.empty? end JSrb.timeout(0) do block.call @in_progress.shift Document.get_element_by_id('in-progress').style.display = 'none' if @in_progress.empty? end end # バージョンファイルを読み終わってから main を呼ぶ def start Document.get_element_by_id('in-progress').style.display = 'none' res = JS.global.fetch('/json/versions.json').await unless res.ok JS.global.alert("error #{res.status}") raise end json = res.json.await @available_versions = JSON.parse(JS.global.JSON.stringify(json)) main end # 開始 def main @param_cache = {} uri = URI.parse(JS.global.location.to_s) q = URI.decode_www_form(uri.query.to_s).to_h @type = q['type'] || uri.path.sub(/index\.html/, '').split('/').compact.reject(&:empty?).last || 'mysqld' list = Document.get_element_by_id('menu').query_selector_all('li a') list.each do |a| a.add_event_listener('click') do @type = URI.parse(a.href).path.split('/')[1] redisplay end end vers = q['vers'].to_s.split(/,/) diff = q['diff'] == 'true' e_diff = diff_element e_diff.checked = diff e_diff.add_event_listener('change'){change_diff} plugin = q['plugin'] == 'true' e_plugin = plugin_element e_plugin.checked = plugin e_plugin.add_event_listener('change'){redisplay} create_version_list vers.each{|ver| add_input(ver.strip)} add_input change_diff redisplay end # 差分表示切り替え def change_diff only_diff = diff_element.checked == true Document.styleSheets[0].cssRules.each do |css| if css[:selectorText].to_s == 'tr.same-value' css.style.display = only_diff ? 'none' : JS::Null end end set_location end # バージョンリストの datalist 要素作成 def create_version_list version_list = create_element('datalist', id: 'version_list') @available_versions.each do |v| o = create_element('option', value: v, text: v) version_list.appendChild(o) end Document.get_element_by_id('versions').append_child(version_list) end # HTML要素作成 # @param type [String] 要素名 # @param opts [Hash] 要素の属性 def create_element(type, **opts) e = Document.create_element(type) opts.each do |k, v| e[k.to_s] = v e.set_attribute(k.to_s, v) end e end # バージョン入力用の input 要素を作成 # @param ver [String] バージョン def add_input(ver=nil) v = create_element('input', name: 'version', className: 'version', type: 'search', list: 'version_list') v[:value] = ver if ver Document.get_element_by_id('versions').append_child(v) v.add_event_listener('keypress') do |e| redisplay if e.key == 'Enter' end v.add_event_listener('change') do redisplay end end # バージョン入力用要素一覧を返す # @return [Array] def version_elements Document.get_elements_by_name('version') end # 「Difference Only」要素 # @return [Array] def diff_element Document.get_element_by_id('only_difference') end # 「Include plugins」要素 # @return [Array] def plugin_element Document.get_element_by_id('include_plugins') end # 再表示 def redisplay in_progress do next if @in_redisplay @in_redisplay = true set_location Document.get_element_by_id('param_table')&.remove @versions = [] version_elements.each_with_index do |v, i| ver = v[:value] = v[:value].strip if ver.empty? v.remove if i < version_elements.size - 1 next end unless @available_versions.include?(ver) ver = @available_versions.select{_1.start_with?(ver+'.')}.first end unless ver v[:className] = 'invalid-version' next end v[:className] = '' read_params(ver) @versions.push ver end add_input if version_elements.empty? || !version_elements.last.value.empty? display_table ensure @in_redisplay = false end end # バージョン ver のパラメータ JSON ファイルを読み込む # @param ver [String] バージョン def read_params(ver) JSrb.sleep 0 # switch fiber plugin = include_plugins? return if @param_cache[[@type, ver, plugin]] file = plugin ? 'all.json' : 'base.json' res = JS.global.fetch("/json/#{ver}/#{@type}/#{file}").await unless res.ok JS.global.alert("error #{res.status}") raise "error #{res.status}" end json = res.json.await @param_cache[[@type, ver, plugin]] = JSON.parse(JS.global.JSON.stringify(json)) end # HTML 要素を元に URL を設定する def set_location uri = URI.parse(JS.global.location.to_s) uri.path = "/#{@type}/" vers = version_elements.map(&:value).reject(&:empty?).join(',') diff = diff_element.checked == true plugin = plugin_element.checked == true q = URI.decode_www_form(uri.query.to_s).to_h q.reject!{|k,| ['type', 'vers', 'diff', 'plugin'].include? k} q['vers'] = vers unless vers.empty? q['diff'] = 'true' if diff q['plugin'] = 'true' if plugin query = URI.encode_www_form(q).gsub(/%2C/, ',') uri.query = query.empty? ? nil : query JS.global.window.history.replace_state('', '', uri.to_s) list = Document.get_element_by_id('menu').query_selector_all('li a') list.each do |a| href = a.href a.href = href.sub(/\?.*|\z/, '?'+query) unless query.empty? if URI.parse(href).path.start_with? "/#{@type}/" a.parent_element[:className] = 'current' Document.title = "#{a[:textContent]} / MySQL Parameters" else a.parent_element[:className] = JS::Null end end JSrb.sleep 0 # switch fiber end # パラメータテーブルを表示する def display_table versions = @versions plugin = include_plugins? table = Document.get_element_by_id('param_table') table&.remove table = create_element('table', id: 'param_table') tr = create_element('tr') tr.append_child(create_element('th', textContent: 'Parameter')) versions.each do |ver| next unless @param_cache[[@type, ver, plugin]] th = create_element('th', textContent: ver) tr.append_child(th) end table.append_child(tr) cache_keys = versions.map{|v| [@type, v, plugin]} params = @param_cache.values_at(*cache_keys).compact.map(&:keys).flatten.sort.uniq Document.get_element_by_id('params').append_child(table) i = 0 params.each do |name| i += 1 JSrb.sleep 0 if i % 15 == 0 # switch fiber same_value = @param_cache.values_at(*cache_keys).compact.map{_1[name]}.uniq.size == 1 tr = create_element('tr') tr.set_attribute('class', 'same-value') if same_value tr.append_child(create_element('td', className: 'parameter-name', textContent: name)) first = true prev = nil values = [] versions.each do |ver| next unless @param_cache[[@type, ver, plugin]] value = @param_cache[[@type, ver, plugin]][name] values.push value td = create_element('td', className: 'parameter-value') if value td[:textContent] = value td[:className] = 'parameter-value difference' if !first && prev != value else td[:className] = 'parameter-value unexist' end tr.append_child(td) prev = value first = false end table.append_child(tr) end end def include_plugins? plugin_element.checked == true end