Mouse dictionary helper

Mouse Dictionary で引いた結果をAnkiwebのdeckに登録する君

目前為 2021-07-09 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         Mouse dictionary helper
// @namespace    http://tampermonkey.net/
// @version      0.4
// @description  Mouse Dictionary で引いた結果をAnkiwebのdeckに登録する君
// @author       @ozero
// @match        *://*/*
// @grant       GM.setValue
// @grant       GM.getValue
// ==/UserScript==

'use strict';

/*
# Mouse Dictionary で引いた結果をAnkiwebのdeckに登録する君

英単語の意味だけ調べて済ませてたか、手作業で辞書から単語帳にコピペしてた人へ。辞書で引いた内容を即Anklwebの単語帳に登録できます。

1. Mouse dictinaryで単語の意味を表示する
2. Mouse dictinaryウィンドウをクリック
3. Ankiwebの単語登録画面が開いて、入力欄にさっき調べてた単語&意味が自動入力される
4. あとは自動入力の内容を確かめて、Saveクリックして閉じる

なお↑の 2. で Ctrl + クリック したら、自動入力に加えて「自動保存&Ankiwebのタブ自動クローズ」までやります。

## セットアップ

まずブラウザにTamperMonkey拡張を入れ、このスクリプトもインストールします。次に以下の手順で連携させます。

1. 拡張 Mouse Dictionary のオプションを開く
2. 設定を開く
3. 上級設定を開く
4. 項目「HTMLテンプレート」をみる
5. テンプレート「Mouse Dictionaryウィンドウ全体」のdivタグに、クラス名 `mouse_dictonary_helper` を足す

足した結果はこうなる

```
<div class="notranslate mouse_dictonary_helper"
     style="all:initial;
            {{systemStyles}}
            width: {{width}}px;
            height: {{height}}px;
            position: fixed;
            overflow-x: hidden;
            overflow-y: {{scroll}};
            top: 5px;
            background-color: {{backgroundColor}};
            z-index: 2147483646;
            padding: 2px 4px 2px 4px;
            border: 1px solid #A0A0A0;"
>
</div>
```

んで、設定を保存してセットアップ終わり。


## 実際の動作(手動追加)

1. Ankiweb にログインする
2. ページを開いて、Mouse Dictionary 拡張を実行
3. 単語をダブルクリックで選択固定
4. Mouse Dictionary ウィンドウをクリック
5. Ankiweb のデッキ追加ページが開いて、Mouse Dictinary で表示されていた内容がAutofillされる


## 実際の動作(自動追加&Ankiwebウィンドウ自動クローズ)

1. Ankiweb にログインする
2. ページを開いて、Mouse Dictionary 拡張を実行
3. 単語をダブルクリックで選択固定
4. Mouse Dictionary ウィンドウを Ctrl + クリック
5. Ankiweb のデッキ追加ページが開いて、Mouse Dictinary で表示されていた内容がAutofillされる
6. 自動でAnkiweb のデッキ追加ページのSaveボタンがクリックされる
7. 自動でAnkiweb のデッキ追加ページがクローズされる


## なんでコンテンツがドメインまたいでるの

`GM.setValue`, `GM.getValue` で userscript実行環境側のストレージを経由して受け渡しをしています。

- https://wiki.greasespot.net/GM.setValue
- https://wiki.greasespot.net/GM.getValue


*/

