JavaScript
chrome-extension

画像やリンクをMarkdown形式でコピーできるChrome拡張をつくった

Chrome拡張をはじめて作ってみた

とりあえず今回は試しにmarkdown形式で画像やリンクを手軽にコピーできる拡張機能を作ってみました。

ストアはこちら
Markdown Copy - Chrome ウェブストア

githubはこちら。
katsunory/markdown_copy

Chrome拡張のつくりかた

Chrome拡張は手軽に作れるのだが、作り方がいろいろあるためにそれを全部紹介していると意外と煩雑だなと思いました。
今回作ってみて最低限下記から覚えるとよいのでは思いました。

  • 作り方は大きく2つあって、 browser_action(どのページでも作動できる拡張機能)と page_action(特定のページで作動することを目的とした拡張機能)でどちらか片方しか使えない
  • jsで機能を書く場所は大きく3つあって、 content_scriptsbackground(eventpage含む)popupがある
  • content_scriptsは ページ上のDOMを操作することができる。ただし直接使用できるAPIが限られている。
  • backgroundは ページ上のDOMは操作できない。ただ、APIの制限はなく、常に起動しているようなjsを書くこともできるし、なにかのときだけ動かしたいときはevent_pageという仕組みを使用することもできる。
  • popupは右上のメニューアイコンをクリックしたらでてくるhtmlで、そのポップアップhtml上でもjsを作動させることができる。ページ上のDOMは操作できない。
  • 基本設定はmanifest.jsonに書く。

もちろん、Override Pagesとかメッセージパッシングとか他にもいろいろあるのだが、基本はこんなところを抑えておけばよいと思った。

今回作ったもの

今回やりたかったことは下記。

  • ページのタイトルとURLをMarkdown形式でコピーする
  • 選択したリンクのURLをMarkdown形式でコピーする
  • 選択した画像をMarkdown形式でコピーする

Markdownは便利ですが、リンクとか画像とかを書くときちょっとめんどくさいと思い、内容的にちょうどいいので作ってみました。
今回やりたかったことを上記の最低限の使用方法で実現しようとするとざっくり下記のように考えられそうです。

・どのページでもMarkdownでコピーできるようにしたいのでbrowser_actionがよさそう
・ページのDOMをゴリゴリ操作する必要はないのでbackgroundにjsを書けるかも。常に起動するわけじゃなくコピー時のみ動けばいいのでevent_pageでいけそう
・popupしてなにか選択するわけではないのでpopupは必要ないかも

manifest.json

下記のような形で設定をjsonに記述していく。
使用するAPIをpermissionsに書いていく。今回はタブのタイトル・URL情報と右クリックメニュー時のクリックした場所の情報がほしいので、tabsとcontextMenusを使用する。

{
  "manifest_version": 2,
  "name": "Markdown Copy",
  "description": "ページリンクや画像をMarkdown形式でコピーする拡張機能です",
  "version": "0.2",
  "icons": {
    "16": "icon.png",
    "24": "icon.png",
    "32": "icon.png"
  },
  "browser_action": {
    "default_title": "Markdown Copy",
    "default_icon": "icon.png"
  },
  "background": {
    "scripts": ["background.js"],
    "persistent": false
  },
  "permissions": [
    "tabs",
    "contextMenus"
  ]
}

background.js

// 初回起動時にコンテキストメニューを作成するイベントハンドラの登録
chrome.runtime.onInstalled.addListener(createContextMenu);
chrome.runtime.onStartup.addListener(createContextMenu);

// コンテキストメニュークリック時に画像、リンクをMarkdown形式でコピーするイベントハンドラの登録
chrome.contextMenus.onClicked.addListener(copyByContextMenu);

// ブラウザアイコンクリック時にページリンクをMarkdown形式でコピーするイベントハンドラの登録
chrome.browserAction.onClicked.addListener(copyByBrowserAction);

/**
 * コンテキストメニューを作成する関数
 */
function createContextMenu() {
    chrome.contextMenus.create({
        id: 'mdCopy',
        title: 'Markdown形式でコピー',
        contexts: ['link', 'image'],
    });
}

/**
 * コンテキストメニューで、画像、リンクをMarkdown形式に変換してコピーする関数
 *
 * @param object info
 */
function copyByContextMenu(info) {
    var mediaType = info.mediaType;
    var srcUrl = info.srcUrl;
    var linkUrl = info.linkUrl;
    var selectionText = info.selectionText;
    var isSelectedText = typeof selectionText !== 'undefined';

    var ta = document.createElement('textarea');

    // コンテキストメニューを出した場所が画像の場合、画像を表示するMD形式の文字列を作成
    if (mediaType === 'image') {
        ta.value = '![altテキスト](' + srcUrl + ')';

    // コンテキストメニューを出した場所がリンクの場合、リンクを表示するMD形式の文字列を作成
    } else if (typeof linkUrl !== 'undefined') {
        if (isSelectedText) {
            ta.value = '[' + selectionText + '](' + linkUrl + ')';
        } else {
            ta.value = '[' + linkUrl + '](' + linkUrl + ')';
        }
    }

    document.body.appendChild(ta);
    ta.select();
    document.execCommand('copy');
    document.body.removeChild(ta);
}

/**
 * ブラウザアイコンで、画像、リンクをMarkdown形式に変換してコピーする関数
 *
 * @param object info
 */
function copyByBrowserAction(tab) {
    var ta = document.createElement('textarea');

    ta.value = '[' + tab.title + '](' + tab.url + ')';

    document.body.appendChild(ta);
    ta.select();
    document.execCommand('copy');
    document.body.removeChild(ta);
}

今回最低限やりたいことだけならbackground(event_page)だけでいけました。
画像を右クリックからMarkdownでコピーできるようになったので記事を書くのがちょっと楽になった。画像あんまり貼らないけど。

altテキスト

基本はわかったので次も作ってみたい。

chrome extension用のAPIはこちら。
Developer's Guide - Google Chrome