17/06/06

Google Chromeの拡張機能を作ってみる3~タイマーの情報を管理とバッジに表示する~


今回は、バックグラウンド処理とバッジ処理を行なっていきます。
バックグランドはデータを管理するのに適しています。
バッジはツールバーのアイコンにテキストを表示したりできます。


 

chrome拡張機能を作成の記事一覧
基礎知識
ポップアップの表示とjsのデバック方法
タイマーの情報を管理とバッジに表示する
コンテキストメニューを表示する
タイマーが終了したら通知する

タイマー部分の実装

とりあえず、このブロックのソースコード
前回、ポップアップを表示できるようにしました。
しかし、このままでは動かないので、タイマー機能をtimer.jsで実装していきます。
とりあえず、timer.jsをすべて以下のように書き直してください。

(function(){
    var $ = {};
    $.timer;
    $.remainder;
    $.timeout;

    // タイマーにしようするノードの処理
    document.addEventListener("DOMContentLoaded", function(event){
        $.timer_start = document.querySelector(".timer__start");
        $.timer_start.addEventListener("click", get_input);
        $.list = document.querySelector(".list");
        $.text = document.createElement("p");
        $.timer_stop = document.createElement("input");
        $.timer_stop.classList.add("timer__stop");
        $.timer_stop.setAttribute("type", "timer_stop");
        $.timer_stop.setAttribute("value", "delete countdown timer");
        $.timer_stop.addEventListener("click", function(){
            $.timer = "";
            remove_node();
            clearTimeout($.timeout);
        });
        $.br = document.createElement("br");
    })

    // input[type="text]部分を取得する
    function get_input(){
        if($.timeout){
            clearTimeout($.timeout);
        }
        var e_hour = document.getElementsByName("timer-h");
        var e_minute = document.getElementsByName("timer-m");
        var e_second = document.getElementsByName("timer-s");

        var hour = e_hour[0].value === ""? 0: Number(e_hour[0].value);
        var minute = e_minute[0].value === ""? 0: Number(e_minute[0].value);
        var second = e_second[0].value === ""? 0: Number(e_second[0].value);

        generate_timer(hour, minute, second);
    }

    // タイマーを作成する
    function generate_timer(h, m, s){
        // 追加したタイマーの時間を確認できるようにするために999ミリ秒を追加している。
        var mill = 999;
        // タイマーを作成した時の現在の時間を管理
        $.timer = new Date();
        $.timer.setHours($.timer.getHours() + h);
        $.timer.setMinutes($.timer.getMinutes() + m);
        $.timer.setSeconds($.timer.getSeconds() + s);
        $.timer.setMilliseconds($.timer.getMilliseconds() + mill)
        // 残り時間を算出
        $.remainder = $.timer.getTime() - new Date().getTime();
        // 2桁に直す
        var parse_object = parse_time($.remainder);
        var str = parse_object.h + ":" + parse_object.m + ":" + parse_object.s
        // nodeを作成
        generate_node(str);
        // タイマーを動かす
        update_timer();
    }

    // タイマーを動かす処理
    function update_timer(){
        // 今回は最小値が秒なので、1秒ごとに再帰処理
        $.timeout = setTimeout(update_timer, 1000);
        var current = new Date().getTime();
        var str = "";
        var h,m,s;

        // タイマーを作成した時の時間から現在の時間を引いて差分を出す
        $.remainder = $.timer.getTime() - current;

        if($.remainder > 0){
            var parse_object = parse_time($.remainder);
            var str = parse_object.h + ":" + parse_object.m + ":" + parse_object.s
            $.text.innerHTML = str;
        }else{
            clearTimeout($.timeout);
            time_is_up();
        }
    }

    // タイマーをDOMに追加する
    function generate_node(str){
        // 初期化
        remove_node();
        $.text.innerHTML = str;
        $.list.appendChild($.text);
        $.list.appendChild($.br);
        $.list.appendChild($.timer_stop);
    }

    // タイマーをDOMから削除
    function remove_node(){
        $.text.classList.remove("transparency");
        while($.list.firstChild){
            $.list.removeChild($.list.firstChild);
        }
    }

    // タイムアップ処理
    function time_is_up(){
        $.text.classList.add("transparency")
    }

    // 時間を2桁にする
    function parse_time(_num, isMill){
        var num = _num;
        var h = Math.floor(num / (60 * 60 * 1000));
        num = num - h * (60 * 60 * 1000);

        var m = Math.floor(num / (60 * 1000));
        num = num - m * (60 * 1000);

        var s = Math.floor(num / 1000);

        if(isMill){
            var mill = num - s * 1000;
            return {
                h: h,
                m: m,
                s: s,
                mill: mill
            };
        }else{
            return {
                h: h,
                m: m,
                s: s
            };
        }
    }

})();