//Mouse dictionaryウィンドウクリック時の処理及び関連付け
( () => {
  let mouse_dictonary_helper = {};

  //Mouse dictionaryウィンドウの内容をGM.setValueでGMストレージに保存
  mouse_dictonary_helper.gsetval = (event) => {
    let mdh_element = document.getElementsByClassName('mouse_dictonary_helper')
    if(!mdh_element[0]){
      return false;
    }
    let content = mdh_element[0].innerText;
    GM.setValue( "mdhtmp", content );
    //console.log("set mdh", content);

    //Ctrlキー押下有無(→AnkiwebでAutofill後自動save&close)も持っとく
    if(event.ctrlKey ){
      GM.setValue( "mdhAutosave", "yes" );//set autosave flag
    }else{
      GM.setValue( "mdhAutosave", "no" );
    }

    //ついでにAnkiwebのデッキ追加ウィンドウを開く
    window.open('https://ankiuser.net/edit/', '_blank');

    return;
  };

  //Mouse dictionaryウィンドウの有無をpolling(200ms毎)してクリックイベントを追加
  const mdh_window_poll = () => {
    let mdh_element = document.getElementsByClassName('mouse_dictonary_helper')
    if(mdh_element[0]){
      mdh_element[0].addEventListener('click', mouse_dictonary_helper.gsetval, false);
      console.log("mdh addEventListener");
      return true;
    }else{
      setTimeout(()=>{
        mdh_window_poll();
      }, 200);
      //console.log("mdh polling");
      return false;
    }
  }
  mdh_window_poll();//Pollingの起動

} )();


//Ankiwebのデッキ追加URLを開いた際の入力欄autofill
(async function(){

  if(window.location.href !== "https://ankiuser.net/edit/"){
    return;
  }

  const mdhtmp = await GM.getValue( "mdhtmp", "" );
  if(mdhtmp === ""){
    return;
  }
  //console.log("get mdh", mdhtmp);

  //Util
  const sleep = (msec) => {
    return new Promise(resolve => setTimeout(resolve, msec))
  };

  //wait for form-dom loaded
  let loaded = false;
  while(!loaded){
    let el_front = document.getElementById("f0");
    if(!el_front){
      await sleep(100);
      continue;
    }
    let el_back = document.getElementById("f1");
    if(!el_back){
      await sleep(100);
      continue;
    }
    loaded = true;
  }

  //Autofill
  const mdhtmp_2 = mdhtmp.split("\n\n");
  const mdhtmp_3 = mdhtmp_2.shift().split("\n");
  const head = mdhtmp_3.shift().toLowerCase();//登録する単語は小文字に揃える
  const body = mdhtmp_3.join("\n");
  let el_front = document.getElementById("f0");
  let el_back = document.getElementById("f1");
  el_front.innerText = head;
  el_back.innerText = body;

  //Clear
  GM.setValue( "mdhtmp", "" );

  //もし既知の単語ならAlert出してここで止める
  const mdhKnownwordJson = await GM.getValue( "mdhKnownword", "{}" );
  let mdhKnownword = JSON.parse(mdhKnownwordJson);
  if(mdhKnownword[head]){
    document.title = "⚠二重登録";
    alert("二重登録: 単語 '" + head + "' は、過去にデッキに登録されています");
    return;
  }else{
    //既知の単語に追加する
    mdhKnownword[head] = true;
    GM.setValue( "mdhKnownword", JSON.stringify(mdhKnownword) );
  }

  //Autosave & close
  const mdhAutosave = await GM.getValue( "mdhAutosave", "no" );
  if(mdhAutosave !== "yes"){
    return;
  }
  GM.setValue( "mdhAutosave", "" );//clear autosave flag

  //Tell Autosave&close to user
  document.getElementById("msg").innerText="Auto Save & Auto Close ENABLED.";
  document.getElementById("msg").style.display = "block";

  await sleep(500);
  let el_btn = document.querySelector("body > main > p > button");
  el_btn.click();

  await sleep(1000);
  window.close();

})();


//Ankiwebの登録済み単語一覧を読み取ってGM.setValueでローカルに格納しておくやつ
(async function(){

  if(window.location.href !== "https://ankiweb.net/search/"){
    return;
  }

  let el_words = document.querySelectorAll("body > main > table > tbody > tr > td");
  if(el_words.length < 1){
    return;
  }

  //登録済み単語一覧をストレージに格納する
  let words = {};
  for(let el_w1 of el_words){
    let el_w2 = ("" + el_w1.innerText).split(" / ");
    if(el_w2.length < 2){
      continue;
    }
    let word = el_w2[0].toLowerCase();
    words[word] = true;
  }
  GM.setValue( "mdhKnownword", JSON.stringify(words) );

  return;
})();

//console.log("mdh loaded");