2023年政策修订增补工作正在进行中,欢迎参与!
  • Moegirl.ICU:萌娘百科流亡社群 581077156(QQ),欢迎对萌娘百科运营感到失望的编辑者加入
  • Moegirl.ICU:账号认领正在试运行,有意者请参照账号认领流程

User:東東君/js/ruby.js

萌娘百科,万物皆可萌的百科全书!转载请标注来源页面的网页链接,并声明引自萌娘百科。内容不可商用。
跳转到导航 跳转到搜索

注意:在保存之后,您可能需要清除浏览器缓存才能看到所作出的变更的影响。

  • Firefox/Safari:按住“Shift”的同时单击“刷新”,或按“Ctrl-F5”或“Ctrl-R”(Mac为“⌘-R”)
  • Google Chrome:按“Ctrl-Shift-R”(Mac为“⌘-Shift-R”)
  • Internet Explorer:按住“Ctrl”的同时单击“刷新”,或按“Ctrl-F5”
  • Opera:在“工具→首选项”中清除缓存
如果您已登录但该页面出现未登录状态,请尝试在地址栏的地址最后添加代码?_=1来访问最新页面。
添加代码后的本页地址如下:-{R|https://moegirl.icu/User:%E6%9D%B1%E6%9D%B1%E5%90%9B/js/ruby.js?_=1}-
/*

该插件是一个歌词注音插件,使用了雅虎日本的文本解析API:ルビ振り。
载入该插件后,在编辑页面右上角的搜索栏左边“更多”中,将添加一个“注音工具”按钮,点击即可呼出该插件的操作界面。
说明一下主要使用方式以及4个功能按钮:
基本用法:左边为一个编辑框,你可以将要注音的歌词粘贴到这个编辑框,点击“添加注音”按钮。待处理完成后,编辑框的将变成ruby注音后的歌词,并在右侧显示预览。

按钮介绍:
获取歌词:将从源代码中第一个指定格式[1] 的LyricsKai模板或其衍生模板的original参数中获取歌词到编辑框中,若源代码中不存在则提示。
添加注音:对编辑框中的文字执行注音,并在完成后显示预览。
          需要注意要注音的内容中不能包含日语当用汉字中不存在的汉字(如日语中不使用的中文简体字等),或一些特殊字符(如心形等),
          如果包含将导致注音失败,并提示。
提交歌词:将编辑框中的文字提交至页面中第一个指定格式[1] 的LyricsKai模板或其衍生模板的original参数中,这将覆盖original参数中原本的内容,若源代码中不存在则提示。
复制歌词:将编辑框中的文字复制至剪切板。
打开转换列表页面:详见此页面:https://zh.moegirl.org.cn/User:東東君/js/ruby.js/转换列表

开关介绍:
书面语注音:对一些单词进行书面语的注音,如:「明日」(あした => あす),多用于一些比较文艺的歌。
      转换规则位于转换列表页面。

[1]:模板传入了original和translated参数,并且original在前,translated在后,这也是目前使用LyricsKai模板的最普遍形式。
 
 @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 @@@@ 特别感谢User:Nzh21 提供的服务器资源 @@@@
 @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
*/

if (/((?!\.css|模块|module).+)action=(edit|submit)/.test(location.href)) {
  let common = []
  let kakikotoba = []
 
  $.get("https://zh.moegirl.org.cn/User:東東君/js/ruby.js/转换列表?action=render")
	.done(resultUrl => {
		const doc = $(resultUrl)
		const findRubyTransferGroupByName = name => Array.from(doc.find(`#rubyTransferGroup-${name} > li`)).map(item => item.textContent.split('、'))
		common = findRubyTransferGroupByName('common')
		kakikotoba = findRubyTransferGroupByName('kakikotoba')
	})

  var escapes = /[àâäèéêëîïôœùûüÿçÀÂÄÈÉÊËÎÏÔŒÙÛÜŸÇ?!·♡⸱]/g

  $(function () {
    var rubyHtml = [
      '  <div id="widget-ruby" style="display:none;">',
      '    <div id="widget-ruby-hide">×</div>',
      '    <div class="ruby-left">',
      '      <div class="ruby-editor">',
      '        <textarea id="ruby-editor-body" lang="ja"></textarea>',
      '        <div class="ruby-btn-group">',
      '          <button id="ruby-getText" title="从模板中获取歌词">获取歌词</button>',
      '          <button id="ruby-execute" title="对内容添加注音">添加注音</button>',
      '          <button id="ruby-update" title="将歌词提交至模板">提交歌词</button>',
      '          <button id="ruby-copy" title="复制歌词至剪切板">复制歌词</button>',
      '		   <label for="ruby-kakikotoba" title="是否应用书面语注音规则,如:「明日」(あした => あす),多用于一些比较文艺的歌" style="display:inline-table; margin-left:10px; background:#eee; padding:3px; border-radius:3px;">',
      '		     <input type="checkbox" id="ruby-kakikotoba" style="vertical-align:-2px;" />',
      '            <span style="font-size:14px;">书面语注音</span>',
      '		   </label>',
      '          <button id="ruby-gotoTransferList" title="由于注音API返回一些读音在歌词中并不常用,需要将其替换为常用读音,你可以在转换列表中添加新的转换规则">打开转换列表页面</button>',
      '        </div>',
      '      </div>',
      '    </div>',
      '    <div class="ruby-right">',
      '      <div class="ruby-view" lang="ja"></div>',
      '    </div>',
      '  </div>',
      '  <style>',
      '    #widget-ruby{',
      '      position: fixed;',
      '      top: 0;',
      '      left: 0;',
      '      bottom: 0;',
      '      right: 0;',
      '      z-index: 100;',
      '      background-color: rgba(0, 0, 0, 0.3);',
      '    }',
      '    #widget-ruby-hide{',
      '      font-size: 30px;',
      '      font-weight: bold;',
      '      color: white;',
      '      font-family: SimSun;',
      '      position: fixed;',
      '      top: 10px;',
      '      right: 20px;',
      '      transition: transform 0.3s;',
      '      z-index: 10001;',
      '      cursor: pointer',
      '    }',
      '    #widget-ruby-hide:hover{',
      '      transform: rotate(90deg);',
      '    }',
      '    .ruby-left, .ruby-right{',
      '      width: 50%;',
      '      height: 100%;',
      '      float: left;',
      '      position: relative;',
      '    }',
      '    .ruby-editor-body, .ruby-btn-group{',
      '      margin: 10px;',
      '    }',
      '    .ruby-editor, .ruby-view{',
      '      width: 450px;',
      '      height: 80%;',
      '      min-height: 300px;',
      '      position: absolute;',
      '      top: 0; left: 0; bottom: 0; right: 0;',
      '      margin: auto;',
      '      background-color: white;',
      '      border: 3px #ccc solid;',
      '    }',
      '    #ruby-editor-body{',
      '      width: 100%;',
      '      height: 100%;',
      '      box-sizing: border-box;',
      '      resize: none;',
      '      padding: 5px;',
      '      outline: none;',
      '    }',
      '    .ruby-btn-group{',
      '      height: 20%;',
      '      min-height: 70px;',
      '    }',
      '    .ruby-view{',
      '      overflow: auto;',
      '      padding: 5px;',
      '    }',
      '  </style>'].join('')

    $('body').append(rubyHtml)
   
    $('#p-cactions ul').append('<li id="btn-ruby"><a title="为日语歌词进行注音">注音工具</a></li>')
    $('#btn-ruby').click(function () {
      $('#widget-ruby').fadeIn(200)
    })
    
    const intervalKey = setInterval(() => {
    	if ($('#mw-notification-area').length !== 0) {
    		document.body.appendChild($('#mw-notification-area').get(0))	// 防止提示框被遮挡
    		clearInterval(intervalKey)
    	}
    }, 300)
    

    var editor = $('#ruby-editor-body'),
      view = $('.ruby-view').eq(0),
      wikiEditor = $('#wpTextbox1'),
      hideBtn = $('#widget-ruby-hide'),
      getTextBtn = $('#ruby-getText'),
      executeBtn = $('#ruby-execute'),
      updateBtn = $('#ruby-update'),
      copyBtn = $('#ruby-copy'),
      gotoTransferListBtn = $('#ruby-gotoTransferList')

    var messages = {
      notFound: '代码中并未找到歌词模板(LyricsKai及其衍生模板)!',
      badFormat: '歌词模板的格式不正确或不受支持,请手动复制粘贴!',
      emptyText: '要注音的内容不能为空!',
      badText: '无法注音,请检查内容中是否包含特殊字符或非日语当用汉字、除拉丁字母以外的文字',
      timeout: '请求超时!'
    }

	gotoTransferListBtn.click(() => window.open('https://zh.moegirl.org.cn/User:東東君/js/ruby.js/转换列表', '_blank'))

    hideBtn.click(function () {
      $('#widget-ruby').fadeOut(200)
    })

    var regex = /(\{\{[Ll]yricsKai[\s\S]*?\|original=)([\s\S]*?)(\|translated)/
    getTextBtn.click(function () {
      var codeContent = wikiEditor.val()
      if (!/\{\{[Ll]yricsKai/.test(codeContent)) {
        mw.notify(messages.notFound, { type: 'error' })
      } else {
        if (!codeContent.match(regex)) {
          mw.notify(messages.badFormat, { type: 'error' })
        } else {
          var text = codeContent.match(regex)[2]
          editor.val(text.trim())
        }
      }
    })

    executeBtn.click(function () {
      var text = editor.val().trim()
      if (text.length == 0) {
        mw.notify(messages.emptyText, { type: 'error' })
      } else {
        function ruby(kanji, kana) {
          return '{{photrans|' + kanji + '|' + kana + '}}'
        }

        text = text.replace(escapes, function (s) { return '!UNICODE(' + escape(s).replace('%', '#') + ')' })

        editor.attr('disabled', 'disabled')
        $.ajax({
          type: 'post',
          url: 'https://api.nzh21.site/yahooapis/FuriganaService/V2/furigana',
          contentType: 'application/json',
          headers: {
          	'x-ua': "Yahoo AppID: dj00aiZpPXE2azZNYXFyR29kSSZzPWNvbnN1bWVyc2VjcmV0Jng9ODY-"
          },
          data: JSON.stringify({
			  "id": "1234-1",
			  "jsonrpc": "2.0",
			  "method": "jlp.furiganaservice.furigana",
			  "params": {
			    "q": text,
			    "grade": 1,
			    "appid": "dj00aiZpPXE2azZNYXFyR29kSSZzPWNvbnN1bWVyc2VjcmV0Jng9ODY-"
			  }
			}),
          timeout: '15000',
        }).always(function () {
          editor.removeAttr('disabled')
        }).done(function (data) {
          if (data.Error) {
            mw.notify(messages.badText, { type: 'error' })
          } else {
            var wordList = data.result.word
            var result = wordList.reduce(function (result, item) {
              return result + (() => {
              	if (item.furigana) {
              		if (item.subword) {
              			return item.subword
              				.map(item => item.furigana !== item.surface ? ruby(item.surface, item.furigana) : item.surface)
              				.join("")
              		}
              		return ruby(item.surface, item.furigana)
              	}
              	return item.surface
              })()
            }, "")

            function rubyReplace(patterns) {
              for (var i = 0, len = patterns.length; i < len; i++) {
                var regex = new RegExp('(\\{\\{photrans\\|' + patterns[i][0] + '\\|)' + patterns[i][1] + '\\}\\}', 'g')
                result = result.replace(regex, '$1' + patterns[i][2] + '}}')
              }
            }

            rubyReplace(common)
            $('#ruby-kakikotoba').prop('checked') && rubyReplace(kakikotoba)

            result = result.replace(/!UNICODE\((.+?)\)/g, function (s, s1) { return unescape(s1.replace('#', '%')) })

            editor.val(result)

            var viewHtml = result.replace(/\n/g, '<br>').replace(/\{\{photrans\|(.+?)\|(.+?)\}\}/g, '<ruby>$1<rt>$2</rt></ruby>')

            view.html(viewHtml)
          }
        }).fail(function (e) {
          mw.notify(messages.timeout, { type: 'error' })
        })
      }
    })

    updateBtn.click(function () {
      var codeContent = wikiEditor.val()
      var ruby = '\n' + editor.val() + '\n\n'
      if (!regex.test(codeContent)) {
        mw.notify(messages.badFormat, { type: 'error' })
      } else {
        if (editor.attr('disabled') == 'disabled') {
          mw.notify('请先等待注音执行完毕')
          return
        }
        wikiEditor.val(codeContent.replace(regex, '{{photrans/button}}\n' + '$1' + ruby + '$3'))
        mw.notify('提交成功!', { type: 'warn' })
        hideBtn.click()
      }
    })

    copyBtn.click(function () {
      editor.focus()
      document.execCommand('selectAll')
      document.execCommand('copy')
      mw.notify('已复制至剪切板', { type: 'warn' })
    })
  })
}