Google Chrome 拡張機能の開発 - 5 - コピーした履歴をわかるようにする

<font color="#0000ff"><b>やりたいこと：コピーしたものの履歴をわかるようにする</b></font>
<font color="#ff0000"><b>やれたこと：オプションページにただ表示するだけ</b></font>
[【ソースコードも一応GitHubへアップしてあります】](https://github.com/rrryutaro/RChromeExtension)(Git, GitHub利用はド初心者です)
一応公開もしています。【[R Chrome Extension - Chrome ウェブストア](https://chrome.google.com/webstore/detail/r-chrome-extension/nmabekdmfpblgbgplficnahfkekbggil)】

# 前回の修正 - コンテンツスクリプト
前回、とりあえずやっつけだったのでいくつかの動きを改善します。

```js:contentscripts.js(旧)
let selectionTarget;
let selectionText;
$("*").hover(e=>{
  selectionTarget = e.target;
},e=>{});
$("*").bind("contextmenu",()=>{
  selectionText = selectionTarget.innerText;
});
chrome.extension.onRequest.addListener((request, sender, sendResponse)=>{
  if (request.command==="Copy")
    sendResponse({"text": selectionText});
});
```
次のように変更しました。

```js:contentscripts.js(新)
let hoverTarget;
let selectionTarget;
$("*").hover(e=>{
  hoverTarget = e.target;
},e=>{});
$("*").bind("contextmenu",()=>{
  if (selectionTarget !== hoverTarget){
    selectionTarget = hoverTarget;
  }
});
chrome.extension.onRequest.addListener((request, sender, sendResponse)=>{
  if (request.command==="Copy" & selectionTarget != null){
    sendResponse({"obj": $(selectionTarget)[0].outerHTML});
  }
});
```
1. 前回、とりあえず、テキストデータのみ渡すために、innerTextを途中で引数に渡していましたが、今回は注目している要素をそのまま扱うようにしています。
2. 今一つイベントがどのように飛んでいるか把握できていないのですが、何度もよばれるため、要素が違う場合のみ、処理を継続するようにしました。
3. 前回、うまく行く場合と、うまくいかない場合がありましたが、これもonRequestが何度もよばれていて、selectionTargetがundefinedの場合があるため、nullチェックするようにしました。
4. jQueryの仕様もしくはChrome Extensionの仕様か、sendResponseでselectionTargetをそのまま渡すと、想定したオブジェクトと異なるものになるため、ともかく欲しい形でデータを送り返すために、要素を作成して、そのouterHTMLを渡すようにしています。

ここまでで、無知ゆえに、色々とアホみたいな試行錯誤をした挙句がこの形です。

# 前回の修正 - バッググラウンド
```js:background.js(旧)
chrome.contextMenus.create({title: "コピー", onclick: info=>{
  chrome.tabs.getSelected(null, tab=>{
    chrome.tabs.sendRequest(tab.id, {command: "Copy"}, response=>{
      console.log(response.text);
      const txt = $("#text");
      txt.val(response.text);
      txt.select();
      document.execCommand("copy");
    });
  });
}});
```
```js:background.js(新)
chrome.contextMenus.create({title: "コピー", id: "RCopy", contexts: ["all"]});

chrome.contextMenus.create({title: "タイトルコピー", parentId: "RCopy", contexts: ["all"], onclick: info=>{
  chrome.tabs.getSelected(null, tab=>{
  	let format="[{1}]({0})";
  	//chrome.storage.sync.get(["CopyFormat"],items=>{
  	//  const data = items.CopyFormat;
  	//  if (data == null){ format = "[{1}]({0})"; }
  	//  else { format = data.CopyFormat[data.SelectedIndex]; }
  	//});
	const val = formatString(format, [tab.url, tab.title]);
  	//console.log(val);
  	AddSaveData(val);
  	const txt = $("#text");
  	txt.val(val);
  	txt.select();
  	document.execCommand("copy");
  });
}});

chrome.contextMenus.create({title: "オートコピー", parentId: "RCopy", contexts: ["all"], onclick: info=>{
  chrome.tabs.getSelected(null, tab=>{
    chrome.tabs.sendRequest(tab.id, {command: "Copy"}, response=>{
      const html = $(response.obj)[0]; 
      console.log(html);
      console.log(html.innerText);
      switch (html.nodeName){
      	case "IMG":
      	  console.log(html.outerHTML);
      	  AddSaveData("IMG", html.outerHTML);
      	  break;
      	default:
      	  console.log(html.nodeName);
      	  if (html.innerText != null)
      	  	AddSaveData("TEXT", html.innerText);
      }
    });
  });
}});
```

1. メニューを階層化しました。
2. ついでなので、タイトルとURLのコピーもこちらからもできるように
3. オートコピーで、要素に合わせて適切なコピーができるような判定を行う構造にしました。とりあえず、IMGタグと、テキストデータが取れるもの程度しかまだ対応していません。
4. データの保存とクリップボードへのコピー関数を作成

```js:utility.js(新規作成)
//テキストエリアに文字列を貼りつけて、選択状態にしてクリップボードへコピーする
const copyText = val=>{
  const txt = $("#text");
  txt.val(val);
  txt.select();
  document.execCommand("copy");
}
//数値変換
//TODO: 配列要素の扱いで絶対にエラーを起こさないような対処が必要
const pi = x=>{
  return parseInt(x.substr(1, x.length-2), 10);
}
//文字列フォーマット "{}"で囲われた範囲を中の数値と対応する配列の文字列で置き換え
//TODO: エスケープや例外への対処が必要
const formatString = (formatValue, replaceStringArray)=>{
  formatValue.match(/({(\d)+})/g).forEach((x)=>{
    formatValue = formatValue.replace(x, replaceStringArray[pi(x)]);
  });
  return formatValue;
}
//オートコピーのデータを保存し、クリップボードへコピーする
const AddSaveData = (typeName, addData)=>{
  chrome.storage.sync.get(["CopyData"],items=>{
  	let saveData = items.CopyData;
    if (saveData==null)
      saveData = [{}];
    saveData.push({Type: typeName, Data: addData});
    chrome.storage.sync.set({ CopyData: saveData });
  });
  copyText(addData);
}
```

1. popup.jsに定義していた文字列フォーマットなどを引越ししました。
2. コピー時のデータを追加保存するようにしました。
3. ところどころ、決め打ちしている所があるので、問題ありですが、今は放置です。

# オプションページの追加
```json:manifest.json(修正:抜粋)
 "options_page": "options.html"
```
```html:options.html(新規作成)
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <script src="jquery-3.1.1.min.js"></script>
    <script src="utility.js"></script>
    <script src="options.js"></script>
  </head>
  <body>
    <table id="copyTable" width="100%" border="1">
    </table>
  </body>
</html>
```
```js:options.js(新規作成)
$(()=>{
  chrome.storage.sync.get(["CopyData"],items=>{
    items.CopyData.forEach(x=>{
      const tbl = $("#copyTable");
      switch (x.Type){
      	case "IMG":
      	  tbl.append(formatString("<tr><td>{0}</td></tr>", [x.Data]));
      	  break;
      	default:
      	  tbl.append(formatString("<tr><td><pre>{0}</pre></td></tr>", [x.Data]));
      	  break;
      }
    });
  });
});
```

とりあえず、テーブルにコピーしたデータをただ表示するだけです。見栄えについては後々よくしたいと思います。
今後の機能追加で、ここから再度クリップボードへコピーするなどできるようにすれば、クリップボード履歴としても使えるようになりそうです。（既にそうしたものはありますが）

# 動かしてみた感じ
前回の記事のコード部分と適当にイメージ部分をコピー
![image](https://qiita-image-store.s3.amazonaws.com/0/156225/5aca7de2-4a83-af81-53d0-01c51da486c7.png)

最初が"undefined"がコピーされていたりと、まだ問題はあったりしますね。

以上

|前回|一覧|次回|
|:--|:--:|--:|
|[マウスカーソルにある位置のものをコピー](http://qiita.com/rrryutaro/items/f4f129dacdf22ce43d14)|[シリーズ一覧](http://qiita.com/rrryutaro/items/015b67568ddd947ec2b5)|[クリップボード履歴を一応それらしく使えるようにする](http://qiita.com/rrryutaro/items/990923b40c62a4caba4c)|