前回からGreasemonkeyによるアプリケーション開発の題材としてカレンダアプリケーションを挙げ,キー押し下げにより表示・非表示を切り替えできるカレンダアプリケーションを作成しました。
今回はさらに二つの機能を追加したいと思います。
- (1) 異なる月も表示する
- (2) 予定情報を登録/表示できるようにする
異なる月の表示機能
異なる月に移動できるようにするため,カレンダ生成用の関数に手を加えます。
「現在の月」を表す変数を用意して,その値が示す月に対応するカレンダ生成を行うように変更すればうまくいきそうです。異なる月に移動するときは,その月の値を増減した上でカレンダ生成用関数を呼び出せばよいわけです。
カレンダに追加する機能の検討
早速その実装をはじめようと思うところですが,その前に少しだけ先のステップのことも考えてみます。最終的には予定情報の管理機能も追加したいので,その操作方法も先に考えておきます。
まず,登録する予定情報として記録するデータは,以下のものとします。
- 予定の年月日
- 予定の内容
予定情報の表示については,カレンダの大きさをコンパクトなままにしておきたいので,以下の点を考慮します。
- 予定情報が登録されている日付は色を変えて表示する
- 日付をカーソルキーなどで選択できるようにして,選択日に予定情報が登録されていればその内容を表示する
また,予定情報を登録するときも選択されている日付を予定情報の年月日として使うようにします。
ということで,日付を選択できるようにしたので,単純に表示中のカレンダの月を動かすだけでなく,
- 「選択日」を動かすようにする
- 「選択日」の月に応じたカレンダを表示する
という処理を実装することとします。
選択日の移動機能の実装
図1,図2(次ページ)に上記処理を実装したユーザスクリプトを示します。図1は前回のカレンダ生成処理から変更した部分を中心に示し,図2は今回のスクリプトで新たに実装した「選択日」を動かす部分を示しています。以下,順に解説します。
図1 選択日の移動処理を実装したユーザスクリプト前半部分(ユーザスクリプトのソースコード全体はこちら)
// ==UserScript==
// @name mini_calendar5
// @namespace http://gomaxfire.dnsdojo.com/
// @description the 5th mini calendar
// @include *
// ==/UserScript==
/* ユーティリティ関数郡の定義 */
/* 中略 */
/** document.getElementById() のエイリアス */
function $(id){
return document.getElementById(id);
}
/**
* Element#removeChild()のエイリアス
* $rmに与えた要素を削除する
*/
function $rm(element){
if(element && element.parentNode)element.parentNode.removeChild(element);
}
//----------------------------------------------------
// calendar application
//----------------------------------------------------
var calendar = (function(){ // -(1)
var frame = $div(); // -(2)
var table = null;
// calendar用処理群の中で共通利用する変数群を定義
var curDate = new Date(); //選択中の日付 -(3)
var TODAY = new Date();
var TODAY_YEAR = TODAY.getFullYear();
var TODAY_MONTH = TODAY.getMonth();
var TODAY_DATE = TODAY.getDate();
var CSS_PREFIX = "_gcal_";
function css(name){
return CSS_PREFIX + name;
}
// 選択中の日付セルを処理しやすくするために
// 日付セルにIDを付加する。そのIDをつくる関数 -(4)
function makeDateId(d){
return css([d.getFullYear(),
f(d.getMonth() + 1),
f(d.getDate())].join("-"));
function f(n){
return n < 10 ? "0" + n : n;
}
}
// calendar処理群としてまとめたので
// makeCalendarTableからmakeTableに名称変更
function makeTable(){
var DAYS = "Sunday Monday Tuesday Wednesday Thursday Friday Saturday".split(" ");
$rm($(css("")));// 別の月に変更する場合のために以前の内容を破棄 -(2)
table = $table({id:css("")}); //-(2)
$add(frame, table); //-(2)
setMonthHeader();
setDayHeader();
setDates();
setStyle();
return frame;
function setMonthHeader(){
// curDateの年/月を使う
var year = curDate.getFullYear(); // -(3)
var month = curDate.getMonth() + 1; // -(3)
$add(table,
$add($tr({className:css("header")}),
$add($th({className:css("header"), colSpan:7}), year + "/" + month)));
}
// 中略
function setDates(){
// curDate自体を動かさないように,
// curDateを複製して表示用Dateオブジェクトとして利用する
var d = new Date(curDate); // -(3)
var nextMonth = d.getMonth() < 11 ? d.getMonth() + 1 : 0;
d.setDate(1);
d.setDate(-d.getDay() + 1);
// 中略
/**
* 年月日が一致したときのみ「今日」の見栄えにする
*/
function setClassName(td, d){
var buffer = [];
if(d.getMonth() == curDate.getMonth()){ // -(3)
buffer.push(css("onDate"));
buffer.push(css(DAYS[d.getDay()]));
} else {
buffer.push(css("outDate"));
}
if(d.getDate() == TODAY_DATE &&
d.getMonth() == TODAY_MONTH &&
d.getFullYear() == TODAY_YEAR){
buffer.push(css("today"));
}
td.className = buffer.join(" ");
}
}
function setStyle(){
var style =
<><![CDATA[
//中略
#_gcal_ td._gcal_select { //-(5)
color : gold;
font-weight : bold;
border-top : 1px solid white;
border-left : 1px solid white;
border-bottom : 1px solid #666666;
border-right : 1px solid #666666;
background-color:#FFFFEE;
}
]]></>;
GM_addStyle(style);
}
}
var gPanel = null;
function toggleCalendar(){
if(!gPanel) {
gPanel = $add($div({id:"_gpanel"}), makeTable());
$add(document.body, gPanel);
selectDate(curDate);
}
with(gPanel.style){
if(display != "block"){
display = "block";
} else {
display = "none";
}
}
}
/* 図2のA部分がここにくる */
// 外部インタフェースとなるオブジェクトを返す // -(1)
return {toggle:toggleCalendar,
nextMonth:makeGoMonthOffset(1),
previousMonth:makeGoMonthOffset(-1),
goToday:goToday,
nextDate:makeGoDateOffset(1),
previousDate:makeGoDateOffset(-1),
nextWeek:makeGoDateOffset(7),
previousWeek:makeGoDateOffset(-7)
};
})();
/* 図2のB部分がここにくる */
前回からの変更点はいくつかありますが,ポイントとなる部分にコメントで番号づけしました。以下その番号順に解説します。
- (1)カレンダ処理をオブジェクトにまとめた
これまでのユーザスクリプトではカレンダ処理は「実行時の月のカレンダを表示する」という処理一つだけだったのですが,今回からは選択日の移動処理や月の移動処理など複数の処理を定義する必要が出てきました。それら複数の処理を定義するために,複数の関数を定義するわけですが,それらの関数は次の二つの種類に分けられます。
- (a) カレンダ処理を利用するために呼び出す関数。
- (b) 関数の定義のために定義した関数。カレンダ処理のために直接呼び出すことはない。
この二種類の関数定義がそのまま混在していると混乱の元ですし,間違って(b)のタイプの関数を呼び出してしまうと意図せず(a)の処理に影響してしまう可能性もあります。そのため,(a)のタイプの関数だけを外部から呼び出せるようにし,(b)のタイプの関数を内部に閉じ込めて外部から呼び出すことができないようにするため,オブジェクトの形にまとめるようにしました。
- (2)月の変更に応じたカレンダテーブルの削除処理のため,それを囲むdiv要素を加えた
月を変更したときはカレンダを表すテーブルを一旦削除し,改めて変更した月に応じたカレンダテーブルを生成するように処理を変更しました。そのため,再生成したカレンダテーブルを配置する「場所」が必要になり,その役目をするdiv要素を置くことにしました。
- (3)選択日に応じてカレンダを生成するようにした
これまではnew Date()によって得られる実行時の日付(の月)に応じたカレンダを生成していましたが,「選択日」を変数curDateで表すこととして,その日付に応じてカレンダを生成するようにしました。
- (4)日付セルを処理しやすくするため,IDを付加するようにした
「選択日」がどれなのか,表示上分かりやすくするために選択日を示す日付セルのclass属性の値を変えて見栄えを変えるようにしました。この日付セルの見栄えを変更する処理のため,見栄えを変更すべき日付セルを特定する必要が必要になるわけですが,この日付セルの特定を簡単に行えるようにするため各日付セルにID属性を付加することにしました。今回追加したユーティリティ関数「$」を使うことで,IDを指定すれば簡単に所望の日付セル要素を取得できるからです。
- (5)選択日のスタイル定義を追加した
カレンダテーブル中の日付セルのうちどれが「選択日」なのか一目で分かるよう,スタイル定義を追加しました。
以上,前回のカレンダ生成処理からの主要な変更点を解説しました。

