【Blogger】カレンダーウィジェット

2024/10/05
アイキャッチ

先日からサイトマップページに、こちらの p--q さん作の Blogger 専用カレンダーウィジェットを試しに置いてみています。

ファビコンp--q
Blogger:カレンダー(12)Calendar5_Bloggerモジュールの導入のスクリーンショット
Blogger:カレンダー(12)Calendar5_Bloggerモジュールの導入
Calendar4_Bloggerモジュールコードが読みにくかったので整理しました。ついでにアイテムページを開いた時に、その投稿の日付の投稿リストを展開して、アイテムページに表示している投稿をハイライトするようにしました。 前の関連記事: Blogger:カレンダー(11)...
https://p--q.blogspot.com/2017/03/blogger12calendar5blogger.html
CONTENTS

再導入した訳

このカレンダーウィジェット、今回初めての導入ではなく、私が Blogger に来た2017年にサイドバーウィジェットとして使ったことがあるですが、次第に不要(邪魔?)に感じてきたもんで、結局数ヶ月くらい使っただけで外してしまっていました。

で、なんでまた導入する気になったのかというと、サイドバーじゃなくてサイトマップページなら邪魔にならないし、「更新日の確認が出来るウィジェット」として置いとけば便利でいいんじゃないかな、とふと思い付いたから。我ながら中々いいアイデアだなと自画自賛(笑)してますけどどうでしょう?

改善済(?)コードを紹介

実はこのウィジェットには、ハイライト対象外の記事がハイライトされるなどの不具合があって私もご指摘させて頂いたのですが、作者さんがご多忙のせいなのか(それともやる気が無いのか?)一向に修正対応される気配なし。ですが、上記記事のコメントで私の後で別のバグを指摘されていた booskanium さんが自ら手を加えられたようで、少なくとも既知のバグに関してはほぼ改善されている可能性が。

ちなみに私も出来る範囲でいくつか改善を施していまして、各要素に クラスや id を振って CSS でデザインカスタマイズをやりやすくしたり、サムネがない時用の No Image 画像を設定したり、日本語表記と英語表記の切り替えを手動でできるようにしたり、2020年以降の祝日変更対応...etc

というわけでせっかくなので、(たぶん)不具合もなく、サイドバーに置いても固定ページに置いても使い勝手の良い便利なカレンダーウィジェットのコードを載せておきますので、使ってみたいという方は宜しければお試しください。色んなテーマで使える用と F-light 専用の2種類用意してあります。

サイドバーに置く場合は、レイアウトページにて HTML/Javascript ガジェットのコンテンツ欄にコードをコピペして保存してください。当ブログのように固定ページに置く場合は編集モードを HTML ビューにしてコードをコピペしてください。

色んなテーマ用コード

