どんなものでも検索してしまいますプラグイン

# 概要
このプラグインは、yahooサーチとyoutubeサーチがベースでしたが、更に有名なサーチサイトも追加しました。プラグイン化することで、どんなwebページでも動作できます。
基本機能は、webページにある文字テキストをマウスカーソルで選択するだけで、yahooサーチもyoutubeサーチ・リスト生成・再生することができます。しかも、文字列選択するだけで、あとは自動実行します。
yyプラグインは、以下のサイトでインストールできます。
[yyプラグインのインストール(無料)](https://chrome.google.com/webstore/detail/yy%E3%83%97%E3%83%A9%E3%82%B0%E3%82%A4%E3%83%B3/edooldmlaabbmllldenkkceddlbbeing?hl=ja&authuser=0)

# 特長(活用場面)
yahooサーチとyoutubeサーチをひとつにまとめたプラグインです。webページで文字テキストをマウスカーソルで選択するだけで、いずれかのサーチを実行します。ポップアップが表示され、そこで、yahooサーチかyoutubeサーチかを選択すると実行します。

下図のサンプルで赤の矢印で示したテキスト文字を選択すると、ポップアップが表示されます。選択するときには、ctrlキーを押しておきます。これは、不用意にポップアップを表示させないため、あえてctrlキーを押したときのみ表示するようにしています。


![select800.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/198006/f004ee59-e0ed-e6c5-30bd-7f2d266c40dc.png)
次回公開予定のポップアップ(サーチサイトを追加しています)
![次回.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/198006/cbd80502-efe3-794f-92cd-c3697e7bcb7e.png)



# ボタン
ポップアップには3つのボタンがあります。
(1)yahooサーチボタン

このボタンを選択すると、セレクトしたテキスト文字でyahooサーチを実行します。この時、別タブでyahooサーチページを開きます。
(2)youtubeサーチボタン

このボタンを選択すると、別タブでyoutubeサーチを実行します。サーチクェリーはセレクトしたテキスト文字です。このサーチは動画のサーチをスクロールしながら約100件の動画を検索して再生リストを作成します。この再生リストは独自の形式のため、再生する場合は、専用の再生ページを開いて再生します。これらの動作は自動で行います。
(3)キャンセルボタン

ポップアップを非表示にします。

# 仕組み
yyプラグインは、複数のtabで連携動作します。この複数のtabで動作させることは単一のtabで動作させる場合と比べ、かなりその動作の仕組みを深く理解する必要があります。
content.jsとbackground.js、さらにタブとの関係をよく整理してプログラミングしないとうまく動作しません。
### (1)タブの種類
(1)Webページタブ(ブログ他)
サーチキーのもとになる、テキスト文字列を選択するための一般のWebページです。

(2)yahooサーチタブ(https://search.yahoo.co.jp/search?ei=UTF-8&p=......)
webページで選択したテキスト文字列をキーにして、yahooサーチページをオープンします。

(3)youtubeサーチタブ(https://www.youtube.com/results?search_query=....)
webページで選択したテキスト文字列をキーにして、youtubeサーチページをオープンします。

(4)プレーヤータブ(https://favorite.tecoyan.net/)
youtubeサーチページで検索した動画リストをもとに再生を行います。
### (2) タブの起動順序
3つのtabは、次の順序で動作します。
テキスト選択ページ(ブログ等)-->プレーヤーページ(favoriteページ)-->youtubeサーチページ
### (3)処理の流れ
ブログ等のページでテキスト選択(ctrlキー押し下げで)するとポップアップを表示します。そこで、例えばyoutubeサーチボタンを押すと、先に、プレーヤーページを開きます。次に、youtubeサーチページが開き、そこで、クェリー(テキスト選択した文字列)でサーチが実行します。このサーチは最大約100件の動画をサーチして再生リストを生成します。
youtube動画のサーチは、1秒毎にスクロールしてサーチ結果の動画をリスト配列へ追加します。リストを生成したら、background.jsへリストを送信します。これから、プレーヤージへ再生リストを渡すには、プレーヤーページのタブidを取得し、そのタブidをパラメータに指定してリストを送信します。このリストを受け取るのが、プレーヤページのcontent.jsです。このcontent.jsはタブ毎にブラウザーで保存されていますので、そのタブのcontent.jsが実行します。そこで、プレーヤーページのDOMへアクセスして、そこのリストを更新して再生が始まります。これらの一連の処理をプラグインが実行しますので、ユーザーは特に操作は必要ありません。
# 作成にあたって考慮した点について
### (1)content.jsの動き方
   このスクリプトの動き方には注意が必要です。このスクリプトはタブ毎にメモリ空間をもっています。そのため、タブAでセットした変数値をタブBでアクセスすることはできません。作り始めのころはこのことに気づかず、苦労しました。複数タブの管理ではこの点が一番問題になったことです。別空間で動作しています。しかもモジュールとしては一つのモジュールです。そのため、同じモジュールの中で、今のタブのURL(lovation.href)をチェックして、タブ毎に処理を振り分けています。全タブ共通の処理は振り分けずに処理できます。リスナーの定義にも注意が必要です。共通で定義するリスナーとタブ毎に定義するリスナーは分離しておくと理解しやすいです。

 
### (2)background.jsの動き方
   このモジュールは、全タブを管理できます。そのため、タブのidを取得して、そのタブのcontent.jsへメッセージを送信できます。メッセージのリスナーは全タブまとめて、一箇所で定義できます。メッセージ名をタブ毎に特定しておけば問題ありません。各タブで同じメッセージ名を使用するとおかしなことになります。

### (3)タブの動作
このプラグインは、一つのタブから、2つのtab(プレーヤータブとyoutubeタブ)を開いています。タブはwindow.open()で開いています。その場合、別タブ(target=_blank)として開いています。 ここでは、3つのタブを識別するため、便宜上、次の名称を使っています。
・クェリータブ(テキスト文字列を選択したページ)
・プレーヤータブ(リストを再生するページ)
  このプラグインでは、専用のページ(https://favorite.tecoyan.net)を使っています。 
・youtubeサーチタブ(動画の検索ページ)
上の3つのタブが連携して動作します。連携するための手段として以下のブラウザーのメッセージ通信を使用しています。
 chrome.runtime.sendMessage(....);
 chrome.runtime.onMessage.addListener(...); 
### (4)アクティブタブの動作
タブは切り換えのタイミングでアクティブタブイベントが入ります。このイベントはbackground.jsスクリプトで捕捉することができます。このイベントで今、どのタブにフォーカスが当たっているかがわかり、当該タブ専用のcontent.jsスクリプトで必要な処理を実行できます。
### (5)DOMアクセスについて
DOMへアクセスできるライブラリーはcontent.jsです。background.jsではアクセスできませんので、必要であれば、content.jsを経由してデータのやり取りができます。各タブのDOMデータをタブ間で共有するためにはメッセージ通信を使用します。メッセージ通信を使うことで、それぞれのcontent.jsの変数も、DOMデータもやり取りできます。
### (6)メッセージの種類
・"check_player"メッセージ
  クェリータブで送信するメッセージで、background.jsでは、タブクェリーで、プレーヤータブがあるかをチェックします。クェリータブは、background.jsからの応答でプレーヤータブが無ければ、window.open()でプレーヤータブをオープンします。 
・"get_value"メッセージ
  プレーヤータブとyoutubeタブで送信するメッセージで、background.jsからmax_value値を取得します。この値は、最大サーチ件数を示します。max_value値はアドレスバーの右端にあるプラグインアイコンをクリックして変更できます。 
・"query"メッセージ
  プレーヤータブで送信するメッセージで、background.jsからクェリー語を取得します。クェリー語はyoutubeタブで事前に送信しておき、background.jsで保持しておきます。

・"youtube_list"メッセージ
  youtubeサーチタブで送信するlistメッセージで、background.jsで保持しておきます。
# コード一式(ご参考)
全く、整理されていないコードです。動作することに注力していましたので、オブジェクト化やクラス化はしていません。まず、複数タブの連携動作に集中していましたので、整理は今後、徐々に進めて行くつもりです。

### background.js
``` background.js
//***************************************************************
//yyプラグイン 
//"manifest_version": 3,
//"name": "yyプラグイン　1.0",
//background_yy.js
//2022-12-03
//********************************************************************
console.log("スタート　(1)yyプラグイン 1.0 background_yy.js loaded ");
let parent_url;
let query_txt;
let favorite_player_id;
let active_tab_id;
let tabs_id_favorite;let tabs_id_youtube;let tabs_id_other;
let query;              //サーチキー
let max_value = 100;          //max検索件数    
let content;            //リストデータ
let tab_id_key;         //
let default_player_id;  //
let tabs_table = [];      //tab_id,tab_url,tab_title
let active_tab_title;   //
let tab_title;
let tab_url;
let tab_id;
let tab_titles;
let tab_title_key;
let youtube_tabid = null; //  
let ttabs = [];          //
let param = [];           //
let host;
let ipAddress;


const aryMin = function (a, b) {
    return Math.min(a, b);
};
const aryMax = function (a, b) {
    return Math.max(a, b);
};
async function getCurrentTab() {
	let queryOptions = { active: true, currentWindow: true };
	let [tab] = await chrome.tabs.query(queryOptions);
	return tab;
}
getCurrentTab().then((tab) => {
        console.log("関数　getCuurentTab 変数　tab.id, tab.title "+tab.id+" タイトル　"+tab.title);
});



//
//
//
console.log("(2)check_favorite_tab_exist()定義");
function check_favorite_tab_exist(){
    tabs_id_favorite = [];tabs_id_youtube = [];tabs_id_other = [];

    chrome.tabs.query({}, tabs => {
            ttabs = tabs;
            //"favorite.tecoyan.net"
            //parent_urlのタブid(active_tab_id)をチェック
            for(let i=0; i<tabs.length; i++){
                    //プレーヤータブ　favorite.tecoyan.net                    
                    if(tabs[i].url.indexOf("favorite.tecoyan.net")!==-1){
                             tabs_id_favorite.push(tabs[i].id);
                             console.log("関数 check_favorite_tab_exist() 変数 tabs_id_favorite "+tabs[i].id);
                    //youtubeタブ         
                    }else if(tabs[i].url.indexOf("www.youtube.com")!==-1){
                             tabs_id_youtube.push(tabs[i].id);
                             console.log("関数 check_favorite_tab_exist() 変数 tabs_id_youtube "+tabs[i].id);
                    //複数あると異なるページを取っている?
                    //その他のタブ
                    }else if(tabs[i].url.indexOf(parent_url)!==-1){
                            console.log("関数 check_favorite_tab_exist() 変数 tabs[i].url https "+tabs[i].url);
                            tabs_id_other.push(tabs[i].id);
                            active_tab_id = tabs[i].id;
                            //
                    }
                    
            }
            //
            console.log("関数 check_favorite_tab_exist() 変数 parent_url active_tab_id "+active_tab_id);
            
            if(tabs_id_favorite.length !== 0){
                //プレーヤタブがある
                favorite_player_id = tabs_id_favorite.reduce(aryMin); //
                //active_tab_idがundefinedになっているケースとは
                //
                console.log("関数 check_favorite_tab_exist() 変数 chrome.tabs.sendMessage active_tab_id = "+active_tab_id+" id:favorite_player_id,data:wb_check_player,content:'ダミー'");
                console.log("関数 check_favorite_tab_exist() 変数 ★★★favorite_player_id　" + favorite_player_id);
                
                //プレーヤーのtab_idを返却
                if(typeof active_tab_id === 'undefined'){
                    //ここは、バグ?
                    //alert("タブを更新してみてください。");
                    return;
                }
                //指定したタブへwb_check_player応答を返す　プレーヤーあり
                try{
                    chrome.tabs.sendMessage(active_tab_id,{type:'wb_check_player', id:favorite_player_id,data:"wb_check_player",content:'ダミー'}); 
                }catch(e){
                    console.log("メッセージ送信エラー wb_check_player active_tab_id "+active_tab_id);
                }
            }else{
                //プレーヤータブがない
                if(typeof active_tab_id === 'undefined'){
                    //ここへは?
                    //alert("タブを更新してみてください。");
                    return;
                }
                active_tab_id = tabs_id_other.reduce(aryMin); //
                
                //ここで、active_tab_idがundefinedのとき、タブがactiveになっていないか                
                console.log("関数 check_favorite_tab_exist() 変数 active_tab_id = "+active_tab_id+" id:favorite_player_id,data:false,content:'ダミー'");
                //falseを返却
                //指定したタブへwb_check_player応答を返す　プレーヤーなし
                try{
                    chrome.tabs.sendMessage(active_tab_id,{type:'wb_check_player', id:'none',data:'false',content:'ダミー'}); 
                }catch(e){
                    alert("chrome.tabs.sendMessageエラー");
                }
                
            }

            return;

    });

    
}
console.log("(3)ex_listener()定義");
//
function ex_listener(message, sender, sendResponse){
//プレーヤーページからの要求    
    switch(message){
        case "version":
            //
            //バージョンをチェック
            console.log("関数 ex_listener() 変数 バージョンをチェック　yahoo");    
            const manifest = chrome.runtime.getManifest();
            sendResponse({
                type: 'success',
                version: manifest.version,
                name:manifest.name
            });
            return true;
            break;
        case "disp_value":
            //変数を要求元へ返す
            //
            chrome.tabs.query({}, tabs => {
                ttabs = tabs;

            });
            let tabs_data=[];
            let i;
            for(i=0;i< ttabs.length;i++){
                tabs_data.push([ttabs[i].id,ttabs[i].title]);
            }
            sendResponse({
                type: 'success',
                data: tabs_data
            });
            return true;            
            
            
            break;            
        
    }

}
//External(Webアプリ)からバージョンリクエスト受信待ち
//
chrome.runtime.onMessageExternal.addListener(ex_listener);

//***************************************************************
//タブのアクティベートをリッスン
//タブが切り替わったたびにタブタイトルとタブidを保存
//■*************************************************************
chrome.tabs.onActivated.addListener(function(activeInfo) {
      console.log("chrome API (4)タブアクティベートをリッスン(onActivated)定義");
      active_tab_id=activeInfo.tabId;
      //console.log("tabs.onActivated.=  "+active_tab_id);

}); 	//
//****************************************************************************
//tecoyan_content.js
//ブログyahoo_extensionからのwb_check_playerメッセージを受信する
//youtubeサーチボタン
//
//■**************************************************************************

chrome.runtime.onMessage.addListener(function (msg, sender){
console.log("chrome API (5)runtime.onMessage.addListener()定義");
//全タブの中にfavorite.tecoyan.netタブがあるかチェック
switch (msg.data){
    case "wb_check_player":
        parent_url = msg.site;
        console.log("chrome API onMessage.addListener()親ページのurl = "+msg.site);
        
        console.log("chrome API onMessage.addListener()wb_check_playerメッセージを受信");
        check_favorite_tab_exist();
            
    break;
    case "save_id":
        console.log("chrome API onMessage.addListener()save_idメッセージを受信 msg.site "+msg.site);
        parent_url = msg.site;
        
    break;    
    
    
}

});

//console.log("(2)get_default_player_tabid()定義");
function get_default_player_tabid() {
    //console.log("(2-1)get_default_player_tabid()コール");
    tabs_id_favorite = [];
    tabs_id_youtube = [];
    chrome.tabs.query({}, tabs => {
        ttabs = tabs;
        //console.log("(2-2)chrome.tabs.query()コール");
        //"「なつかしの曲」(ポータル他)"
        for (let i = 0; i < tabs.length; i++) {
            //なつかしの曲サイト                    
            if (tabs[i].url.indexOf("favorite.tecoyan.net") !== -1) {
                tabs_id_favorite.push(tabs[i].id);
                ////console.log("tabs_id_favorite "+tabs[i].id);
            }
            //youtubeタブ                    
            if (tabs[i].url.indexOf("www.youtube.com") !== -1) {
                tabs_id_youtube.push(tabs[i].id);
                ////console.log("tabs_id_youtube "+tabs[i].id);
            }

        }
        //console.log("(2-3)chrome.tabs.remove()コール");
        default_player_id = tabs_id_favorite.reduce(aryMin); // 
        ////console.log("★★★default_player_id　" + default_player_id);
        for (let i = 0; i < tabs_id_youtube.length; i++) {
            chrome.tabs.remove(tabs_id_youtube[i]);
        }
         //console.log("(2-4)chrome.tabs.update()コール");
        //タブを切り替え
        chrome.tabs.update(default_player_id, {selected: true}, callback);
        //console.log("★★★タブidキー　" + tab_id_key);
        setTimeout(function () {
            //プレーヤーページ(タブ)  
            console.log("関数 get_default_player_tabid()(2-5)★★★親タブへリストを送信しました default_player_id　query "+default_player_id+" "+query);
            try{
                chrome.tabs.sendMessage(default_player_id, {type: 'yt_youtube_list', id: default_player_id, data: 'yt_youtube_list', content: {body: content, query: query}});
            }catch(e){
                alert("chrome.tabs.sendMessageエラー");
            }
                ////console.log("★★★親タブへリストを送信しました。◆◆◆default_player_id　" + default_player_id);
        }, 1000);
    });


}

//***************************************************************
//タブのアクティベートをリッスン
//タブが切り替わったたびにタブタイトルとタブidを保存
//■*************************************************************
//console.log("(4)tabs.onActivated.addListener()定義");
chrome.tabs.onActivated.addListener(function (activeInfo) {
    //console.log("(4-1)tabs.onActivated.addListener()イベント "+activeInfo.tabId);
    setTimeout(y_timer, 10);
    function y_timer() {
        //ここで、カレントの全タブを取得して、DBへ保存
        //事前に、ipAddressを見て、一旦、クリアしておく。
        chrome.tabs.query({}, tabs => {
            ttabs = tabs;
            //console.log("(6)chrome.tabs.query()開始*****************************");
            //**********************************************
            //DBへタブタイトルとタブidを保存
            //タブの切り替え毎に
            //**********************************************
            for (let i = 0; i < tabs.length; i++) {
                //console.log("(6-1)tabs タイトル id "+tabs[i].title+" "+tabs[i].id+"  "+i);
                
                if (tabs[i].url.indexOf("www.youtube.com") !== -1) {
                    youtube_tabid = tabs[i].id;
                }

                if (tabs[i].id === activeInfo.tabId) {
                    active_tab_title = tabs[i].title;

                    //タブタイトルに:が含まれているケース
                    if (active_tab_title.indexOf(":") !== -1) {
                        tab_titles = active_tab_title.split(':');
                        tab_title = tab_titles[0];
                    } else {
                        tab_title = active_tab_title;
                    }
                }
                ////console.log("■■■今のタブのタイトルは、 "+tab_title);
                tab_title = tabs[i].title;
                tab_url = tabs[i].url;
                tab_id = tabs[i].id;
                tab_title_key = tab_title;
                //
                //tab_idをDBに保存する
                ////console.log("■■■DBに保存　tab_id,tab_title,tab_url "+tab_id+" "+tab_title+" "+tab_url);
                //console.log("(6-1)■■■tab_idをDBに保存する");
                //********************************************************************
                fetch('https://blog-tool.tecoyan.net/db/active_tab_id_db.php?tab_id=' + tab_id + '&tab_title=' + tab_title + '&tab_url=' + tab_url, {
                    method: 'GET',
                    headers: {
                        'Content-Type': 'text/html; charset=utf-8',
                    }

                }).then((response) => {
                    return response.text();
                }).then((data) => {
                    ////console.log("■■■応答active_tab_id "+data+" "+tab_title);
                }).catch((error) => {
                    console.error('Error:', error);
                });

            }
            //console.log("(7)chrome.tabs.query()終了*****************************");
        });
    }

}); 	//
//****************************************************************************
//tecoyan_content.jsからメッセージを受信する　tabは複数ありえる
//■(1)youtubeからの'yt_youtube_list'メッセージ待ち リストとquery
//■(2)youtubeからの'send_list'メッセージ待ち
//■(3)popup.jsからmax_valueを受信
//■(4)youtubeからの'get_value'メッセージ待ち
//■(5)プレーヤータブからのget_queryメッセージ待ち
//■**************************************************************************
//console.log("(5)runtime.onMessage.addListener()定義");
chrome.runtime.onMessage.addListener(function (msg, sender, sendResponse) {
    //console.log("(8)メッセージを受信　"+msg.data);
    //メッセージをチェック
    switch(msg.data){
        case "max_value":
                    //popup.jsより
                    //console.log("(8-1)★★★addListener popup.jsからmax_valueを受信 " + msg.data);
                    max_value = Number(msg.content);
                    //return;
                    break;
       case "get_value":
                    //youtubeページからget_valueメッセージを受信
                    //プレーヤーページからget_valueメッセージを受信
                    //console.log("(8-2)★★★addListener get_value " + msg.data);
                    //今のtabのyoutube_tabidを取得
                    chrome.tabs.query({}, tabs => {
                        ttabs = tabs;
                        //"「なつかしの曲」(ポータル他)"
                        for (let i = 0; i < tabs.length; i++) {
                            //youtubeサイト                    
                            if (tabs[i].url.indexOf("www.youtube.com") !== -1) {
                                youtube_tabid = tabs[i].id;
                                console.log("(8-2-1)youtube_tabid " + youtube_tabid);
                                //max_valueをyoutubeページへ送信
                                //console.log("(8-2-2)max_vlaue  youtube_tabid " + max_value + "  " + youtube_tabid);
                                //console.log("(8-2-3)youtubeページへmax_valueを送信");
                                console.log("関数 onMessage.addListener(8-2-3)content youtube_tabid max_value default_player_id "+youtube_tabid+"  "+ max_value+"  "+default_player_id);
                                //queryが取れていないケースあり 数秒してから取得している
                                try{
                                    chrome.tabs.sendMessage(youtube_tabid, {type: 'get_value', data: 'get_value', content:{ max_value:max_value,player_tab_id: default_player_id}});
                                }catch(e){
                                    alert("chrome.tabs.sendMessageエラー");
                                }                                
                            }
                            if (tabs[i].url.indexOf("favorite.tecoyan.net") !== -1) {
                                default_player_id = tabs[i].id;
                                console.log("関数 onMessage.addListener(8-2-6)default_player_idページへmax_valueを送信 "+default_player_id+" "+ max_value);
                                try{
                                    chrome.tabs.sendMessage(default_player_id, {type: 'get_value', data: 'get_value', content:{max_value: max_value, player_tab_id: default_player_id}});
                                }catch(e){
                                    alert("chrome.tabs.sendMessageエラー");
                                }  
                            }                
                        }

                    });
                    return;
                    break;
        case "get_query":
                    //content.jsから、get_queryメッセージを受信したら、プレーヤーページへリストを送る    
                    //console.log("(8-3-1)★★★addListener get_query " + msg.data);
                    //このリストを送信するタイミングは、デフォルトプレーヤーページがロードされて
                    //リストの受信待ちに入ったのち
                    //プレーヤーへリストを送る
                    //console.log("(8-3-2)★★★addListener get query default_player_id " + default_player_id);
                    setTimeout(function () {
                        //
                                if(query===""){
                                    console.log("queryは空白です。 ");
                                }
                                try{
                                    chrome.tabs.sendMessage(default_player_id, {type: 'get_query', data: 'get_query', content: query});
                                }catch(e){
                                    alert("chrome.tabs.sendMessageエラー");
                                }                         
                        //console.log("(8-3-3)★★★デフォルトプレーヤーへ送信しました。default_player_id " + default_player_id);
                    }, 1000);
                    return;
                    break;
        case "send_list":
                    //content.jsから、send_listメッセージを受信したら、プレーヤーページへリストを送る    
                    //console.log("(8-4-1)★★★addListener send_list " + msg.data);
                    //このリストを送信するタイミングは、デフォルトプレーヤーページがロードされて
                    //リストの受信待ちに入ったのち
                    //プレーヤーへリストを送る
                    //console.log("(8-4-2)★★★addListener default_player_id " + default_player_id);
                    setTimeout(function () {
                        //
                                try{
                                    chrome.tabs.sendMessage(default_player_id, {type: 'send_list', data: 'send_list', content: msg.content});
                                }catch(e){
                                    alert("chrome.tabs.sendMessageエラー");
                                }                          
                        //console.log("(8-4-3)★★★デフォルトプレーヤーへ送信しました。default_player_id " + default_player_id);
                    }, 1000);
                    return;
                    break;
//        case "wb_check_player":
//                    //
//                    //favorite.tecoyan.netタブのチェック
//                    //console.log("(8-5-1)★★addListener wb_check_player " + msg.data);
//                    //タブのチェック　あり:タブidを返す　なし:　false
//                    chrome.tabs.sendMessage(default_player_id, {type: 'wb_check_player', data: 'false'});
//                    return;
//                    break;
        case "yt_youtube_list":
                    //console.log("(8-6-1)★★★addListener youtube_list " + msg.data);
                    //if (msg.data === 'yt_youtube_list' && msg.url === "www.youtube.com"){
                    //グローバルへ保持
                    content = msg.content['body'];
                    query = msg.content['query'];
                    
                    console.log("関数 onMessage.addListener(8-6-2)★★★addListener youtube_list query "+query); 
                    //***************************************************************
                    //active_tab_idをDBから取得
                    //***************************************************************
                    fetch('https://blog-tool.tecoyan.net/get_ipaddress_data.php', {
                        method: 'GET',
                        headers: {
                            'Content-Type': 'text/html; charset=utf-8',
                        }
                    }).then((response) => {
                        return response.text();
                    }).then((data) => {
                        ipAddress = data;
                        var re = /(\w+):\/\/([\w\.-]+)\/(\S*)/;
                        //
                        var matches = msg.site.match(re);
                        if (matches != null) {
                            //サーチリクエストを送信したサイト
                            //どのサイトからかがわかる　tecoyan以外からもあり
                            //プレーヤーをもたないサイトも
                            host = matches[2]; //
                            //console.log("(8-6-2)★★★addListener youtube_list host " + host);
                        }
                    }).then(() => {
                        var param = {
                            'site_name': host
                        };
                        fetch('https://blog-tool.tecoyan.net/db/get_tab_title_db.php?site_name=' + host, {
                            method: 'GET',
                            headers: {
                                'Content-Type': 'text/html; charset=utf-8'
                            },
                            //body: param,
                        }).then((response) => {
                            return response.text();
                        }).then((tab_title) => {

                            tab_title = tab_title.replace(/[\n]/g, "");
                            //console.log("(8-6-3)★★★addListener youtube_list tab_title.replace　" + tab_title);

                            //タブタイトルのタブidを取得
                            var param = {
                                ipAddress: ipAddress,
                                tab_title: tab_title
                            };
                            fetch('https://blog-tool.tecoyan.net/db/active_tab_id_get_db.php?ipAddress=' + ipAddress + '&tab_title=' + tab_title, {
                                method: 'GET',
                                headers: {
                                    'Content-Type': 'text/html; charset=utf-8'
                                }
                                //body: param
                            }).then((response) => {
                                return response.text();
                            }).then((data) => {
                                //alert("■サーチ元ページへ戻り、リストをサーチ結果と入れ替えます。親タブid= "+data);
                                //************************************************************
                                data = Number(data.replace(/[^0-9]/g, ''));
                                tab_id_key = data;
                                //************************************************************
                                //■呼び出し元()へリストを送信
                                //■ここで、dataがないエラーError in event handler: ReferenceError: data is not defined
                                //************************************************************
                                //console.log("(8-6-4)★★★addListener youtube_list 変数youtube_tabid " + youtube_tabid);
                                //console.log("(8-6-5)★★★addListener youtube_list 変数tab_id_key " + tab_id_key);
                                //console.log("(8-6-6)★★★addListener youtube_list get_default_player_tabid()コール");
                                get_default_player_tabid();

                            }).catch((error) => {
                                console.error('★★★addListener youtube_list Error:', error);
                            });
                        });
                    }).catch((error) => {
                        console.error('★★★addListener youtube_list Error:', error);
                    });
                    break;
    }
});
//
function callback() {

}
```
### content.js

``` content.js
//******************************************************************************
//tecoyan.content_yy.js
//yahoo_youtubeプラグイン 1.0",
//(1)処理概要:
//プラグインをロード時にポップアップpopup_dialog要素を動的追加
//ボタン　yahooサーチ、youtubeサーチ
//(2)コードのチェック:
//このコードを見た時、わかりやすいかどうかをチェックしてみる。
//処理の流れ(順序)がわかるか。特に、非同期処理があると、処理の流れが入れ替わって
//わかりにくくなる。
//console.log()で、その処理順序をチェックしてみる。
//******************************************************************************
//全タブ共通処理
console.log("■全タブ共通で　chrome(1)tecoyan-content_yy_new.js先頭開始");
let option_text;
let query;
let max_value = 10;
let player_tab_id;
var dom_parser = new DOMParser();
let document_obj = new Object;
var aaa = [];
var ccc = [];
var ddd = [];
let sv_length;
let ccnt;
//
let site_name = "";
let youtube_timer = null;
let varscroll = null;
let cc = 1;
let list = [];
let kensu = 0;
let title = "";
let title1 = null;
let thumbnail = "";
let llduration = null;
let duration1 = null;
let response = null;
let query_txt;
//******************************************************************************
//popupダイヤログ表示
//******************************************************************************
if(typeof popup_dialog==='undefined'){
    //
    console.log("■全タブ共通で　ページアクティブ(2)popup_dialog要素を追加");
    var popup_dialog = `<div id="popup_dialog" title="確認メッセージ" style="text-align:center;display:none;">
    <p id="popup_mess"></p><div style="text-align:center;">
    <label><input type="checkbox" class="tecoyan_ck" value="1">　yahooサーチ</label>
    <label><input type="checkbox" class="tecoyan_ck" value="2" checked=true>　youtubeサーチ</label>
    <label><input type="checkbox" class="tecoyan_ck" value="3">　wiki</label>
    <label><input type="checkbox" class="tecoyan_ck" value="4">　Qiitaサーチ</label></div>
    <label><input type="checkbox" class="tecoyan_ck" value="5">　stackoverflowサーチ</label></div>
    
</div>
`;
        console.log("■全タブ共通で　ページアクティブ(2)popup_dialog要素をbodyにappend");
       $("body").append(popup_dialog);
        let style = document.createElement('style');
        style.innerHTML = '*{ user-select: auto !important; }';
        document.body.appendChild(style);
        //チェック　リセット
        $.each($(".tecoyan_ck"),function(i,val){
            val.checked = false;
        });
        
}
//pop_statusする
    console.log("■全タブ共通で　ページアクティブ(2)popup_status要素をbodyに追加");
    var pop_status = `<div id="pop_status" style="display:none;background-color:light-yellow;width:400px;height:auto;">テスト　テスト　テスト</dic>`;
    $("body").append(pop_status);
//******************************************************************************
//ボタン選択イベント　チェックボックス
//******************************************************************************
$(".tecoyan_ck").on('click',function(e){
        console.log("■全タブ共通で　ページアクティブ(2).tecoyan_ckクラスのチェックをリセット");
        //チェック　リセット
        //ここのチェックボックスのみに限定するには、特別の識別コードが必要
        //
        $.each($(".tecoyan_ck"),function(i,val){
            if((i+1)===Number(e.currentTarget.value)){
                val.checked = true;
            }else{
                val.checked= false;
            }
            
        });
        
        
   //alert(e.currentTarget.value); 
});
var w = null;
//******************************************************************************
//クェリーでdialogを表示　yahooサーチ or youtubeページをオープン
//******************************************************************************
console.log("■全タブで　(3)yahoo()定義");
function get_text(tt){
        console.log("■全タブ共通で　アクティブページ関数 get_text(tt) 変数(4-1)tt "+tt);
        $("#popup_mess").html("「"+tt+"」で検索します。<br>");
        //**************************************************************
        //background.jsへsave_idメッセージを送信
        //document.URLの保存要求
        //**************************************************************
        chrome.runtime.sendMessage({data: 'save_id', site: document.URL});
        console.log("■全タブ共通で　アクティブページ関数 get_text(tt) 変数(4-2)sendMessage save_id "+document.URL);
        option_text="";
        if(location.href.indexOf("favorite.tecoyan.net")!==-1){
            $("#option_text")[0].value="";
        }
        //*************************************************************
        //ダイヤログ表示
        //*************************************************************
        console.log("■全タブ共通で　アクティブページダイヤログ表示");
        $('#popup_dialog').dialog({
                autoOpen: true,
                title: 'yyプラグイン　v1.0',
                closeOnEscape: true,
                position: {my: "left top", at: "center top", of: "#popup_dialog"},
                width:'500px',
                modal: false,
                buttons: [        // ボタン名 : 処理 を設定
                    //
                    {                                
                    text: '実行',
                    click: function(e){
                        //チェックボックスを判定
                        //yahoo,youtube,wiki他
                        $.each($(".tecoyan_ck"),function(i,val){
                            if(val.checked===true){
                                //
                                switch(i+1){
                                    case 1:
                                        yahoo(tt);
                                        break;
                                    case 2:

                                        speechSynthesis.speak(
                                           new SpeechSynthesisUtterance("youtubeサーチを実行します。")
                                         );
                                       //youtube
                                       console.log("■全タブ共通で　youtubeサーチボタン　メッセージ送信wb_check_player ");
                                       //**********************************************
                                       //プレーヤーページの有無を確認
                                       //background.jsへwb_check_playerメッセージを送信
                                       //**********************************************
                                       chrome.runtime.sendMessage({data: 'wb_check_player', site: document.URL, content: "ダミー"});                                
                                       // ダイアログを閉じる
                                       //$(this).dialog('close');                                                
                                        break;
                                    case 3:
                                        //wiki
                                        wiki(tt);
                                        break;
                                   case 4:
                                        //Qiitq
                                        qiita(tt);
                                        break;
                                    case 5:
                                        //statckoverflow
                                        stackoverflow(tt);
                                        break;


                                }
                                return false;
                            }

                        });

                        //yahoo(tt);
                        // ダイアログを閉じる
                        ////$(this).dialog('close');
                        return false;
                    }
                    },  

                  {
                    text: 'キャンセル',
                    click: function(){
                              //チェック　リセット
                              $.each($(".tecoyan_ck"),function(i,val){
                                  val.checked = false;
                              });                          

                            // ダイアログを閉じる
                            $(this).dialog('close');
                            return false;	
                    }
                  }
                ]
        });

        //**************************************************************
        //ボタン処理より前に実行
        //**************************************************************
        $("div[role=dialog]").css({"display":"block","background-color":"lightblue","z-index":"1000","position": "fixed", "top": "100px"});                                    
        $(".ui-dialog").draggable();
        $("#ui-id-1").html("<br><img src=https://favorite.tecoyan.net/images/yy19.png></img>　　　yyプラグイン　v1.0");
        $("#ui-id-1").css("text-align","center");
                
}
//******************************************************************************
//stackoverflow
//******************************************************************************
function stackoverflow(k_w) {
    w = null;
    var char_ = "";
    if (document.all) {
        char_ = document.charset;
    } else {
        char_ = document.characterSet;
    }
    if (char_ === "utf-8" || char_ === "UTF-8") {
    } else {
    }
    k_w = k_w.replace(/&nbsp;/g, ' ');
    if (typeof (k_w) !== "undefined") {
        var gg = 'https://stackoverflow.com/questions/tagged/'+k_w;
        if(w === undefined||w===null){
	        w = window.open(gg, '_blank');
                w.focus();        
        }else{
        	w.close();
        	w = window.open(gg, '_blank');
        	w.focus();
        }
        window.getSelection().empty();
    }
}
//******************************************************************************
//Qiita
//******************************************************************************
function qiita(k_w) {
    w = null;
    var char_ = "";
    if (document.all) {
        char_ = document.charset;
    } else {
        char_ = document.characterSet;
    }
    if (char_ === "utf-8" || char_ === "UTF-8") {
    } else {
    }
    k_w = k_w.replace(/&nbsp;/g, ' ');
    if (typeof (k_w) !== "undefined") {
        var gg = 'https://qiita.com/search?q='+k_w;
        if(w === undefined||w===null){
	        w = window.open(gg, '_blank');
                w.focus();        
        }else{
        	w.close();
        	w = window.open(gg, '_blank');
        	w.focus();
        }
        window.getSelection().empty();
    }
}
//******************************************************************************
//wiki
//******************************************************************************
function wiki(k_w) {
    w = null;
    var char_ = "";
    if (document.all) {
        char_ = document.charset;
    } else {
        char_ = document.characterSet;
    }
    if (char_ === "utf-8" || char_ === "UTF-8") {
    } else {
    }
    k_w = k_w.replace(/&nbsp;/g, ' ');
    if (typeof (k_w) !== "undefined") {
        var gg = 'https://ja.wikipedia.org/wiki/'+k_w;
        if(w === undefined||w===null){
	        w = window.open(gg, '_blank');
                w.focus();        
        }else{
        	w.close();
        	w = window.open(gg, '_blank');
        	w.focus();
        }
        window.getSelection().empty();
    }
}
//******************************************************************************
//yahoo
//******************************************************************************
function yahoo(k_w) {
    w = null;
    var char_ = "";
    if (document.all) {
        char_ = document.charset;
    } else {
        char_ = document.characterSet;
    }
    if (char_ === "utf-8" || char_ === "UTF-8") {
    } else {
    }
    k_w = k_w.replace(/&nbsp;/g, ' ');
    if (typeof (k_w) !== "undefined") {
        var gg = 'http://search.yahoo.co.jp/search?ei=' + char_ + '&p=' + k_w;
        if(w === undefined||w===null){
	        w = window.open(gg, '_blank');
                w.focus();        
        }else{
        	w.close();
        	w = window.open(gg, '_blank');
        	w.focus();
        }
        window.getSelection().empty();
    }
}
//******************************************************************************
//全タブ共通　
//リスナーでCtrl+clickキーイベントを待つ
//テキスト選択後のイベント(マウスアップ)
//ポップアップを表示
//yahooサーチボタン、youtubeサーチボタン
//
//******************************************************************************
console.log("■全タブ共通で　アクティブページイベントリスナー　(4)mouseupイベントを登録");
document.addEventListener(`mouseup`, (e) => {
        console.log("■全タブ共通で　イベントリスナー　(4)mouseupイベントをリッスンする");   
        //
        if (event.ctrlKey) {
                //cytl+click
                console.log("■全タブ共通で　イベントリスナー　ctrlキー　ON");
               //セレクト&サーチ
                var text = document.getSelection();
                if(text.type==="None"){
                       return false;
                }
                //フォーカスした文字を取得
                if(typeof text.focusNode.data==='undefined'){
                       return false;
                }

                var tt = text.focusNode.data.substring(text.anchorOffset,text.focusOffset);
                if(tt===""){
                        return false;
                }
                //
                //dialogを表示
                get_text(tt);
              
  			
	}else{
            //ctrl+click以外
            if(e.currentTarget.activeElement.id !== "cancel_option"){
                //リセットボタン以外
                if(location.href.indexOf("favorite.tecoyan.net")!==-1){
                    //プレーヤーページの入力フィールドの値
                    option_text = $("#option_text")[0].value;
                    //空白以外の時、youtubeサーチする
                    if(option_text!==""){
                        $("#popup_mess").html("「"+option_text+"」で検索します。<br>");
                        //この値で検索
                           speechSynthesis.speak(
                               new SpeechSynthesisUtterance("youtubeサーチを実行します。")
                             );
                           //youtube
                           console.log("■全タブ共通で　入力フィールドでyoutubeサーチ メッセージ送信wb_check_player ");
                           //**********************************************
                           //プレーヤーページの有無を確認
                           //background.jsへwb_check_playerメッセージを送信
                           //**********************************************
                           chrome.runtime.sendMessage({data: 'wb_check_player', site: document.URL, content: "ダミー"});                                
                           // ダイアログを閉じる
                           ////$(this).dialog('close');
                           return false;


                    }
                }
            }
            
        }
        return; 
});
//******************************************************************************
//全タブ共通
//background.jsからwb_check_playerメッセージの結果を待つ
//このリスナーは一箇所で
//重要注意　このリスナーは、タブがアクティブになるたびに有効になるのか
//そのため、何回も受信するか
//******************************************************************************
console.log("■全タブ共通で　(5)background.jsからのメッセージを待つ");
//このリスナーは、youtube,favoriteタブ以外で
if(location.href.indexOf("www.youtube.com")===-1&&location.href.indexOf("favorite.tecoyan.net")===-1){
        chrome.runtime.onMessage.addListener(function (msg, sender, sendResponse) {

                console.log("■全タブ共通で　イベントリスナー　(5)background.jsからメッセージ受信wb_check_player ");
                switch(msg.type){
                    case "wb_check_player":
                            //
                            if (msg.type === 'wb_check_player' && msg.data==='false'){
                                    //オープン youtubeページ
                                    window.open("https://favorite.tecoyan.net");
                            }
                            //少し遅らせて、youtubeサーチをオープン
                            setTimeout(function(){
                                query_txt = $("#popup_mess")[0].innerText.replace(/「(.*)」で検索します。/,"$1");
                                //ここで、<b>タグがあれば、削除
                                query_txt = query_txt.replace(/<br>/,"");
                                query_txt = query_txt.replace(/<\/br>/,"");
                                if(query_txt===""){
                                    alert("query_txtが空白です。");
                                    return;
                                }
                                window.open("https://www.youtube.com/results?search_query=" + query_txt);
                                $("#system_mess").css("display","none");
                                $(".ui-dialog").css("display","none");
                                // ダイアログを閉じる
                                //$("#popup_dialog").dialog('close');
                            },1000);
                        break;
                    default :                
                        break;

                }
                return false;
        });
}
//******************************************************************************
//今いるページはfavoriteページか
//******************************************************************************
if (/favorite.tecoyan.net/.test(document.URL)) {
    console.log("■全タブ共通で　favoriteページエンター　favorite.tecoyan.net");
    chrome.runtime.onMessage.addListener(function (msg, sender, sendResponse) {

                console.log("■全タブ共通で　イベントリスナー　(5)background.jsからメッセージ受信wb_check_player ");
                switch(msg.type){
                    case "wb_check_player":
                            //
//                            if (msg.type === 'wb_check_player' && msg.data==='false'){
//                                    //オープン youtubeページ
//                                    window.open("https://favorite.tecoyan.net");
//                            }
                            //少し遅らせて、youtubeサーチをオープン
                            setTimeout(function(){
                                query_txt = $("#popup_mess")[0].innerText.replace(/「(.*)」で検索します。/,"$1");
                                //ここで、<b>タグがあれば、削除
                                query_txt = query_txt.replace(/<br>/,"");
                                query_txt = query_txt.replace(/<\/br>/,"");
                                if(query_txt===""){
                                    alert("query_txtが空白です。");
                                    return;
                                }
                                window.open("https://www.youtube.com/results?search_query=" + query_txt);
                                $("#system_mess").css("display","none");
                                $(".ui-dialog").css("display","none");
                                // ダイアログを閉じる
                                //$("#popup_dialog").dialog('close');
                            },1000);
                        break;
                    default :                
                        break;

                }
                return false;
        });  
}
//******************************************************************************
//youtubeタブ専用
//2022-12-03
//content_readyメソッド宣言
//******************************************************************************
function content_ready() {
    debugger;
    clearTimeout(youtube_timer);
    //その時のリストデータを取得　徐々に増大
    document_obj = dom_parser.parseFromString(document.all[0].outerHTML, "text/html");
    //****************************************************************************************
    //検索結果より、ytd-video-renderer,ytd-playlist-renderer,ytd-radio-renderer要素を処理
    //
    //****************************************************************************************
    //その時点のデータを取得　1秒毎
    aaa = document_obj.getElementsByTagName("ytd-video-renderer");
    if(aaa.length===0){
        alert("クェリーが空白の可能性あり、サーチ結果がゼロ");
        return;
    }
    //ここで、この中でタイトルに除外キーがある場合はリストから除く
    //ここで、aaa.length<100以下の場合は、再度、タイマーを伸ばして、検索を続行。
    //alert("aaa.lengthチェック　"+aaa.length+" "+max_value);
    //debugger;
    //alert("max_valueチェック　"+max_value);
    if (aaa.length < max_value) {
        // 
        if (sv_length === aaa.length) {
            ccnt++;
        }
        if (ccnt > 5) {
            //抜ける
        } else {
            sv_length = aaa.length;
            //カウントダウンを表示
            setTimeout(function () {
                countdown(aaa);
            }, 3000);
            //countdown(aaa);
            //次のサーチへ
            youtube_timer = setTimeout(() => {
                content_ready();
            }, 1000);
            return;
        }

    }

    //100件または、終了
    clearTimeout(varscroll);
    clearTimeout(youtube_timer);
    //ステータス
    var ee = $("input#search");
    ee[0].style.fontSize = "20pt";
    ee[0].style.color = "red";
    ee[0].value = cc + "秒経過(" + aaa.length + "件取得しました。)";
    //    
    //
    $.each(aaa, function (i, val) {
        //titleとthumbnail、durationを抽出
        //ここで、初回のサーチで正常にデータを取得できているのか、二回目はOKなのだが。            
        document_obj = dom_parser.parseFromString(val.innerHTML, "text/html");
        try {
            duration = document_obj.getElementsByClassName("ytd-thumbnail-overlay-time-status-renderer")[1].innerText;
        } catch (e) {
            //alert("durationエラー "+i+" "+e);
            return true;
        }
        duration1 = duration.replace(/[\s]/g, "");
        title = document_obj.getElementById("video-title").innerText;
        title1 = title.replace(/(^\s+)([\S].*)([\s]*)/g, "$2");
        //****************************************************************************
        //これまでは、この形式で取得できていたが、変更になった。
        //****************************************************************************

        thumbnail = document_obj.getElementsByTagName("img")[0].src
        //サムネイルが""ならば、
        if (thumbnail !== "") {
            ccc.push([thumbnail, duration1, title1, "検索"]);
        } else {

        }
    });
    ddd.push(ccc);
    //このメッセージがたまに出るが、原因は、
    //dddが取得できていない。
    if (ddd[0].length === 0) {
        //alert("リストが取得できません。");
        //この場合、一度、タイマーで遅らせてみる
        //alert("リストが取得できません。ddd=[]　aaa.length= "+aaa.length+" ccc.length="+ccc.length);
        setTimeout(function () {
            //cc=1;
            varscroll = setInterval(function () {
                window.scrollBy(0, 1000);
            }, 1000);
            youtube_timer = setTimeout(() => {
                content_ready();
            }, 1000);

        }, 500);
        return false;
    }
    //*******************************************************
    //check_slist()でフィルタリング
    //*******************************************************
    check_slist(ddd[0]);
}
//******************************************************************************
//フィルタリングするには、skip_list[]とリストが必要
//skip_list[]はファイルから取得、リストはパラメータ
//結果は、返却する。
//******************************************************************************
//console.log("(3)check_slist()定義");
async function check_slist(list) {
    var list_data = [];
    resolve = await fetch("https://autoplay.tecoyan.net/get_skipfile.php");
    result = await resolve.json();

    let skip_list = result;
    let i, j;
    for (i = 0; i < list.length; i++) {
        //リストタグからタイトルを取得
        let title = list[i][2];
        //全てのスキップワードをチェック
        let flag = true;
        for (j = 0; j < skip_list.length; j++) {
            //■ スキップワードをロード　
            var sword = skip_list[j].word;
            var ss = sword.replace("\n", "");

            //スキップワード有りか?
            var rs = title.indexOf(ss);
            if (rs === -1) {
                //なし
                flag = true;
            } else {
                //有り
                flag = false;
                break;
            }

        }
        //
        if (flag === true) {
            //	
            list_data.push(list[i]);

        }

    }

    //結果を返す
    list = list_data;
    list_info = list_data.length;
    //ジャンルを指定して保存場合、
    //一つのリストを生成
    var param = {
        data: list
    };
    //一番目　list_dataを生成(html形式)　
    var response = $.ajax({
        url: 'https://service-tag.tecoyan.net/youtube_list_create_ss.php',
        type: 'POST',
        dataType: 'html',
        cache: false,
        data: param,
        async: false
    }).responseText;
    //二番目  ここで、user_messテーブルを見て、そのvidがあれば、c_iconを追加する。
    //全レコードを取得
    var data = $.ajax({
        url: 'https://service-tag.tecoyan.net/user_mess_vid_db.php',
        type: 'GET',
        dataType: 'json',
        cache: false,
        async: false
                //data: param,
    }).responseText;
    var vid_array = JSON.stringify(data);
    vid_array = JSON.parse(vid_array);
    //ここで、user_messテーブルを見て、そのvidがあれば、c_iconを追加する。
    //var dom_parser = new DOMParser();
    document_obj = dom_parser.parseFromString(response, "text/html");
    var elem = document_obj.getElementsByClassName('c_icon');
    var elem_vid = document_obj.getElementsByClassName('youtube');
    $.each(elem, function (i, val) {
        //
        if (val.outerHTML.indexOf("＜img") !== -1) {
            //<imgあり
        } else {
            //<imgなしで、user_messテーブルに当該vidがあれば<imgを追加する
            var vid = elem_vid[i].id;
            if (vid_array[vid] === "on") {
                $(val).html("<img style=\"position:relative;top:8px;\" src=\"https://sample-blog.tecoyan.net/images/comment.png\">");
            }
        }
    });
    response = document_obj.documentElement.innerHTML;
    //********************************************************************    
    //リストをDB保存する
    //この時点ではidは決まらない。DB出力時に決定する。とりあえず0にする。
    //
    //********************************************************************
    var param = {
        id: 0,
        user_name: "リスト太郎",
        thumbnail: list[0][0],
        title: list[0][2],
        page_type: list[0][3],
        genre: "未分類",
        list_data: response,
        list_info: list_info
    };
    //console.log("■■tab_title = " + list[0][2]);

    //三番目
    var resp = $.ajax({
        url: 'https://service-tag.tecoyan.net/youtube_list_save_db.php',
        type: 'POST',
        dataType: 'html',
        cache: false,
        data: param,
        async: false
    }).responseText;
    //■********************************************************************
    //■background.jsへ、'yt_youtube_list'メッセージで、リストとqueryを送る
    //
    //■content:response
    //■********************************************************************
    console.log("■youtbeページ 関数 check_slist(list) chrome API (3-1)runtime.sendMessage yt_youtube_list query "+query);
    chrome.runtime.sendMessage({data: 'yt_youtube_list', site: document.URL, content: {body: response, query: query}, url: "www.youtube.com"});

}
//******************************************************************************
//countdownメソッド
//******************************************************************************
//console.log("(4)countdown()定義");
function countdown(zzz) {
    if (cc === 1) {
        debugger;
        //DOMアクセスしてqueryを取得
        query = $("input#search")[0].value;
        console.log("■■youtubeページ 関数 countdown(zzz) (4-1)query "+query);
        
    }
    console.log("■■youtubeページ 関数 countdown(zzz) (4-2)query "+query);
    var ee = $("input#search");
    ee[0].style.fontSize = "20pt";
    ee[0].style.color = "red";
    ee[0].value = cc + "秒経過(" + zzz.length + "件取得しました。)";
    cc++;

}

//******************************************************************************
//タブ毎の個別処理
//タブ別処理チェック　location.hrefで判定　
//youtubeタブで実行
//今ページはyoutubeページにいるかをチェック
//
//
//******************************************************************************

if (/www.youtube.com/.test(document.URL)) {
    //youtubeタブで実行中　Yes
    if (/www.youtube.com\/watch?/.test(document.URL)) {
        //Yes
        clearTimeout(varscroll);
        clearTimeout(youtube_timer);
    } else {
        //youtubeサーチ中　No
        //if(!window.opener || window.opener.closed){
        if (window.opener.closed) {
            //Yes
            window.alert('親ウィンドウがありません。');
        } else {
            //youtubeサーチ中　タブ
            //get_valueメッセージを送信
            //max_valueを要求
            chrome.runtime.sendMessage({data: 'get_value', site: document.URL});
            //******************************************************************
            //
            //background.jsからデータ受信
            //
            //******************************************************************
            chrome.runtime.onMessage.addListener(function (msg, sender, sendResponse) {
                //
                //console.log("(6-2)background.jsからデータ受信　" + msg.type);
                if (msg.type === 'get_value') {     //max_value
                    //alert("backgroundからget_valueメッセージを受信　"+msg.content);
                    max_value = msg.content['max_value'];
                    player_tab_id = msg.content['player_tab_id']; 
                    console.log("■■youtubeタブで　イベントリスナー (6-2)background.jsからデータ受信　player_tab_id" + msg.content['player_tab_id']);
                    //
                    query = $("input#search")[0].value;
                    console.log("■■youtubeタブで　(6-2-1)input#searchからqueryを取得　"+query);
                    //以下は、www.youtube.comページで動作
                    //1秒毎にスクロールして
                    varscroll = setInterval(function () {
                        window.scrollBy(0, 1000);
                    }, 1000);
                    //1秒毎にリストを取得
                    youtube_timer = setTimeout(() => {
                        ccnt = 0;    //リセット
                        content_ready();
                    }, 1000);

                    return;
                }
                return false;
            });

        }
    }
} else {
//********************************************************
//youtubeタブ以外
//クェリータブまたはプレーヤータブで実行
//
//******************************************************************************
    //ここで、親のURLを取得
    site_name = location.href;
    //*******************************************************************
    //■ background.jsからメッセージ受信
    //■ get_query,get_value,send_list,youtube_list
    //■*******************************************************************
    //console.log("(5-2)background.jsからデータを受信待ち リスナー登録");
    chrome.runtime.onMessage.addListener(function (msg, sender, sendResponse) {
        //
        //console.log("(5-3)background.jsからデータ受信　" + msg.type);
        switch(msg.type){
            case "get_query":
                console.log("■■favoriteタブ　 　 イベントリスナー (5-3-1)background.jsからデータ受信　" + msg.type);
                console.log("■■favoriteタブ　 　 イベントリスナー (5-3-2)selected_strings " + msg.content);
                //
                $.ajax({
                    url:'https://blog-tool.tecoyan.net/db/get_max_value_db.php',
                    method: 'GET',
                    dataType: 'html',
                    cache: false,
                    success: function (data) {
                        max_value = data;
                        //console.log("(5-3-3)■■■sortable リストを更新 player_tab_id "+player_tab_id);  
                        $("#selected_strings").html("<img src=https://favorite.tecoyan.net/images/yy19.png></img>  <span style='position:relative;top:-6px;font-size:0.8em;'>(yyプラグイン v1.0)</span><br>"+msg.content+ "(max検索件数　"+max_value+" "+player_tab_id+")<br><br>");
                        speechSynthesis.speak(
                                new SpeechSynthesisUtterance("サーチ結果のリストで再生します。")
                                );

                        location.href = "#top_title";
                        //console.log("(5-3-3)■■■sortable リストを更新");            
                        return;   
                    },
                    fail: function (data) {
                         alert("fail: get_ipaddress_data.php");
                    }             
              
                });    
                return;
                break;
            case "get_value":        
                //max_valueを取得
                max_value = msg.content['max_value'];
                player_tab_id = msg.content['player_tab_id'];
                
                console.log("■■favoriteタブ　 　 (5-3-4)get_value受信 " + max_value+" palyer_tab_id "+player_tab_id);
                return;
                break;
            case "send_list":
                console.log("■■favoriteタブ　 　 (5-3-5)send_list受信 " + max_value);
                //send_listメッセージならば、リスト更新して、再生スタート

                $("#sortable").css("width", "660px");
                $("#sortable").html(msg.content);
                $("#sortable")[0].title = "plugin";
                $(".ui-state-default").css("draggable", "false");
                location.href = "#top_title";
                idx_idx = 0;
                LoadCheck();
                //console.log("(5-3-6)■■■sortable リストを更新");
                return;
                break;
                
            case "yt_youtube_list":        
                //このページがプレーヤを持つか否かをチェック
                //console.log("(5-3-7)('#sortable').length " + $('#sortable').length);
                //
                if (location.href.indexOf("favorite.tecoyan.net") === -1) {
                    //******************************************************************
                    //プレーヤーなしのタブ(一般Webページtab)
                    //プレーヤー以外のページの場合、プレーヤーを起動してから、リストを要求
                    //******************************************************************
                    //console.log("(5-3-8)window.open() 実行"); //
                    window.open("https://favorite.tecoyan.net");
                    //background.jsへ「デフォルトプレーヤー」へリストを送る
                    //msg.data  "send_list"
                    chrome.runtime.sendMessage({data: 'send_list', site: document.URL, content: msg.content['body']});

                    return;
                }else{
                    //**********************************************************************
                    //favorite.tecoyan.net　プレーヤーのtab
                    //**********************************************************************
                    if (msg.type === 'yt_youtube_list') {
                        //console.log("(5-3-9)yt_youtube_list 受信");
                            //プレーヤーあり
                            //**********************************************************
                            // Call the specified callback, passing
                            // ここで、リストを更新する。msg.content
                            //**********************************************************
                            if (msg.content['body'] !== "") {
                                    //plugin_flag = 1;		//この変数を参照するには、set on
                                    //リストを更新
                                    $("#sortable").css("width", "660px");
                                    $("#sortable").html(msg.content['body']);
                                    $("#sortable")[0].title = "plugin";
                                    //draggable:trueを外す
                                    $(".ui-state-default").css("draggable", "false");
                                    //ここで、最新のmax_valueを要求
                                    //get_queryメッセージ
                                    console.log("■■favoriteタブ　 　 イベントリスナー (5-3-10)get_query 要求 ");
                                    chrome.runtime.sendMessage({data: 'get_query', site: document.URL});     

                            } else {
                                //console.log("空リストです。");
                            }
                    }else{
                        //console.log("msg.type ????? "+msg.type);
                    }
                }
        }
        return false;
    });

}
```




# 今後の課題
今回のプラグインは、テキスト文字列の選択のみですが、次回のプラグインでは、テキスト入力フィールドも追加して「なんでも検索」できるプラグインを公開予定です。


# あとがき
yahooサーチとyoutubeサーチの二つのサーチ方法がひとつのポップアップで選択してできる手軽さが特長です。ポップアップがテキスト文字の選択だけで表示できることです。
ネット上にはさまざまなサーチサイトがあります。それらの個々のサーチサイトを効率的に融合して更に使い勝手のいい、メタサーチプラグインを作成して行く予定です。