Greasemonkeyによるアプリケーション開発

第3回 Greasemonkeyによるカレンダアプリケーションの機能向上

この記事を読むのに必要な時間:およそ 12 分

次に,今回のスクリプトで新たに実装した「選択日」を動かす部分について解説します。

図2 選択日の移動処理を実装したユーザスクリプト後半部分(ユーザスクリプトのソースコード全体はこちら

    // ↓↓↓↓ここからA部分 ↓↓↓↓
    /**
     * dateで選択した日付に移動する
     */
    function selectDate(date){             // -(1)
      changeMonthIfNeed(date);             // -(1-a)
      unselect(curDate);                   // -(1-b)
      select(date);                        // -(1-c)
      
      function changeMonthIfNeed(date){
        if(curDate.getMonth() != date.getMonth()){
          curDate.setDate(1); // 意図せず二月分移動してしまうのを防ぐため
          curDate.setMonth(date.getMonth());
          curDate.setFullYear(date.getFullYear());
          makeTable();
        }
      }
      function unselect(date){
        var preSelect = $(makeDateId(date));
        if(preSelect){
          preSelect.className = 
            preSelect.className.replace(/ _gcal_select/, "");
        }
      }
      function select(date){
        curDate = new Date(date);
        var select = $(makeDateId(date));
        select.className  += " _gcal_select";
      }
    }
    
    /**
     * offset分だけ月を変更してカレンダテーブルの再生成をする
     * 選択日は1日にする
     * <外部インタフェース生成用>
     */
    function makeGoMonthOffset(offset){    // -(2)
      return function(){
        if(!isShown())return;
        var newDate = new Date(curDate);
        newDate.setDate(1);
        newDate.setMonth(curDate.getMonth() + offset);
        selectDate(newDate);
      };
    }

    /**
     * offset分だけ日付を変更してカレンダテーブルの再生成をする
     * <外部インタフェース生成用>
     */
    function makeGoDateOffset(offset){     //-(3)
      return function (){
        if(!isShown())return;
        var newDate = new Date(curDate);
        newDate.setDate(newDate.getDate() + offset);
        selectDate(newDate);
      };
    }

    /**
     * 実行時の日付に移動する
     * <外部インタフェース用>
     */
    function goToday(){                    //-(4)
      if(isShown())selectDate(TODAY);
    }

    /**
     * 選択日の移動処理はカレンダを表示しているときのみ
     * 実行したい。そのために表示中か否かを示す関数を用意。
     */
    function isShown(){
      return gPanel && gPanel.style.display != "none";
    }
    // ↑↑↑↑ここまでA部分 ↑↑↑↑
    
    // 外部インタフェースとなるオブジェクトを返す(再掲)
    return {toggle:toggleCalendar,
            nextMonth:makeGoMonthOffset(1),      //(2-a)
            previousMonth:makeGoMonthOffset(-1), //(2-b)
            goToday:goToday,
            nextDate:makeGoDateOffset(1),        //(3-a)
            previousDate:makeGoDateOffset(-1),   //(3-b)
            nextWeek:makeGoDateOffset(7),        //(3-c)
            previousWeek:makeGoDateOffset(-7)    //(3-d)
    };
  })();

// ↓↓↓↓ここからB部分 ↓↓↓↓
keybind("S-c", calendar.toggle); 
keybind("S-n S-right S-down".split(" "), calendar.nextMonth); 
keybind("S-p S-left S-up".split(" "), calendar.previousMonth); 
keybind("f right".split(" "), calendar.nextDate); 
keybind("b left".split(" "), calendar.previousDate); 
keybind("n down".split(" "), calendar.nextWeek); 
keybind("p up".split(" "), calendar.previousWeek); 
keybind("S-t", calendar.goToday); 
// ↑↑↑↑ここまでB部分 ↑↑↑↑

図2のA部分は選択日を移動する処理用の関数の定義で,B部分はキーバインドの定義です。A部分で定義している主な関数は(1)selectDate,(2)makeGoMonthOffset,(3)makeGoDateOffset,(4)goToday の四つです。以下,各関数について解説します。

(1)selectDate:引数で指定した日付を「選択日」とする