さてこれで、タイマーの実装ができました。
しかし、タイマーを設定した後、ポップアップを閉じ、再度開くと正しく動かないと思います。

 

これはなぜかというと、
ポップアップを開いた時にindex.htmlはリロードされ、
ポップアップを閉じた時にindex.htmlはアンロードされます。
そのため、タイマーのデータが消えてしまいます。

タイマーの情報をバックグラウンドで管理する

とりあえず、このブロックのソースコード
そこでバックグラウンドを用いていきます。
background.jsは拡張機能がリロードされた時から動いているので、
データを一元で管理するのに適しています。

またデータを管理するためにはstorageを利用します。
そのためにはまず、manifest.jsonで権限を追加します

//manifest.json
    "background":{
        "scripts": ["background.js"]
    },
+   “permissions”:[
+       "storage"
+   ]
}

permissionsでapiの権限を許可します。
storageは拡張機能で用いる、web storageになります。

 

次に、timer.jsとbackground.jsでデータの管理を行なっていきます。
まずはtimer.jsに変更します。

//timer.js
(function(){
    var $ = {};
    $.timer;
    $.remainder;
    $.timeout;
+   // 6行目あたり
+   // background.jsの変数にアクセスする
+   $.background = chrome.extension.getBackgroundPage();
+   window.addEventListener("unload", function(){
+       // タイマーの時間をbackgroundに送る
+       $.background.close_popup($.timer);
+   })

-----
-----

$.timer_stop.addEventListener("click", function(){
    $.timer = "";
    remove_node();
    clearTimeout($.timeout);
    //27行目あたり
+   // storageのtimerを削除
+   chrome.storage.local.remove("timer", function(){});
});
$.br = document.createElement("br");
+ // chrome storageのkeyがtimerを取得 
+ chrome.storage.local.get("timer",function(res){
+   // json形式のstorageに{timer: data}があれば取得
+   if(res.hasOwnProperty("timer")){
+       // jsonを連想配列に変換
+       var parse_data = JSON.parse(res.timer);
+       if(parse_data){
+           // Date型を生成
+           var date = new Date(parse_data);
+           var current = new Date().getTime();
+           // 設定した時間が過ぎていなければタイマーを作成
+            if(date.getTime() - current > 0){
+                var diff = date.getTime() - current;
+                var parse_object = parse_time(diff, true);
+                generate_timer(
+                   parse_object.h, 
+                   parse_object.m, 
+                   parse_object.s, 
+                   parse_object.mill
+                );
+            }
+       }
+    }
+})

つぎにbackground.jsを変更します。
とりあえず以下にすべて書き直してください

//popupが閉じた時のイベント
function close_popup(_data){
    var data = _data.getTime();
    var json = JSON.stringify(data);

    //storageにデータをセット
    chrome.storage.local.set({"timer":json}, function(){
        // console.log("saved")
    });

    set_up_notification(data);
    update_badge();
}

さて、これでポップアップを閉じた時に、タイマーのデータが残っていれば、
backgroundに渡しstorageに保存。再度ポップアップを開いた時に、
storageにあるデータからタイマーを作成ができるようになりました。

 

リロードするか再度読み込んでみてください
以下のように動作すると思います。

 

バッジに残り時間を表示する

とりあえず、このブロックのソースコード
しかし、これでは今どれだけ時間が経ったかわかりません。
なのでバッジを利用して、残り時間を表示してみましょう。
ただ、バッジは文字数が限られてるので留意が必要です。

 

ではbackground.jsを変更していきます

// background.js
// 0行目
+ var timer_store;
+ var badge_timeout;
+ 
-----
-----

    //storageにデータをセット
    chrome.storage.local.set({"timer":json}, function(){
        console.log("saved")
    });
    // 10行目あたり
+    // タイマーの情報
+    // これを元に、バッジの残り時間や通知の時間をセットする
+    timer_store = _data;
+
+    //バッジを動かす
+    update_badge();
+}
+
+// バッジを再帰で動かす
+// backgroundで動作しているために問題なく動く
+function update_badge() {
+   if(!timer_store){
+       // バッジのテキストを初期化
+       chrome.browserAction.setBadgeText({text:""});
+       if(badge_timeout){
+           // 再帰処理をとめる
+           window.clearTimeout(badge_timeout);
+       }
+       return;
+   }
+   // 再帰処理
+   badge_timeout = setTimeout(update_badge, 1000);
+
+   // 残り時間
+   var remainder = timer_store.getTime() - new Date().getTime();
+   var tmp = remainder;
+
+   var h = Math.floor(tmp / (60 * 60 * 1000));
+      tmp = tmp - h * (60 * 60 * 1000);

+    var m = Math.floor(tmp / (60 * 1000));
+    tmp = tmp - m * (60 * 1000);

+   var s = Math.floor(tmp / 1000);
+    // バッジの文字数が限られているので、h,m,sのどれかの残り時間を表示する
+    // 優先順位は h > m > s
+   if(remainder > 0){
+       if(h > 0){
+           // バッジにテキストセットする
+           chrome.browserAction.setBadgeText({text: h + "h"});
+       }else if(m > 0){
+           chrome.browserAction.setBadgeText({text: m + "m"});
+       }else{
+           chrome.browserAction.setBadgeText({text: s + "s"});
+       }
+       // バッジ部分をホバーした時に出てくるテキストにも残り時間を表示する
+       chrome.browserAction.setTitle({title: h + ":" + m + ":" + s});
+   }else{
+       // 残り時間がなくなったら初期化する
+       timer_store = "";
+       init_text();
+   }
+}
+
+// ポップアップが表示された時、バッジを見る必要がないので、
+// バッジの再帰処理を止めて、初期化する
+function clear_timeout(){
+   // ループ処理を停止
+    window.clearTimeout(badge_timeout);
+    init_text();
+}

+// テキストを初期化する
+function init_text(){
+   // バッジ部分をホバーした時にでてくるテキスト
+   chrome.browserAction.setTitle({title: "sample_timer"});
+   // バッジのタイトルを初期化
+   chrome.browserAction.setBadgeText({text:""});
+}

次にtimer.jsでポップアップが開かれた時に、
バックグランドの再帰処理を止め、バッジのテキストを初期化します。

//timer.js
$.timer_stop.addEventListener("click", function(){
    $.timer = "";
    remove_node();
    clearTimeout($.timeout);
+   // 29行目あたり
+   // storageのtimerを削除
+   chrome.storage.local.remove("timer", function(){});
+   // バックグラウンドの再帰処理を止める
+   // バッジなども初期化する
+   $.background.clear_timeout();
});
+ $.background.clear_timeout();

リロードするか再度読み込んでみてください。
以下のように動くと思います。

 

 

今回は以上になります。
javascriptのコードが長くなってしまいましたが最後までありがとうございました。

 

次回

 

スポンサーリンク

メールアドレスが公開されることはありません。

youya66

だらけとびびり、それとちょっぴりのてきとーさ。

コアラになってだらだらしながら愛されたい。