<style>
/*カレンダー全体(記事リストを除く)*/
#calendar {
display: flex;
flex-wrap: wrap;
}
/*記事のある日付のハイライト*/
.posted {
text-decoration: none;
background: rgba(176, 224, 230, .5);
transition: .2s;
border-radius: 3px;
cursor: pointer;
}
.posted:hover {
background: rgba(176, 224, 230, .8);
}
/*ナビゲーションボタン全体*/
#cal-title,
.btn {
background: dodgerblue;
color: #fff;
transition: .2s;
padding: 2px 0;
flex: 0 0 18%;
text-align: center;
}
/*年月表示部分(投稿・更新切り替えボタン)*/
#cal-title {
flex:1 0 64%;
text-align: center;
}
/*ナビゲーションボタン(最大年の右ボタン、最終年の左ボタンを除く)のホバー*/
#cal-title:hover,
#right-btn:hover,
#left-btn:hover {
opacity: .8;
cursor: pointer;
}
/*右ボタン・左ボタン*/
.btn:nth-of-type(3) {
border-radius: 0 3px 3px 0;
}
.btn:nth-of-type(1) {
border-radius: 3px 0 0 3px;
}
/*曜日・日付*/
.dow,
.posted,
.nopost {
flex: 1 0 calc(14% - 4px);
text-align: center;
margin: 2px;
}
/*記事一覧の枠*/
.post-list,
#current-post {
border-top: dashed 1px rgba(128, 128, 128, .6);
padding: 5px;
}
/*表示中の記事*/
#current-post {
background: #eef;
pointer-events: none;
}
#current-post .post-title a {
color: inherit;
}
/*記事リストのタイトル*/
#list-title {
display: flex;
flex-direction: column;
padding-top: 5px;
text-align: center;
}
/*サムネイル画像*/
.post-img {
float: left;
padding-right: 5px;
border-radius: 5px;
transition: .2s;
}
.post-img img {
border-radius: 5px;
}
.post-img:hover {
opacity: .8;
}
/*記事タイトル*/
.post-title {
line-height: 1.5;
text-align:left;
}
.post-title a {
text-decoration: none;
}
.post-list a:hover {
text-decoration: underline;
}
/*読込中アニメーション*/
.loader {
border: 4px solid transparent;
border-top-color: #666;
border-right-color: #666;
border-bottom-color: #666;
border-radius: 50%;
width: 60px;
height: 60px;
animation: spin .7s linear infinite;
margin: 5em auto 2em;
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
</style>
<div id="calendar5_blogger"><div class="loader"></div></div>
<script>
// Calendar5_Bloggerモジュール
var Calendar5_Blogger = Calendar5_Blogger || function () {
var cl = {
defaults: {}, // デフォルト値を入れるオブジェクト。
callback: { // コールバック関数。
getArticles: function (json) { // 指定した月のフィードを受け取る。
Array.prototype.push.apply(g.posts, json.feed.entry); // 投稿のフィードデータを配列に追加。
if (json.feed.openSearch$totalResults.$t < g.max) { // 取得投稿数がg.maxより小さい時はすべて取得できたと考える。
var re = /\d\d(?=T\d\d:\d\d:\d\d\.\d\d\d.\d\d:\d\d)/i; // フィードの日時データから日を取得するための正規表現パターン。
var yyyy = /\d\d\d\d(?=\-\d\d\-\d\dT\d\d:\d\d:\d\d\.\d\d\d.\d\d:\d\d)/i; // ※ booskanium 19.05.18 日時データから年を取得するための正規表現パターン。
var mm = /\d\d(?=\-\d\dT\d\d:\d\d:\d\d\.\d\d\d.\d\d:\d\d)/i; // ※ booskanium 19.05.18 日時データから月を取得するための正規表現パターン。
g.posts.forEach(function (e) { // 投稿のフィードデータについて
var d = Number(re.exec(e[g.order].$t)); // 投稿の日を取得。
g.dic[d] = g.dic[d] || []; // 辞書の値の配列を初期化する。
var url = (e.media$thumbnail) ? e.media$thumbnail.url : null; // サムネイルのurl。
//g.dic[d].push([e.link[4].href, e.link[4].title, url]); // 辞書の値の配列に[投稿のURL, 投稿タイトル, サムネイルのURL]の配列を入れて2次元配列にする。
var y = yyyy.exec(e[g.order].$t); // 投稿の年を取得
var m = mm.exec(e[g.order].$t); // 投稿の月を取得
g.dic[d].push([e.link[4].href, e.link[4].title, url, y, m]); // ※booskanium 19.05.18 辞書の値の配列に[投稿のURL, 投稿タイトル, サムネイルのURL]の配列を入れて2次元配列にする。
});
var m = cal.createCalendar(); // フィードデータからカレンダーを作成する。
m.addEventListener('mousedown', eh.mouseDown, false); // カレンダーのflexコンテナでイベントバブリングを受け取る。マウスが要素をクリックしたとき。
m.addEventListener('mouseout', eh.mouseOut, false); // マウスポインタが要素から出た時。
g.elem.textContent = null; // 追加する対象の要素の子ノードを消去する。
g.elem.appendChild(m); // 追加する対象の要素の子ノードにカレンダーのflexコンテナを追加。
g.elem.appendChild(pt.elem); // 投稿リストを表示するノードを追加。
if (!eh.node && g.mc) {
pt.getPostDate()
}; // eh.nodeがnull(つまりページのロード時のみ)かつアイテムページの時のみアイテムページの投稿リストを展開する。
} else { // 未取得のフィードを再取得する。最新の投稿が先頭に来る。
var m = /(\d\d\d\d-\d\d-\d\dT\d\d:\d\d:\d\d)\.\d\d\d(.\d\d:\d\d)/i.exec(json.feed.entry[json.feed.entry.length - 1][g.order].$t); // フィードの最終投稿(最古)データの日時を取得。
var dt = new Date(m[1] + m[2]); // フィードの最終投稿(最古)データの日時の日付オブジェクトを取得。
dt.setSeconds(dt.getSeconds() - 1); // 最古の投稿の日時より1秒古い日時を取得。
if (g.m == dt.getMonth() + 1) { // 1秒古くても同じ月ならば
var max = g.y + "-" + fd.fm(g.m) + "-" + fd.fm(dt.getDate()) + "T" + fd.fm(dt.getHours()) + ":" + fd.fm(dt.getMinutes()) + ":" + fd.fm(dt.getSeconds()) + "%2B09:00"; // フィード取得のための最新日時を作成。
fd.createURL(max); // フィード取得のURLを作成。
}
}
},
},
all: function (elemID) { // ここから開始する。
g.elem = document.getElementById(elemID); // idから追加する対象の要素を取得。
if (g.elem) { // 追加対象の要素が存在するとき
st.init(); // 言語設定。
cal.init(); // カレンダーのノードの不変部分を作成しておく。
pt.init(); // 投稿リストのノードの不変部分を作成しておく。
var dt; // 日付オブジェクト。
g.mc = /\/(20\d\d)\/([01]\d)\//.exec(document.URL); // URLから年と月を正規表現で得る。g[1]が年、g.mc[2]が月。
if (g.mc) { // URLから年と月を取得できた時。つまりアイテムページの時。
var m = Number(g.mc[2]) - 1; // 月ひく1を取得
dt = new Date(g.mc[1], m, 1); // 投稿月の日付オブジェクトを取得。
} else { // アイテムページ以外の時は今日の日付を取得。
dt = new Date();
};
fd.getFeed(dt);
}
}
}; // end of cl
var g = { // モジュール内の"グローバル"変数。
max: 150, // Bloggerのフィードで取得できる最大投稿数を設定。
order: "published", // publishedかupdatedが入る。
elem: null, // 置換するdiv要素。
init_d: function (dt) { // 日付オブジェクトからカレンダーのデータを作り直す。
g.y = dt.getFullYear(); // 表示カレンダーの年を取得。
g.m = dt.getMonth() + 1; // 表示カレンダーの月を取得。
g.em = new Date(g.y, g.m, 0).getDate(); // 表示カレンダーの末日を取得。
g.posts = []; // フィードデータをリセットする。投稿のフィードデータを収納する配列。
g.dic = {}; // 投稿データをリセットする。キーを日、値を投稿のURLと投稿タイトルの配列の配列、とする辞書。
},
mc: false, // アイテムページの年[1]と月[2]の配列。
}; // end of g
var st = { // 言語置換
enM: ["Jan.", "Feb.", "Mar.", "Apr.", "May", "Jun.", "Jul.", "Aug.", "Sept.", "Oct.", "Nov.", "Dec."],
init: function () {
st.f = true; // Change value "false" for switch to English.
st.days = (st.f) ? ["日", "月", "火", "水", "木", "金", "土"] : ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]; // 曜日の配列。
st.left_btn = (st.f) ? "翌月へ" : "Newer";
st.updated = (st.f) ? " 更新" : "updated";
st.published = (st.f) ? " 投稿" : "published";
st.tooltip = (st.f) ? "投稿日と更新日を切り替える" : "Switching between published and updated";
st.right_btn = (st.f) ? "前月へ" : "Older";
}
}; // end of st
var cal = { // カレンダーを作成するオブジェクト。
_holidayC: "tomato", // 休日の文字色
_SatC: "royalblue", // 土曜日の文字色
_nodes: null,
init: function () { // カレンダーのノードの不変部分の取得。
cal._nodes = cal._createNodes();
},
_createNodes: function () { // カレンダーのノードの不変部分を作成しておく。
var m = nd.createElem("div");
m.id = "calendar";
var a = nd.createElem("div");
a.className = "btn";
m.appendChild(a);
var t = nd.createElem("div");
m.appendChild(t);
m.appendChild(a.cloneNode(true));
var d = nd.createElem("div");
d.className = "dow";
st.days.forEach(function (e, i) { // 1行目に曜日を表示させる。2番目の引数は配列のインデックス。
var node = d.cloneNode(true);
node.appendChild(nd.createTxt(st.days[i]));
cal._getDayC(node, i); // 曜日の色をつける。
if (!st.f) {
node.style.fontSize = "80%"; // 英語表記では1行に収まらないのでフォントサイズを縮小。
}
m.appendChild(node); // カレンダーのflexコンテナに追加。
});
d.className = "nopost";
for (i = 0; i < 42; i++) { // カレンダーの5行目まで作成。
m.appendChild(cal._createDateNodes(d)); // カレンダーのflexコンテナに追加。
}
d.style.display = "none"; // カレンダーの6行目はデフォルトでは表示させない。
for (i = 0; i < 7; i++) {
m.appendChild(cal._createDateNodes(d)); // カレンダーのflexコンテナに追加。
}
return m;
},
_createDateNodes: function (d) {
var node = d.cloneNode(true);
cal._getDayC(node, i % 7); // 曜日の色をつける。
return node;
},
createCalendar: function () { // カレンダーのHTML要素を作成。
var m = cal._nodes.cloneNode(true);
var dt = new Date(); // 今日の日付オブジェクトを取得。
var now = new Date(dt.getFullYear(), dt.getMonth(), 1).getTime(); // 今月の1日のミリ秒を取得。
var caldt = new Date(g.y, g.m - 1, 1);
var caldate = caldt.getTime(); // カレンダーの1日のミリ秒を取得。
if (now > caldate) { // 表示カレンダーの月が現在より過去のときのみ左矢印を表示させる。
m.childNodes[0].appendChild(nd.createTxt('\u226a'));
m.childNodes[0].title = st.left_btn;
m.childNodes[0].id = "left-btn";
}
var titleText = (st.f) ? g.y + "年 " + g.m + "月" : st.enM[g.m - 1] + " " + g.y + " ";
titleText += (g.order == "published") ? "" : st.updated;
m.childNodes[1].appendChild(nd.createTxt(titleText));
m.childNodes[1].title = st.tooltip;
m.childNodes[1].id = "cal-title";
dt = new Date(cl.defaults.StartYear, cl.defaults.StartMonth - 1, 1); // 最初の投稿月の日付オブジェクトを取得。
var firstpost = new Date(dt.getFullYear(), dt.getMonth(), 1).getTime(); // 1日のミリ秒を取得。
if (firstpost < caldate) { // 表示カレンダーの月が初投稿月より未来のときのみ右矢印を表示させる。
m.childNodes[2].appendChild(nd.createTxt('\u226b'));
m.childNodes[2].title = st.right_btn;
m.childNodes[2].id = "right-btn";
}
var day = caldt.getDay(); // 1日の曜日を取得。日曜日は0、土曜日は6になる。
var c = 9 + day; // 1日の要素番号−1。
pt.dic = {}; // 日付、とカレンダーノードの辞書をリセットする。
for (var i = 1; i < 1 + g.em; i++) { // 1日から末日まで。
var d = m.childNodes[c + i];
d.appendChild(nd.createTxt(i));
var t = ""; // nullはundefinedと表示されるのでだめ。
if (i in g.dic) { // 辞書のキーに日があるとき
pt.dic[i] = d; // アイテムページで投稿リストを展開するための辞書。keyが日付、値はカレンダーのノード。
g.dic[i].forEach(function (arr) { // title属性に投稿タイトルのみ入れる。
t += (t) ? "\n" + "\u30fb" + arr[1] : "\u30fb" + arr[1];
});
d.title = t;
d.className = "posted";
}
cal._getHolidayC(d, i); // 祝日に色をつける。
}
if (day + g.em > 35) { // 最終行の表示。
for (var i = 45; i < 52; i++) {
m.childNodes[i].style.display = null;
}
}
return m;
},
_getHolidayC: function (node, i) { // 祝日に色をつける。JSON文字列はhttps://p--q.blogspot.jp/2016/12/blogger10json.htmlを作成。
// キーは年、値は二元元配列。1次が月数、二次が祝日の配列。
var holiday = cl.defaults.Holidays;
var arr = holiday[g.y][g.m - 1]; // 祝日の配列を取得。
if (arr.indexOf(i) != -1) { // 祝日配列に日付があるとき。in演算子はインデックスの有無の確認をするだけ。
node.style.color = cal._holidayC;
}
},
_getDayC: function (node, r) { // 曜日の色をつける。オブジェクトの参照渡しを利用。
node.setAttribute("data-remainder", r); // ノードに曜日番号を付ける。data-から始まるプロパティにしないとNode.cloneNode(true)で消えてしまう。
if (r == 0) { // 日曜日のとき
node.style.color = cal._holidayC;
} else if (r == 6) { // 土曜日のとき
node.style.color = cal._SatC;
}
}
}; // end of cal
var fd = {
_writeScript: function (url) { // スクリプト注入。
var ws = nd.createElem('script');
ws.type = 'text/javascript';
ws.src = url;
document.getElementsByTagName('head')[0].appendChild(ws);
},
createURL: function (max) { // フィードを取得するためのURLを作成。
var url = "/feeds/posts/summary?alt=json-in-script&orderby=" + g.order + "&" + g.order + "-min=" + g.y + "-" + fd.fm(g.m) + "-01T00:00:00%2B09:00&" + g.order + "-max=" + max; // 1日0時0分0秒からmaxの日時までの投稿フィードを取得。データは最新の投稿から返ってくる。
url += "&callback=Calendar5_Blogger.callback.getArticles&max-results=" + g.max; // コールバック関数と最大取得投稿数を設定。
fd._writeScript(url); // スクリプト注入でフィードを取得。。
},
fm: function (m) { // 数値を2桁の固定長にする。
return ("0" + m).slice(-2);
},
getFeed: function (dt) { // 日付オブジェクトからフィードを得てカレンダーを作成する。
g.init_d(dt); // 日付オブジェクトからカレンダーのデータを作成。
var max = g.y + "-" + fd.fm(g.m) + "-" + fd.fm(g.em) + "T23:59:59%2B09:00"; // 表示カレンダーの最終日23時59分59秒までのフィードを得るための日時を作成。
fd.createURL(max); // フィードを取得するためのURLを作成。
},
removeParam: function (thisUrl) {
return thisUrl.replace(/\?m=[01][&\?]/, "?").replace(/[&\?]m=[01]/, ""); // ウェブバージョンとモバイルサイトのパラメータを削除。
}
}; // end of fd
var nd = { // ノード関連。
createElem: function (tag) { // tagの要素を作成して返す関数。
return document.createElement(tag);
},
createTxt: function (txt) { // テキストノードを返す関数。
return document.createTextNode(txt);
},
}; // end of nd
var pt = { // その日の投稿リストを表示
dic: {}, // keyが日付、値がカレンダーのノードの辞書。
elem: null, // 投稿リストを表示させるノード。
_nodes: null, // 投稿リストのノードの不変部分
//_reF: /\w+\.html/, // htmlファイル名を抽出する正規表現パターン。
_reF: function (s) { // ※booskanium 19.5.18 ファイル名は月を跨ぐと重複する事がある : htmlファイル名を抽出する正規表現パターン。
return s.replace(/\//g, '\\/');
},
init: function () {
pt._nodes = pt._createNodes(); // 投稿リストのノードの不変部分の取得。
pt.elem = nd.createElem("div"); // 投稿リストの年月日を表示する要素の作成。
pt.elem.id = "list-title";
var thisUrl = fd.removeParam(document.URL); // URLからパラメータを除去する。
//pt._html = pt._reF.exec(thisUrl); // URLからhtmlファイル名を取得。
pt._html = pt._reF(thisUrl); // URLからhtmlファイル名を取得。
},
_createNodes: function () { // 投稿リストのノードの不変部分を作成しておく。
var p = nd.createElem("div");
p.className = "post-list";
p.appendChild(nd.createElem("div"));
p.childNodes[0].className = "post-img";
p.childNodes[0].appendChild(nd.createElem("a"));
//p.childNodes[0].childNodes[0].target = "_blank"; // ※fujiyan 23/4/29 リンクを同一タブで開くように変更。
p.childNodes[0].childNodes[0].appendChild(nd.createElem("img"));
p.childNodes[0].childNodes[0].childNodes[0].setAttribute("alt", "サムネイル");
p.childNodes[0].childNodes[0].childNodes[0].setAttribute("width", "72");
p.childNodes[0].childNodes[0].childNodes[0].setAttribute("height", "72"); // ※fujiyan 23/4/29 alt, height, width 属性追加。
p.appendChild(nd.createElem("div"));
p.childNodes[1].className = "post-title";
p.childNodes[1].appendChild(nd.createElem("a"));
//p.childNodes[1].childNodes[0].target = "_blank"; // ※fujiyan 23/4/29 リンクを同一タブで開くように変更。
return p;
},
_postNode: function (arr) { // 引数は[投稿のURL, 投稿タイトル, サムネイルのURL]の配列。
var p = pt._nodes.cloneNode(true);
if (arr[2]) { // サムネイルがあるとき
p.childNodes[0].childNodes[0].href = arr[0]; // 投稿のurlを取得。
p.childNodes[0].childNodes[0].childNodes[0].src = arr[2]; // サムネイル画像のurlを取得。
} else { // ※fujiyan 23/4/29 サムネイルがない時用の画像。
p.childNodes[0].childNodes[0].href = arr[0]; // 投稿のurlを取得。
p.childNodes[0].childNodes[0].childNodes[0].src ='https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgPxIVQEq6xXVGIx0xpJAEYXU2Y94Et6azwn2qk6GG2428pKu5zyJL1z7fW_Axl_yVGGXvOmjJjPRSKVChPVXkIQ5gyFZwsufKkWZzShRffUWJr-rcGNioCa33gq2LGokmCWihp2gNNzATk/s72-c/noimage.png';
}
p.childNodes[1].childNodes[0].href = arr[0]; // 投稿のurlを取得。
p.childNodes[1].childNodes[0].appendChild(nd.createTxt(arr[1])); // 投稿タイトルを取得。
return p;
},
createPostList: function (postNo) { // 投稿リストのタイトルを作成。2番目の引数はハイライトする投稿の要素番号。
var d = parseInt(eh.node.textContent, 10); // 日付を取得。
if (!st.f) { // 投稿リストのタイトルを設定。
pt.elem.textContent = g.order + ": " + st.enM[g.m - 1] + " " + d + " " + g.y;
} else {
var order = (g.order == "published") ? st.published + "記事" : st.updated + "記事";
pt.elem.textContent = g.y + "/" + g.m + "/" + d + " (" + st.days[eh.node.getAttribute("data-remainder")] + ") " + order;
}
g.dic[d].forEach(function (e, i) { // 選択している日付の投稿リストを作成。
pt.elem.appendChild(pt._postNode(e));
if (i == postNo) { // ハイライトする投稿のとき
var p = pt.elem.lastChild; // ハイライトする投稿のリストのノードを取得。
p.id = "current-post"; // アンカータグのリンクも無効にする。
}
});
},
getPostDate: function () { // アイテムページのURLからhtmlファイル名を比較して投稿日とハイライトする投稿番号を取得。
var days = Object.keys(g.dic); // 投稿のある日付の配列を取得。
for (i = 0; i < days.length; i++) { // forEachメソッドでは途中で抜けれないのでfor文を使う。
var d = days[i]; // 日付を取得。
var posts = g.dic[d]; // 各日付の投稿の配列を取得。
for (j = 0; j < posts.length; j++) { // 各投稿について
//if (pt._html[0] == pt._reF.exec(posts[j])[0]) { // 投稿のhtmlファイル名が一致するとき。フィードは.comで返ってきてTDLが異なるのでURL直接は比較できない。
if (pt._html == pt._reF(posts[j][0])) { // ※booskanium 19.5.18 投稿のhtmlファイル名が一致するとき。フィードは.comで返ってきてTDLが異なるのでURL直接は比較できない。
eh.node = pt.dic[d]; // カレンダーの日付のノードを取得。
pt.createPostList(j); // 投稿リストの作成。ハイライトする投稿の要素番号も渡す。
return; // for文を抜ける。
}
}
}
},
getHighlightPostNo: function () { // アイテムページのURLからhtmlファイル名を比較してハイライトする投稿番号を取得。
var arr = g.dic[eh.node.textContent]; // その日付の投稿リストを取得。
for (i = 0; i < arr.length; i++) {
//if (pt._html[0] == pt._reF.exec(arr[i])[0]) { // 投稿のhtmlファイル名が一致するとき。フィードは.comで返ってきてTDLが異なるのでURL直接は比較できない。
if (pt._html == pt._reF(arr[i][0])) { // ※booskanium 19.5.18 投稿のhtmlファイル名が一致するとき。フィードは.comで返ってきてTDLが異なるのでURL直接は比較できない。
pt.createPostList(i); // 投稿リストの作成。ハイライトする投稿の要素番号も渡す。
//pt.createPostList(null); // 投稿リストの作成。ハイライトする投稿の要素番号も渡す。
return;
}
}
pt.createPostList(null); // ハイライトする投稿番号がなかった時はnullを渡す。
}
}; // end of pt
var eh = { // イベントハンドラオブジェクト。
node: null, // 投稿一覧を表示している日付のノード。
_timer: null, // ノードのハイライトを消すタイマーID。
_rgbaC: null, // 背景色。styleオブジェクトで取得すると参照渡しになってしまう。
_fontC: null, // 文字色。
mouseDown: function (e) { // 要素をクリックしたときのイベントを受け取る関数。
var target = e.target; // イベントを発生したオブジェクト。
switch (target.className) {
case "posted": // 投稿がある日のとき
if (eh.node) { // 投稿一覧を表示させているノードがあるとき。
eh.node.style.backgroundColor = eh._rgbaC; // そのノードの背景色を元に戻す。
eh.node.style.textDecoration = null; // 文字の下線を消す。
}
eh.node = target; // 投稿を表示させるノードを取得。
if (g.mc) { // アイテムページの時
pt.getHighlightPostNo(); // ハイライト付きで投稿リストを展開する。
} else { // アイテムページ以外の時
pt.createPostList(null); // ハイライトする投稿番号はnullを渡す。
}
break;
case "nopost": // 投稿がない日のとき
pt.elem.textContent = null; // 表示を消す。
if (eh.node) { // 投稿一覧を表示させているノードがあるとき。
eh.node.style.textDecoration = null; // 文字の下線を消す。
eh.node.style.backgroundColor = eh._rgbaC; // そのノードの背景色を元に戻す。
eh.node = null; // 取得しているノードを消去。
target.style.pointerEvents = null; // クリックを有効にする。
}
break;
default:
var dt = new Date(g.y, g.m - 1, 1); // 表示しているカレンダーの1日の日付オブジェクトを取得。
switch (target.id) {
case "cal-title": // 公開日と更新日を切り替える。
target.style.pointerEvents = "none"; // 連続クリックできないようにする。
g.order = (g.order == "published") ? "updated" : "published";
fd.getFeed(dt);
break;
case "left-btn":
target.style.pointerEvents = "none"; // 連続クリックできないようにする。
dt.setMonth(dt.getMonth() + 1); // 翌月の日付オブジェクトを取得。
fd.getFeed(dt);
break;
case "right-btn":
target.style.pointerEvents = "none"; // 連続クリックできないようにする。
dt.setMonth(dt.getMonth() - 1); // 前月の日付オブジェクトを取得。
fd.getFeed(dt);
break;
}
}
}
}; // end of eh
return cl; // グローバルスコープにオブジェクトを出す。
}();
Calendar5_Blogger.defaults["StartYear"] = 2013; // 遡る最大年。
Calendar5_Blogger.defaults["StartMonth"] = 1; // 遡る最大月。
// 祝日一覧 (2013~2030年)
Calendar5_Blogger.defaults["Holidays"] = {
"2013":[[1,14],[11],[20],[29],[3,4,5,6],[],[15],[],[16,23],[14],[3,4,23],[23]],"2014":[[1,13],[11],[21],[29],[3,4,5,6],[],[21],[],[15,23],[13],[3,23,24],[23]],"2015":[[1,12],[11],[21],[29],[3,4,5,6],[],[20],[],[21,22,23],[12],[3,23],[23]],"2016":[[1,11],[11],[20,21],[29],[3,4,5],[],[18],[11],[19,22],[10],[3,23],[23]],"2017":[[1,2,9],[11],[20],[29],[3,4,5],[],[17],[11],[18,23],[9],[3,23],[23]],"2018":[[1,8],[11,12],[21],[29,30],[3,4,5],[],[16],[11],[17,23,24],[8],[3,23],[23,24]],"2019":[[1,14],[11],[21],[29],[3,4,5,6],[],[15],[11,12],[16,23],[14],[3,4,23],[23]],"2020":[[1,13],[11,23,24],[20],[29],[3,4,5,6],[],[23,24],[10],[21,22],[],[3,23],[]],"2021":[[1,11],[11,23],[20],[29],[3,4,5],[],[22,23],[8,9],[20,23],[],[3,23],[]],"2022":[[1,10],[11,23],[21],[29],[3,4,5],[],[18],[11],[19,23],[10],[3,23],[]],"2023":[[1,2,9],[11,23],[21],[29],[3,4,5],[],[17],[11],[18,23],[9],[3,23],[]],"2024":[[1,8],[11,12,23],[20],[29],[3,4,5,6],[],[15],[11,12],[16,22,23],[14],[3,4,23],[]],"2025":[[1,13],[11,23,24],[20],[29],[3,4,5,6],[],[21],[11],[15,23],[13],[3,23,24],[]],"2026":[[1,12],[11,23],[20],[29],[3,4,5,6],[],[20],[11],[21,22,23],[12],[3,23],[]],"2027":[[1,11],[11,23],[21,22],[29],[3,4,5],[],[19],[11],[20,23],[11],[3,23],[]],"2028":[[1,10],[11,23],[20],[29],[3,4,5],[],[17],[11],[18,22],[9],[3,23],[]],"2029":[[1,8],[11,12,23],[20],[29,30],[3,4,5],[],[16],[11],[17,23,24],[8],[3,23],[23,24]],"2030":[[1,14],[11,23],[20],[29],[3,4,5,6],[],[15],[11,12],[16,23],[14],[3,4,23],[]]
};
Calendar5_Blogger.all("calendar5_blogger"); // idがcalendar5_bloggerの要素にカレンダーを表示させる。
</script>

F-light 専用ダークモード対応コード

上記コードとの違いは、CSS でカスタムプロパティを使っている点だけです。
尚、F-light v1.28 以前をお使いの方は22行目のvar(--brand-font)#fffに変更してください。

<style>
/*カレンダー全体(記事リストを除く)*/
#calendar {
display: flex;
flex-wrap: wrap;
}
/*記事のある日付のハイライト*/
.posted {
text-decoration: none;
background: rgba(176, 224, 230, .5);
transition: .2s;
border-radius: 3px;
cursor: pointer;
}
.posted:hover {
background: rgba(176, 224, 230, .8);
}
/*ナビゲーションボタン全体*/
#cal-title,
.btn {
background: var(--brand);
color: var(--brand-font); /*v1.28 以前は #fff に変更*/
transition: .2s;
padding: 2px 0;
flex: 0 0 18%;
text-align: center;
}
/*年月表示部分(投稿・更新切り替えボタン)*/
#cal-title {
flex:1 0 64%;
text-align: center;
}
/*ナビゲーションボタン(最大年の右ボタン、最終年の左ボタンを除く)のホバー*/
#cal-title:hover,
#right-btn:hover,
#left-btn:hover {
opacity: .8;
cursor: pointer;
}
/*右ボタン・左ボタン*/
.btn:nth-of-type(3) {
border-radius: 0 3px 3px 0;
}
.btn:nth-of-type(1) {
border-radius: 3px 0 0 3px;
}
/*曜日・日付*/
.dow,
.posted,
.nopost {
flex: 1 0 calc(14% - 4px);
text-align: center;
margin: 2px;
}
/*記事一覧の枠*/
.post-list,
#current-post {
border-top: dashed 1px rgba(128, 128, 128, .6);
padding: 5px;
}
/*表示中の記事*/
#current-post {
background: var(--dark-bg);
pointer-events: none;
}
#current-post .post-title a {
color: var(--color);
}
/*記事リストのタイトル*/
#list-title {
display: flex;
flex-direction: column;
padding-top: 5px;
text-align: center;
}
/*サムネイル画像*/
.post-img {
float: left;
padding-right: 5px;
border-radius: 5px;
transition: .2s;
}
.post-img img {
border-radius: 5px;
}
.post-img:hover {
opacity: .8;
}
/*記事タイトル*/
.post-title {
line-height: 1.5;
text-align:left;
}
.post-title a {
text-decoration: none;
}
.post-list a:hover {
text-decoration: underline;
}
/*読込中アニメーション*/
.loader {
border: 4px solid transparent;
border-top-color: var(--dark-color);
border-right-color: var(--dark-color);
border-bottom-color: var(--dark-color);
border-radius: 50%;
width: 60px;
height: 60px;
animation: spin .7s linear infinite;
margin: 5em auto 2em;
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
</style>
<div id="calendar5_blogger"><div class="loader"></div></div>
<script>
// Calendar5_Bloggerモジュール
var Calendar5_Blogger = Calendar5_Blogger || function () {
var cl = {
defaults: {}, // デフォルト値を入れるオブジェクト。
callback: { // コールバック関数。
getArticles: function (json) { // 指定した月のフィードを受け取る。
Array.prototype.push.apply(g.posts, json.feed.entry); // 投稿のフィードデータを配列に追加。
if (json.feed.openSearch$totalResults.$t < g.max) { // 取得投稿数がg.maxより小さい時はすべて取得できたと考える。
var re = /\d\d(?=T\d\d:\d\d:\d\d\.\d\d\d.\d\d:\d\d)/i; // フィードの日時データから日を取得するための正規表現パターン。
var yyyy = /\d\d\d\d(?=\-\d\d\-\d\dT\d\d:\d\d:\d\d\.\d\d\d.\d\d:\d\d)/i; // ※ booskanium 19.05.18 日時データから年を取得するための正規表現パターン。
var mm = /\d\d(?=\-\d\dT\d\d:\d\d:\d\d\.\d\d\d.\d\d:\d\d)/i; // ※ booskanium 19.05.18 日時データから月を取得するための正規表現パターン。
g.posts.forEach(function (e) { // 投稿のフィードデータについて
var d = Number(re.exec(e[g.order].$t)); // 投稿の日を取得。
g.dic[d] = g.dic[d] || []; // 辞書の値の配列を初期化する。
var url = (e.media$thumbnail) ? e.media$thumbnail.url : null; // サムネイルのurl。
//g.dic[d].push([e.link[4].href, e.link[4].title, url]); // 辞書の値の配列に[投稿のURL, 投稿タイトル, サムネイルのURL]の配列を入れて2次元配列にする。
var y = yyyy.exec(e[g.order].$t); // 投稿の年を取得
var m = mm.exec(e[g.order].$t); // 投稿の月を取得
g.dic[d].push([e.link[4].href, e.link[4].title, url, y, m]); // ※booskanium 19.05.18 辞書の値の配列に[投稿のURL, 投稿タイトル, サムネイルのURL]の配列を入れて2次元配列にする。
});
var m = cal.createCalendar(); // フィードデータからカレンダーを作成する。
m.addEventListener('mousedown', eh.mouseDown, false); // カレンダーのflexコンテナでイベントバブリングを受け取る。マウスが要素をクリックしたとき。
m.addEventListener('mouseout', eh.mouseOut, false); // マウスポインタが要素から出た時。
g.elem.textContent = null; // 追加する対象の要素の子ノードを消去する。
g.elem.appendChild(m); // 追加する対象の要素の子ノードにカレンダーのflexコンテナを追加。
g.elem.appendChild(pt.elem); // 投稿リストを表示するノードを追加。
if (!eh.node && g.mc) {
pt.getPostDate()
}; // eh.nodeがnull(つまりページのロード時のみ)かつアイテムページの時のみアイテムページの投稿リストを展開する。
} else { // 未取得のフィードを再取得する。最新の投稿が先頭に来る。
var m = /(\d\d\d\d-\d\d-\d\dT\d\d:\d\d:\d\d)\.\d\d\d(.\d\d:\d\d)/i.exec(json.feed.entry[json.feed.entry.length - 1][g.order].$t); // フィードの最終投稿(最古)データの日時を取得。
var dt = new Date(m[1] + m[2]); // フィードの最終投稿(最古)データの日時の日付オブジェクトを取得。
dt.setSeconds(dt.getSeconds() - 1); // 最古の投稿の日時より1秒古い日時を取得。
if (g.m == dt.getMonth() + 1) { // 1秒古くても同じ月ならば
var max = g.y + "-" + fd.fm(g.m) + "-" + fd.fm(dt.getDate()) + "T" + fd.fm(dt.getHours()) + ":" + fd.fm(dt.getMinutes()) + ":" + fd.fm(dt.getSeconds()) + "%2B09:00"; // フィード取得のための最新日時を作成。
fd.createURL(max); // フィード取得のURLを作成。
}
}
},
},
all: function (elemID) { // ここから開始する。
g.elem = document.getElementById(elemID); // idから追加する対象の要素を取得。
if (g.elem) { // 追加対象の要素が存在するとき
st.init(); // 言語設定。
cal.init(); // カレンダーのノードの不変部分を作成しておく。
pt.init(); // 投稿リストのノードの不変部分を作成しておく。
var dt; // 日付オブジェクト。
g.mc = /\/(20\d\d)\/([01]\d)\//.exec(document.URL); // URLから年と月を正規表現で得る。g[1]が年、g.mc[2]が月。
if (g.mc) { // URLから年と月を取得できた時。つまりアイテムページの時。
var m = Number(g.mc[2]) - 1; // 月ひく1を取得
dt = new Date(g.mc[1], m, 1); // 投稿月の日付オブジェクトを取得。
} else { // アイテムページ以外の時は今日の日付を取得。
dt = new Date();
};
fd.getFeed(dt);
}
}
}; // end of cl
var g = { // モジュール内の"グローバル"変数。
max: 150, // Bloggerのフィードで取得できる最大投稿数を設定。
order: "published", // publishedかupdatedが入る。
elem: null, // 置換するdiv要素。
init_d: function (dt) { // 日付オブジェクトからカレンダーのデータを作り直す。
g.y = dt.getFullYear(); // 表示カレンダーの年を取得。
g.m = dt.getMonth() + 1; // 表示カレンダーの月を取得。
g.em = new Date(g.y, g.m, 0).getDate(); // 表示カレンダーの末日を取得。
g.posts = []; // フィードデータをリセットする。投稿のフィードデータを収納する配列。
g.dic = {}; // 投稿データをリセットする。キーを日、値を投稿のURLと投稿タイトルの配列の配列、とする辞書。
},
mc: false, // アイテムページの年[1]と月[2]の配列。
}; // end of g
var st = { // 言語置換
enM: ["Jan.", "Feb.", "Mar.", "Apr.", "May", "Jun.", "Jul.", "Aug.", "Sept.", "Oct.", "Nov.", "Dec."],
init: function () {
st.f = true; // Change value "false" for switch to English.
st.days = (st.f) ? ["日", "月", "火", "水", "木", "金", "土"] : ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]; // 曜日の配列。
st.left_btn = (st.f) ? "翌月へ" : "Newer";
st.updated = (st.f) ? " 更新" : "updated";
st.published = (st.f) ? " 投稿" : "published";
st.tooltip = (st.f) ? "投稿日と更新日を切り替える" : "Switching between published and updated";
st.right_btn = (st.f) ? "前月へ" : "Older";
}
}; // end of st
var cal = { // カレンダーを作成するオブジェクト。
_holidayC: "var(--red-color)", // 休日の文字色
_SatC: "var(--blue-color)", // 土曜日の文字色
_nodes: null,
init: function () { // カレンダーのノードの不変部分の取得。
cal._nodes = cal._createNodes();
},
_createNodes: function () { // カレンダーのノードの不変部分を作成しておく。
var m = nd.createElem("div");
m.id = "calendar";
var a = nd.createElem("div");
a.className = "btn";
m.appendChild(a);
var t = nd.createElem("div");
m.appendChild(t);
m.appendChild(a.cloneNode(true));
var d = nd.createElem("div");
d.className = "dow";
st.days.forEach(function (e, i) { // 1行目に曜日を表示させる。2番目の引数は配列のインデックス。
var node = d.cloneNode(true);
node.appendChild(nd.createTxt(st.days[i]));
cal._getDayC(node, i); // 曜日の色をつける。
if (!st.f) {
node.style.fontSize = "80%"; // 英語表記では1行に収まらないのでフォントサイズを縮小。
}
m.appendChild(node); // カレンダーのflexコンテナに追加。
});
d.className = "nopost";
for (i = 0; i < 42; i++) { // カレンダーの5行目まで作成。
m.appendChild(cal._createDateNodes(d)); // カレンダーのflexコンテナに追加。
}
d.style.display = "none"; // カレンダーの6行目はデフォルトでは表示させない。
for (i = 0; i < 7; i++) {
m.appendChild(cal._createDateNodes(d)); // カレンダーのflexコンテナに追加。
}
return m;
},
_createDateNodes: function (d) {
var node = d.cloneNode(true);
cal._getDayC(node, i % 7); // 曜日の色をつける。
return node;
},
createCalendar: function () { // カレンダーのHTML要素を作成。
var m = cal._nodes.cloneNode(true);
var dt = new Date(); // 今日の日付オブジェクトを取得。
var now = new Date(dt.getFullYear(), dt.getMonth(), 1).getTime(); // 今月の1日のミリ秒を取得。
var caldt = new Date(g.y, g.m - 1, 1);
var caldate = caldt.getTime(); // カレンダーの1日のミリ秒を取得。
if (now > caldate) { // 表示カレンダーの月が現在より過去のときのみ左矢印を表示させる。
m.childNodes[0].appendChild(nd.createTxt('\u226a'));
m.childNodes[0].title = st.left_btn;
m.childNodes[0].id = "left-btn";
}
var titleText = (st.f) ? g.y + "年 " + g.m + "月" : st.enM[g.m - 1] + " " + g.y + " ";
titleText += (g.order == "published") ? "" : st.updated;
m.childNodes[1].appendChild(nd.createTxt(titleText));
m.childNodes[1].title = st.tooltip;
m.childNodes[1].id = "cal-title";
dt = new Date(cl.defaults.StartYear, cl.defaults.StartMonth - 1, 1); // 最初の投稿月の日付オブジェクトを取得。
var firstpost = new Date(dt.getFullYear(), dt.getMonth(), 1).getTime(); // 1日のミリ秒を取得。
if (firstpost < caldate) { // 表示カレンダーの月が初投稿月より未来のときのみ右矢印を表示させる。
m.childNodes[2].appendChild(nd.createTxt('\u226b'));
m.childNodes[2].title = st.right_btn;
m.childNodes[2].id = "right-btn";
}
var day = caldt.getDay(); // 1日の曜日を取得。日曜日は0、土曜日は6になる。
var c = 9 + day; // 1日の要素番号−1。
pt.dic = {}; // 日付、とカレンダーノードの辞書をリセットする。
for (var i = 1; i < 1 + g.em; i++) { // 1日から末日まで。
var d = m.childNodes[c + i];
d.appendChild(nd.createTxt(i));
var t = ""; // nullはundefinedと表示されるのでだめ。
if (i in g.dic) { // 辞書のキーに日があるとき
pt.dic[i] = d; // アイテムページで投稿リストを展開するための辞書。keyが日付、値はカレンダーのノード。
g.dic[i].forEach(function (arr) { // title属性に投稿タイトルのみ入れる。
t += (t) ? "\n" + "\u30fb" + arr[1] : "\u30fb" + arr[1];
});
d.title = t;
d.className = "posted";
}
cal._getHolidayC(d, i); // 祝日に色をつける。
}
if (day + g.em > 35) { // 最終行の表示。
for (var i = 45; i < 52; i++) {
m.childNodes[i].style.display = null;
}
}
return m;
},
_getHolidayC: function (node, i) { // 祝日に色をつける。JSON文字列はhttps://p--q.blogspot.jp/2016/12/blogger10json.htmlを作成。
// キーは年、値は二元元配列。1次が月数、二次が祝日の配列。
var holiday = cl.defaults.Holidays;
var arr = holiday[g.y][g.m - 1]; // 祝日の配列を取得。
if (arr.indexOf(i) != -1) { // 祝日配列に日付があるとき。in演算子はインデックスの有無の確認をするだけ。
node.style.color = cal._holidayC;
}
},
_getDayC: function (node, r) { // 曜日の色をつける。オブジェクトの参照渡しを利用。
node.setAttribute("data-remainder", r); // ノードに曜日番号を付ける。data-から始まるプロパティにしないとNode.cloneNode(true)で消えてしまう。
if (r == 0) { // 日曜日のとき
node.style.color = cal._holidayC;
} else if (r == 6) { // 土曜日のとき
node.style.color = cal._SatC;
}
}
}; // end of cal
var fd = {
_writeScript: function (url) { // スクリプト注入。
var ws = nd.createElem('script');
ws.type = 'text/javascript';
ws.src = url;
document.getElementsByTagName('head')[0].appendChild(ws);
},
createURL: function (max) { // フィードを取得するためのURLを作成。
var url = "/feeds/posts/summary?alt=json-in-script&orderby=" + g.order + "&" + g.order + "-min=" + g.y + "-" + fd.fm(g.m) + "-01T00:00:00%2B09:00&" + g.order + "-max=" + max; // 1日0時0分0秒からmaxの日時までの投稿フィードを取得。データは最新の投稿から返ってくる。
url += "&callback=Calendar5_Blogger.callback.getArticles&max-results=" + g.max; // コールバック関数と最大取得投稿数を設定。
fd._writeScript(url); // スクリプト注入でフィードを取得。。
},
fm: function (m) { // 数値を2桁の固定長にする。
return ("0" + m).slice(-2);
},
getFeed: function (dt) { // 日付オブジェクトからフィードを得てカレンダーを作成する。
g.init_d(dt); // 日付オブジェクトからカレンダーのデータを作成。
var max = g.y + "-" + fd.fm(g.m) + "-" + fd.fm(g.em) + "T23:59:59%2B09:00"; // 表示カレンダーの最終日23時59分59秒までのフィードを得るための日時を作成。
fd.createURL(max); // フィードを取得するためのURLを作成。
},
removeParam: function (thisUrl) {
return thisUrl.replace(/\?m=[01][&\?]/, "?").replace(/[&\?]m=[01]/, ""); // ウェブバージョンとモバイルサイトのパラメータを削除。
}
}; // end of fd
var nd = { // ノード関連。
createElem: function (tag) { // tagの要素を作成して返す関数。
return document.createElement(tag);
},
createTxt: function (txt) { // テキストノードを返す関数。
return document.createTextNode(txt);
},
}; // end of nd
var pt = { // その日の投稿リストを表示
dic: {}, // keyが日付、値がカレンダーのノードの辞書。
elem: null, // 投稿リストを表示させるノード。
_nodes: null, // 投稿リストのノードの不変部分
//_reF: /\w+\.html/, // htmlファイル名を抽出する正規表現パターン。
_reF: function (s) { // ※booskanium 19.5.18 ファイル名は月を跨ぐと重複する事がある : htmlファイル名を抽出する正規表現パターン。
return s.replace(/\//g, '\\/');
},
init: function () {
pt._nodes = pt._createNodes(); // 投稿リストのノードの不変部分の取得。
pt.elem = nd.createElem("div"); // 投稿リストの年月日を表示する要素の作成。
pt.elem.id = "list-title";
var thisUrl = fd.removeParam(document.URL); // URLからパラメータを除去する。
//pt._html = pt._reF.exec(thisUrl); // URLからhtmlファイル名を取得。
pt._html = pt._reF(thisUrl); // URLからhtmlファイル名を取得。
},
_createNodes: function () { // 投稿リストのノードの不変部分を作成しておく。
var p = nd.createElem("div");
p.className = "post-list";
p.appendChild(nd.createElem("div"));
p.childNodes[0].className = "post-img";
p.childNodes[0].appendChild(nd.createElem("a"));
//p.childNodes[0].childNodes[0].target = "_blank"; // ※fujiyan 23/4/29 リンクを同一タブで開くように変更。
p.childNodes[0].childNodes[0].appendChild(nd.createElem("img"));
p.childNodes[0].childNodes[0].childNodes[0].setAttribute("alt", "サムネイル");
p.childNodes[0].childNodes[0].childNodes[0].setAttribute("width", "72");
p.childNodes[0].childNodes[0].childNodes[0].setAttribute("height", "72"); // ※fujiyan 23/4/29 alt, height, width 属性追加。
p.appendChild(nd.createElem("div"));
p.childNodes[1].className = "post-title";
p.childNodes[1].appendChild(nd.createElem("a"));
//p.childNodes[1].childNodes[0].target = "_blank"; // ※fujiyan 23/4/29 リンクを同一タブで開くように変更。
return p;
},
_postNode: function (arr) { // 引数は[投稿のURL, 投稿タイトル, サムネイルのURL]の配列。
var p = pt._nodes.cloneNode(true);
if (arr[2]) { // サムネイルがあるとき
p.childNodes[0].childNodes[0].href = arr[0]; // 投稿のurlを取得。
p.childNodes[0].childNodes[0].childNodes[0].src = arr[2]; // サムネイル画像のurlを取得。
} else { // ※fujiyan 23/4/29 サムネイルがない時用の画像。
p.childNodes[0].childNodes[0].href = arr[0]; // 投稿のurlを取得。
p.childNodes[0].childNodes[0].childNodes[0].src ='https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgPxIVQEq6xXVGIx0xpJAEYXU2Y94Et6azwn2qk6GG2428pKu5zyJL1z7fW_Axl_yVGGXvOmjJjPRSKVChPVXkIQ5gyFZwsufKkWZzShRffUWJr-rcGNioCa33gq2LGokmCWihp2gNNzATk/s72-c/noimage.png';
}
p.childNodes[1].childNodes[0].href = arr[0]; // 投稿のurlを取得。
p.childNodes[1].childNodes[0].appendChild(nd.createTxt(arr[1])); // 投稿タイトルを取得。
return p;
},
createPostList: function (postNo) { // 投稿リストのタイトルを作成。2番目の引数はハイライトする投稿の要素番号。
var d = parseInt(eh.node.textContent, 10); // 日付を取得。
if (!st.f) { // 投稿リストのタイトルを設定。
pt.elem.textContent = g.order + ": " + st.enM[g.m - 1] + " " + d + " " + g.y;
} else {
var order = (g.order == "published") ? st.published + "記事" : st.updated + "記事";
pt.elem.textContent = g.y + "/" + g.m + "/" + d + " (" + st.days[eh.node.getAttribute("data-remainder")] + ") " + order;
}
g.dic[d].forEach(function (e, i) { // 選択している日付の投稿リストを作成。
pt.elem.appendChild(pt._postNode(e));
if (i == postNo) { // ハイライトする投稿のとき
var p = pt.elem.lastChild; // ハイライトする投稿のリストのノードを取得。
p.id = "current-post"; // アンカータグのリンクも無効にする。
}
});
},
getPostDate: function () { // アイテムページのURLからhtmlファイル名を比較して投稿日とハイライトする投稿番号を取得。
var days = Object.keys(g.dic); // 投稿のある日付の配列を取得。
for (i = 0; i < days.length; i++) { // forEachメソッドでは途中で抜けれないのでfor文を使う。
var d = days[i]; // 日付を取得。
var posts = g.dic[d]; // 各日付の投稿の配列を取得。
for (j = 0; j < posts.length; j++) { // 各投稿について
//if (pt._html[0] == pt._reF.exec(posts[j])[0]) { // 投稿のhtmlファイル名が一致するとき。フィードは.comで返ってきてTDLが異なるのでURL直接は比較できない。
if (pt._html == pt._reF(posts[j][0])) { // ※booskanium 19.5.18 投稿のhtmlファイル名が一致するとき。フィードは.comで返ってきてTDLが異なるのでURL直接は比較できない。
eh.node = pt.dic[d]; // カレンダーの日付のノードを取得。
pt.createPostList(j); // 投稿リストの作成。ハイライトする投稿の要素番号も渡す。
return; // for文を抜ける。
}
}
}
},
getHighlightPostNo: function () { // アイテムページのURLからhtmlファイル名を比較してハイライトする投稿番号を取得。
var arr = g.dic[eh.node.textContent]; // その日付の投稿リストを取得。
for (i = 0; i < arr.length; i++) {
//if (pt._html[0] == pt._reF.exec(arr[i])[0]) { // 投稿のhtmlファイル名が一致するとき。フィードは.comで返ってきてTDLが異なるのでURL直接は比較できない。
if (pt._html == pt._reF(arr[i][0])) { // ※booskanium 19.5.18 投稿のhtmlファイル名が一致するとき。フィードは.comで返ってきてTDLが異なるのでURL直接は比較できない。
pt.createPostList(i); // 投稿リストの作成。ハイライトする投稿の要素番号も渡す。
//pt.createPostList(null); // 投稿リストの作成。ハイライトする投稿の要素番号も渡す。
return;
}
}
pt.createPostList(null); // ハイライトする投稿番号がなかった時はnullを渡す。
}
}; // end of pt
var eh = { // イベントハンドラオブジェクト。
node: null, // 投稿一覧を表示している日付のノード。
_timer: null, // ノードのハイライトを消すタイマーID。
_rgbaC: null, // 背景色。styleオブジェクトで取得すると参照渡しになってしまう。
_fontC: null, // 文字色。
mouseDown: function (e) { // 要素をクリックしたときのイベントを受け取る関数。
var target = e.target; // イベントを発生したオブジェクト。
switch (target.className) {
case "posted": // 投稿がある日のとき
if (eh.node) { // 投稿一覧を表示させているノードがあるとき。
eh.node.style.backgroundColor = eh._rgbaC; // そのノードの背景色を元に戻す。
eh.node.style.textDecoration = null; // 文字の下線を消す。
}
eh.node = target; // 投稿を表示させるノードを取得。
if (g.mc) { // アイテムページの時
pt.getHighlightPostNo(); // ハイライト付きで投稿リストを展開する。
} else { // アイテムページ以外の時
pt.createPostList(null); // ハイライトする投稿番号はnullを渡す。
}
break;
case "nopost": // 投稿がない日のとき
pt.elem.textContent = null; // 表示を消す。
if (eh.node) { // 投稿一覧を表示させているノードがあるとき。
eh.node.style.textDecoration = null; // 文字の下線を消す。
eh.node.style.backgroundColor = eh._rgbaC; // そのノードの背景色を元に戻す。
eh.node = null; // 取得しているノードを消去。
target.style.pointerEvents = null; // クリックを有効にする。
}
break;
default:
var dt = new Date(g.y, g.m - 1, 1); // 表示しているカレンダーの1日の日付オブジェクトを取得。
switch (target.id) {
case "cal-title": // 公開日と更新日を切り替える。
target.style.pointerEvents = "none"; // 連続クリックできないようにする。
g.order = (g.order == "published") ? "updated" : "published";
fd.getFeed(dt);
break;
case "left-btn":
target.style.pointerEvents = "none"; // 連続クリックできないようにする。
dt.setMonth(dt.getMonth() + 1); // 翌月の日付オブジェクトを取得。
fd.getFeed(dt);
break;
case "right-btn":
target.style.pointerEvents = "none"; // 連続クリックできないようにする。
dt.setMonth(dt.getMonth() - 1); // 前月の日付オブジェクトを取得。
fd.getFeed(dt);
break;
}
}
}
}; // end of eh
return cl; // グローバルスコープにオブジェクトを出す。
}();
Calendar5_Blogger.defaults["StartYear"] = 2013; // 遡る最大年。
Calendar5_Blogger.defaults["StartMonth"] = 1; // 遡る最大月。
// 祝日一覧
Calendar5_Blogger.defaults["Holidays"] = {
"2013":[[1,14],[11],[20],[29],[3,4,5,6],[],[15],[],[16,23],[14],[3,4,23],[23]],"2014":[[1,13],[11],[21],[29],[3,4,5,6],[],[21],[],[15,23],[13],[3,23,24],[23]],"2015":[[1,12],[11],[21],[29],[3,4,5,6],[],[20],[],[21,22,23],[12],[3,23],[23]],"2016":[[1,11],[11],[20,21],[29],[3,4,5],[],[18],[11],[19,22],[10],[3,23],[23]],"2017":[[1,2,9],[11],[20],[29],[3,4,5],[],[17],[11],[18,23],[9],[3,23],[23]],"2018":[[1,8],[11,12],[21],[29,30],[3,4,5],[],[16],[11],[17,23,24],[8],[3,23],[23,24]],"2019":[[1,14],[11],[21],[29],[3,4,5,6],[],[15],[11,12],[16,23],[14],[3,4,23],[23]],"2020":[[1,13],[11,23,24],[20],[29],[3,4,5,6],[],[23,24],[10],[21,22],[],[3,23],[]],"2021":[[1,11],[11,23],[20],[29],[3,4,5],[],[22,23],[8,9],[20,23],[],[3,23],[]],"2022":[[1,10],[11,23],[21],[29],[3,4,5],[],[18],[11],[19,23],[10],[3,23],[]],"2023":[[1,2,9],[11,23],[21],[29],[3,4,5],[],[17],[11],[18,23],[9],[3,23],[]],"2024":[[1,8],[11,12,23],[20],[29],[3,4,5,6],[],[15],[11,12],[16,22,23],[14],[3,4,23],[]],"2025":[[1,13],[11,23,24],[20],[29],[3,4,5,6],[],[21],[11],[15,23],[13],[3,23,24],[]],"2026":[[1,12],[11,23],[20],[29],[3,4,5,6],[],[20],[11],[21,22,23],[12],[3,23],[]],"2027":[[1,11],[11,23],[21,22],[29],[3,4,5],[],[19],[11],[20,23],[11],[3,23],[]],"2028":[[1,10],[11,23],[20],[29],[3,4,5],[],[17],[11],[18,22],[9],[3,23],[]],"2029":[[1,8],[11,12,23],[20],[29,30],[3,4,5],[],[16],[11],[17,23,24],[8],[3,23],[23,24]],"2030":[[1,14],[11,23],[20],[29],[3,4,5,6],[],[15],[11,12],[16,23],[14],[3,4,23],[]]
};
Calendar5_Blogger.all("calendar5_blogger"); // idがcalendar5_bloggerの要素にカレンダーを表示させる。
</script>

コードのカスタマイズ

  • 殆どの CSS は <style>~</style>の所で変更出来ますが、休日と土曜日の文字色に関しては JavaScript 内(210~211行目)の以下の箇所の色コード(色名)を変更してください。
    _holidayC: "色コード", // 休日の文字色
    _SatC: "色コード", // 土曜日の文字色
  • カレンダーは2013年1月~2030年12月まで表示可能ですが、現在の年月以降には遷移できない仕様になっています。遷移可能にしたい場合は257行目のif (now > caldate) {と261行目の閉じ括弧}をコメントアウト処理します。
    if (now > caldate) { // 表示カレンダーの月が現在より過去のときのみ左矢印を表示させる。
    m.childNodes[0].appendChild(nd.createTxt('\u226a'));
    m.childNodes[0].title = st.left_btn;
    m.childNodes[0].id = "left-btn";
    }
    ↓↓変更
    //if (now > caldate) { // 表示カレンダーの月が現在より過去のときのみ左矢印を表示させる。
    m.childNodes[0].appendChild(nd.createTxt('\u226a'));
    m.childNodes[0].title = st.left_btn;
    m.childNodes[0].id = "left-btn";
    //}
  • 遡れる最大年月は490行目の以下の所で変更できます。
    Calendar5_Blogger.defaults["StartYear"] = 2013; // 遡る最大年。
    Calendar5_Blogger.defaults["StartMonth"] = 1; // 遡る最大月。
  • 英語表記にしたい方は、200行目の以下の所の値を true → false に変えてください。
    st.f = true; // Change value "false" for switch to English.

カレンダーの使い方や注意点

  • ナビゲーションの「年月」をクリックする毎に「投稿日」と「更新日」が切り替わります。
  • ハイライトされている日付をクリックすると、カレンダーの下にその日に投稿(更新)された記事一覧が表示されます。(最大150件まで?)
    マウスホバーでツールチップにも記事リストが出ますが、Chrome の場合は多すぎると何も出ないようです。
    何もない日付をクリックすると記事一覧が消えます。
  • サイドバーに置いた場合、閲覧中の記事が記事リストに含まれていると、その記事がハイライト表示されます。
  • QooQ に置くとこんな感じになります。
    QooQ に置いたカレンダーウィジェット
  • 月別アーカイブページでは当該年月に連動したカレンダーが表示されます。
  • 当然ですが、フィードが非公開のブログでは使えません。ブログによってはフィードが公開状態でも動かない場合もあるようです。
  • 2012年以前から記事を書かれてる場合は、最大年月を変更し、こちらのツールで祝日 JSON を出力して追加してください。

最後に

特にサイドバーウィジェットとしての使用に関してはほとんど検証しておらず、未知の不具合が潜んでいる可能性もあり得ますのでご了承ください。もしバグを発見された場合、たぶん私にはどうにも出来ないと思いますので、作者の p--q さんか、booskanium さんのようなスキルのある方にダメ元で改善をお願いしてみてください(^^;