前回はConcreteCMS9.4.8でカレンダーのイベントの時間選択を5分から30分に変更する方法を記載しました。
が今回はイベントの開始時間を設定した時にデフォルトでは終了時間が1時間後になるところを3時間になるようにしたいと思います。作成するカレンダーによっては数時間単位で動いてくれたほうがイベント入力者にとっては手間が省ける場合もあると思います。

設定方法は「管理画面」→{サイトマップ}→「オプション」→「サイトマップにシステムページを含める」を選択すると管理画面以下のサイトが表示されます。
「カレンダー&イベント」→「カレンダー閲覧」→「SEO」→「追加ヘッダー要素」に下記のコードを記述して「変更を保存」して下さい。(このページのみのカスタマイズで良いのでこの方法です。)
<script>
document.addEventListener('DOMContentLoaded', function () {
// Detects whether a time string is 12h or 24h format
function detectFormat(value) {
if (/am|pm/i.test(value)) return '12h';
if (/^\d{1,2}:\d{2}$/.test(value)) return '24h';
return null;
}
// Parse 12h time → minutes
function parse12h(value) {
var match = value.match(/^(\d{1,2}):(\d{2})(am|pm)$/i);
if (!match) return null;
var hours = parseInt(match[1], 10);
var minutes = parseInt(match[2], 10);
var period = match[3].toLowerCase();
if (period === 'am' && hours === 12) hours = 0;
if (period === 'pm' && hours !== 12) hours += 12;
return hours * 60 + minutes;
}
// Parse 24h time → minutes
function parse24h(value) {
var match = value.match(/^(\d{1,2}):(\d{2})$/);
if (!match) return null;
var hours = parseInt(match[1], 10);
var minutes = parseInt(match[2], 10);
return hours * 60 + minutes;
}
// Convert minutes → 12h time
function to12h(totalMinutes) {
var hours24 = Math.floor(totalMinutes / 60) % 24;
var minutes = totalMinutes % 60;
var period = hours24 < 12 ? 'am' : 'pm';
var hours12 = hours24 % 12 || 12;
return hours12 + ':' + String(minutes).padStart(2, '0') + period;
}
// Convert minutes → 24h time
function to24h(totalMinutes) {
var hours = Math.floor(totalMinutes / 60) % 24;
var minutes = totalMinutes % 60;
return String(hours).padStart(2, '0') + ':' + String(minutes).padStart(2, '0');
}
// Smart add-hours that preserves original format
function addHoursSmart(value, hoursToAdd) {
var format = detectFormat(value);
if (!format) return null;
var minutes = format === '12h' ? parse12h(value) : parse24h(value);
if (minutes === null) return null;
minutes += hoursToAdd * 60;
return format === '12h' ? to12h(minutes) : to24h(minutes);
}
// Snap to nearest valid option in the END dropdown
function snapToClosestOption(ts, targetValue) {
var options = Object.keys(ts.options);
if (!options.length) return targetValue;
// If exact match exists, use it
if (ts.options[targetValue]) return targetValue;
// Convert all options + target to minutes for comparison
function toMinutes(v) {
return detectFormat(v) === '12h' ? parse12h(v) : parse24h(v);
}
var targetMin = toMinutes(targetValue);
var closest = options[0];
var closestDiff = Math.abs(toMinutes(closest) - targetMin);
options.forEach(function(opt) {
var diff = Math.abs(toMinutes(opt) - targetMin);
if (diff < closestDiff) {
closest = opt;
closestDiff = diff;
}
});
return closest;
}
function attachTimeLogic() {
var selects = document.querySelectorAll('select');
var startTS = null;
var endTS = null;
selects.forEach(function(select) {
if (!select.tomselect) return;
var name = (select.name || '').toLowerCase();
if (name.includes('start')) startTS = select.tomselect;
if (name.includes('end')) endTS = select.tomselect;
});
if (!startTS || !endTS) return;
if (startTS._smartAttached) return;
startTS._smartAttached = true;
var initialized = false;
function setEnd(startValue) {
var rawEnd = addHoursSmart(startValue, 3);
if (!rawEnd) return;
var snapped = snapToClosestOption(endTS, rawEnd);
endTS.setValue(snapped);
}
// Initial auto-fill
setTimeout(function () {
var startValue = startTS.getValue();
if (startValue) {
setEnd(startValue);
initialized = true;
}
}, 100);
// Update on change
startTS.on('change', function (value) {
if (!initialized) return;
setEnd(value);
});
// Keep TomSelect behaviour consistent
if (endTS.control) {
endTS.control.addEventListener('mousedown', function () {
var value = endTS.getValue();
if (!value) return;
var option = endTS.getOption(value);
if (option) {
endTS.setActiveOption(option);
}
});
}
}
var observer = new MutationObserver(function () {
attachTimeLogic();
});
observer.observe(document.documentElement, {
childList: true,
subtree: true
});
});
</script>
これでカレンダーのイベント開始時間を設定した時に終了時間が3時間後になるようにできました。