この関数では(a)必要に応じて月を変更してカレンダテーブルを再生成し,(b)現在選択中の日付を非選択にし,(c)指定された日付を選択する,という処理をしています。

(a)の処理は選択中の月と指定された月が異なる場合に実行するようにしています。

選択日curDateの日にちを1日に設定してから指定されたdateの月,年に設定し,カレンダテーブルを再生成しています。最初に日にちを1日に設定しているのは,日にちによっては意図した月と異なる月になってしまう可能性があるためです。たとえば1月31日を表すDateオブジェクトに対しsetMonth(1)を実行する(Dateオブジェクトは1月を0として扱うため,1は2月を表す)と,2月31日ではなく,3月3日に変更されます。このようにDateオブジェクトは存在しない日時を示さないように適切に処理する機能があるのですが,カレンダの生成処理では指定した月に応じたカレンダを表示したいので,このような月のジャンプが起こらないようにしたいわけです。そのため日にちを先に1日するようにしています。

(b)の処理は選択日から日付セル要素を特定し,その日付セル要素のクラス名から選択日の見栄え用のクラス名を除去しています。

(c)の処理は指定された日付を新たに選択日に設定し,該当する日付セル要素のクラス名に選択日の見栄え用のクラス名を付加しています。

なお,前回も解説しましたが,ここでも関数定義を分割することで,コメントなしでもコードが読みやすくなるように工夫しています。

私は一つの関数の定義が長くなり,数十行を越えてくると全体として何をやろうとしているのか自分でも分からなくなってしまうので,このようにできるだけ関数定義は処理の単位で分割するようにしています。

ありがたいことにJavaScriptでは関数の内部で関数を定義できるので,その関数の外部で使う必要のない(内部の)関数は内部で定義しておくことができ,長い定義になるような関数の分割がやりやすくなっています。

ただし,デメリットもあって,関数呼び出しが増えるとその分実行速度も低下します。実行速度を重視したい場合は分割の度合いを下げる必要があるかもしれません。

(2)makeGoMonthOffset:引数で指定された分だけ月を移動する関数を生成する

本来必要なのは先月,来月に移動するための関数なのですが,その関数を生成するための関数を用意することでそれぞれの関数を定義する手間を省いています。引数に与えた数の分だけ現在の日付の月から移動する関数を生成するようにしているので,先月への移動は-1,来月への移動は1を引数として与えてそれぞれの関数を生成しています(2-a,2-b)⁠

また今回の実装では先月,来月に移動する処理だけを使っていますが,前年,来年に移動する処理の場合でも,±12を引数に与えれば同様の関数を生成することができます。

(3)makeGoDateOffset:引数で指定された分だけ選択日を移動する関数を生成する

makeGoMonthOffsetの日にち移動用版です。前の日,次の日,一週間前,一週間後に移動するための関数を生成するために定義しました(3-a,3-b,3-c,3-d)⁠

(4)goToday : 実行時の日付に移動する

月を何度も移動しているとふと今日に戻りたくなるものです。そのための関数を用意しておきました。

以上が今回のユーザスクリプトの主要な部分でした。

最後のB部分は前回導入したkeybind関数を使って選択日の移動のためのキー操作を定義しています。⁠読んで字のごとく」な定義になっていると思いますので解説の必要はないでしょう。

今回の実装により,カーソルキー(とf,b,p,nのEmacsライクなキー操作)を使って選択日を上下左右に移動させたり,シフト+カーソルキー(とf,b,p,nのEmacsライクなキー操作)を使って月移動させたりできるようになりました。

カレンダアプリケーションとしての便利度をちょっとあげることができたと思います。

著者プロフィール

gotin(ゴチン:GOrdon TImothy Nathanson)

会社員です。肩書きは特にありません。Greasemonkeyは会社の後輩が使っていてそれ面白いじゃんって思って使い始めました。自宅サーバで作ったRuby on Railsな自作ブログツールで作った自作ブログにGreasemonkeyのユーザスクリプトを載せたりもしていたんですが,誰にも読んでもらえなくて寂しかったのです。で,作ったままほったらかしにしていたはてなダイアリーに載せたら自作ブログよりかはありがたいことに多少読んでもらえるようになり,その後いろいろあって,そして今ここにいます。

URLhttp://d.hatena.ne.jp/gotin/

コメント

コメントの記入