コンテンツにスキップ

「MediaWiki:Common.js」の版間の差分

提供: RTA Wiki
編集の要約なし
編集の要約なし
 
(同じ利用者による、間の16版が非表示)
1行目: 1行目:
/* RTA Wiki: PCの匿名利用者には各ページでメインメニューを左側に表示 */
(function () {
if (
mw.config.get( 'wgUserName' ) ||
!window.matchMedia( '(min-width: 1000px)' ).matches
) {
return;
}
function pinMainMenu() {
var pinButton = document.querySelector(
'[data-event-name="pinnable-header.vector-main-menu.pin"]'
);
if ( pinButton ) {
pinButton.click();
}
}
if ( document.documentElement.classList.contains( 'vector-animations-ready' ) ) {
pinMainMenu();
return;
}
var observer = new MutationObserver( function () {
if ( document.documentElement.classList.contains( 'vector-animations-ready' ) ) {
pinMainMenu();
observer.disconnect();
}
} );
observer.observe( document.documentElement, {
attributes: true,
attributeFilter: [ 'class' ]
} );
}());
/* RTA Wiki: 管理者以外には既存ページの編集導線を表示しない */
/* RTA Wiki: 管理者以外には既存ページの編集導線を表示しない */
mw.loader.using( 'mediawiki.util' ).then( function () {
mw.loader.using( 'mediawiki.util' ).then( function () {
20行目: 55行目:
} );
} );


/* RTA Wiki: 記録時間から記録秒を自動計算する */
/* RTA Wiki: 時・分・秒・ミリ秒の入力から記録時間・記録秒を自動設定する */
mw.loader.using( 'mediawiki.util' ).then( function () {
mw.loader.using( 'mediawiki.util' ).then( function () {
function setUpRecordSeconds() {
function setUpRecordTimeInputs() {
var timeInput = document.querySelector(
var timeInput = document.querySelector(
'input[name="RTA記録[記録時間]"], input[name$="[記録時間]"]'
'input[name="RTA記録[記録時間]"], input[name$="[記録時間]"]'
);
);
var secondsInput = document.querySelector(
var storedSecondsInput = document.querySelector(
'input[name="RTA記録[記録秒]"], input[name$="[記録秒]"]'
'input[name="RTA記録[記録秒]"], input[name$="[記録秒]"]'
);
);
var controls = document.getElementById( 'rta-time-inputs' );


if ( !timeInput || !secondsInput || secondsInput.dataset.rtaAutoSeconds === '1' ) {
if (
!timeInput || !storedSecondsInput || !controls ||
controls.dataset.rtaTimeSetUp === '1'
) {
return;
return;
}
}


secondsInput.dataset.rtaAutoSeconds = '1';
controls.dataset.rtaTimeSetUp = '1';
secondsInput.readOnly = true;
controls.style.display = 'flex';
secondsInput.placeholder = '記録時間から自動計算されます';
controls.style.gap = '10px';
controls.style.alignItems = 'center';
controls.style.flexWrap = 'wrap';
 
function makeInput( id, labelText, maxLength, inputMode ) {
var label = document.createElement( 'label' );
var input = document.createElement( 'input' );
 
input.id = id;
input.type = 'text';
input.inputMode = inputMode;
input.size = 3;
input.maxLength = maxLength;
input.value = '0';
input.autocomplete = 'off';
input.style.width = '4.5em';
input.style.boxSizing = 'border-box';
 
label.style.display = 'inline-flex';
label.style.alignItems = 'center';
label.style.gap = '4px';
 
label.appendChild( input );
label.appendChild( document.createTextNode( labelText ) );
controls.appendChild( label );
 
return input;
}
 
var hoursInput = makeInput( 'rta-record-hours', '時', 6, 'numeric' );
var minutesInput = makeInput( 'rta-record-minutes', '分', 2, 'numeric' );
var secondsInput = makeInput( 'rta-record-seconds', '秒', 6, 'decimal' );


var message = document.createElement( 'small' );
var message = document.createElement( 'small' );
42行目: 112行目:
message.style.marginTop = '4px';
message.style.marginTop = '4px';
message.style.color = '#54595d';
message.style.color = '#54595d';
secondsInput.insertAdjacentElement( 'afterend', message );
controls.insertAdjacentElement( 'afterend', message );
 
function normalizeDigits( value ) {
return value
.replace( /[0-9]/g, function ( character ) {
return String.fromCharCode( character.charCodeAt( 0 ) - 0xFEE0 );
} )
.replace( /[.。]/g, '.' )
.trim();
}
 
function showMessage( text, isError ) {
message.textContent = text;
message.style.color = isError ? '#d33' : '#54595d';
}
 
function setVisibleValuesFromStoredTime() {
var value = normalizeDigits( timeInput.value );
var match = value.match( /^(\d+):([0-5]\d):([0-5]\d(?:\.\d{1,3})?)$/ );
 
if ( !match ) {
return;
}
 
hoursInput.value = String( Number( match[1] ) );
minutesInput.value = String( Number( match[2] ) );
secondsInput.value = match[3];
}
 
function updateRecordValues() {
var hoursText = normalizeDigits( hoursInput.value );
var minutesText = normalizeDigits( minutesInput.value );
var secondsText = normalizeDigits( secondsInput.value );
 
hoursInput.value = hoursText;
minutesInput.value = minutesText;
secondsInput.value = secondsText;
 
if (
!/^\d+$/.test( hoursText ) ||
!/^\d+$/.test( minutesText ) ||
!/^\d+(?:\.\d{1,3})?$/.test( secondsText )
) {
timeInput.value = '';
storedSecondsInput.value = '';
showMessage( '時・分・秒を正しく入力してください。秒は小数点以下3桁までです。', true );
return false;
}
 
var hours = Number( hoursText );
var minutes = Number( minutesText );
var secondParts = secondsText.split( '.' );
var wholeSeconds = Number( secondParts[0] );
var milliseconds = Number( ( secondParts[1] || '' ).padEnd( 3, '0' ) );


function updateSeconds() {
if (
var value = timeInput.value.trim();
!Number.isSafeInteger( hours ) ||
var match = value.match( /^(\d+):([0-5]\d):([0-5]\d)$/ );
minutes > 59 ||
var hours, minutes, seconds;
wholeSeconds > 59
) {
timeInput.value = '';
storedSecondsInput.value = '';
showMessage( '分・秒は 0〜59 の範囲で入力してください。', true );
return false;
}


if ( match ) {
var totalMilliseconds =
hours = Number( match[1] );
( ( hours * 60 + minutes ) * 60 + wholeSeconds ) * 1000 +
minutes = Number( match[2] );
milliseconds;
seconds = Number( match[3] );
} else {
match = value.match( /^([0-5]?\d):([0-5]\d)$/ );


if ( match ) {
if ( !Number.isSafeInteger( totalMilliseconds ) || totalMilliseconds === 0 ) {
hours = 0;
timeInput.value = '';
minutes = Number( match[1] );
storedSecondsInput.value = '';
seconds = Number( match[2] );
showMessage( '0秒より大きい記録時間を入力してください。', true );
} else {
return false;
secondsInput.value = '';
}
message.textContent = value ?
 
'「時:分:秒」または「分:秒」の形式で入力してください。' :
var formattedSeconds = String( wholeSeconds ).padStart( 2, '0' );
'記録時間を入力すると自動計算されます。';
if ( milliseconds > 0 ) {
return;
formattedSeconds += '.' + String( milliseconds ).padStart( 3, '0' );
}
}
}


secondsInput.value = String( hours * 3600 + minutes * 60 + seconds );
timeInput.value =
message.textContent = '自動計算: ' + secondsInput.value + ' 秒';
String( hours ) + ':' +
String( minutes ).padStart( 2, '0' ) + ':' +
formattedSeconds;
 
storedSecondsInput.value = ( totalMilliseconds / 1000 ).toFixed( 3 );
 
showMessage(
'保存値: ' + timeInput.value + '(' + totalMilliseconds + ' ミリ秒)',
false
);
return true;
}
}


timeInput.addEventListener( 'input', updateSeconds );
setVisibleValuesFromStoredTime();
timeInput.addEventListener( 'change', updateSeconds );
 
timeInput.addEventListener( 'blur', updateSeconds );
[ hoursInput, minutesInput, secondsInput ].forEach( function ( input ) {
input.addEventListener( 'input', updateRecordValues );
input.addEventListener( 'change', updateRecordValues );
input.addEventListener( 'blur', updateRecordValues );
} );
 
if ( timeInput.form ) {
timeInput.form.addEventListener( 'submit', function ( event ) {
if ( !updateRecordValues() ) {
event.preventDefault();
}
} );
}


if ( timeInput.value ) {
if ( timeInput.value ) {
updateSeconds();
updateRecordValues();
} else {
showMessage( '時・分・秒を入力してください。秒は小数点以下3桁まで入力できます。', false );
}
}
}
}


function start() {
function start() {
setUpRecordSeconds();
setUpRecordTimeInputs();
mw.hook( 'wikipage.content' ).add( setUpRecordSeconds );
mw.hook( 'wikipage.content' ).add( setUpRecordTimeInputs );
}
 
if ( document.readyState === 'loading' ) {
document.addEventListener( 'DOMContentLoaded', start );
} else {
start();
}
} );
/* RTA Wiki: 記録カードの並べ替え */
mw.loader.using( 'mediawiki.util' ).then( function () {
function setUpRecordCardSorts() {
document.querySelectorAll( '.rta-record-cards--sortable' ).forEach( function ( container, index ) {
if ( container.dataset.rtaSortable === '1' ) {
return;
}
container.dataset.rtaSortable = '1';
 
var control = document.createElement( 'div' );
var label = document.createElement( 'label' );
var select = document.createElement( 'select' );
var selectId = 'rta-record-sort-' + index;
 
control.className = 'rta-record-sort';
label.htmlFor = selectId;
label.textContent = '並び順:';
select.id = selectId;
 
[
[ 'time-asc', 'タイムが速い順' ],
[ 'time-desc', 'タイムが遅い順' ],
[ 'date-desc', '記録日が新しい順' ],
[ 'date-asc', '記録日が古い順' ]
].forEach( function ( item ) {
var option = document.createElement( 'option' );
option.value = item[ 0 ];
option.textContent = item[ 1 ];
select.appendChild( option );
} );
 
control.appendChild( label );
control.appendChild( select );
container.parentNode.insertBefore( control, container );
 
function sortCards() {
var cards = Array.from( container.children ).filter( function ( element ) {
return element.classList.contains( 'rta-record-card' );
} );
 
cards.sort( function ( a, b ) {
var mode = select.value;
var aSeconds = Number( a.dataset.rtaSeconds );
var bSeconds = Number( b.dataset.rtaSeconds );
var aDate = a.dataset.rtaDate || '';
var bDate = b.dataset.rtaDate || '';
 
if ( mode === 'time-asc' || mode === 'time-desc' ) {
if ( !Number.isFinite( aSeconds ) ) {
return 1;
}
if ( !Number.isFinite( bSeconds ) ) {
return -1;
}
return mode === 'time-asc' ?
aSeconds - bSeconds :
bSeconds - aSeconds;
}
 
if ( !aDate ) {
return 1;
}
if ( !bDate ) {
return -1;
}
 
return mode === 'date-asc' ?
aDate.localeCompare( bDate ) :
bDate.localeCompare( aDate );
} );
 
cards.forEach( function ( card ) {
container.appendChild( card );
} );
}
 
select.addEventListener( 'change', sortCards );
} );
}
 
function start() {
setUpRecordCardSorts();
mw.hook( 'wikipage.content' ).add( setUpRecordCardSorts );
}
}


92行目: 332行目:
start();
start();
}
}
} );
/* RTA Wiki: 記録カードの年・版・実施条件・確認区分による絞り込み */
mw.loader.using( 'mediawiki.util' ).then( function () {
        function setUpRecordCardFilters() {
                document.querySelectorAll( '.rta-record-cards--filterable' ).forEach( function ( container, index ) {
                        if ( container.dataset.rtaFilterable === '1' ) {
                                return;
                        }
                        container.dataset.rtaFilterable = '1';
                        var cards = Array.from( container.children ).filter( function ( element ) {
                                return element.classList.contains( 'rta-record-card' );
                        } );
                        var years = Array.from( new Set( cards.map( function ( card ) {
                                return card.dataset.rtaYear;
                        } ).filter( Boolean ) ) ).sort().reverse();
                        var versions = Array.from( new Set( cards.map( function ( card ) {
                                return card.dataset.rtaVersion;
                        } ).filter( Boolean ) ) ).sort();
                        var control = document.createElement( 'div' );
                        var yearLabel = document.createElement( 'label' );
                        var yearSelect = document.createElement( 'select' );
                        var versionLabel = document.createElement( 'label' );
                        var versionSelect = document.createElement( 'select' );
                        var conditionLabel = document.createElement( 'label' );
                        var conditionSelect = document.createElement( 'select' );
                        var verificationLabel = document.createElement( 'label' );
                        var verificationSelect = document.createElement( 'select' );
                        var result = document.createElement( 'span' );
                        var yearId = 'rta-record-year-filter-' + index;
                        var versionId = 'rta-record-version-filter-' + index;
                        var conditionId = 'rta-record-condition-filter-' + index;
                        var verificationId = 'rta-record-verification-filter-' + index;
                        control.className = 'rta-record-filter';
                        yearLabel.htmlFor = yearId;
                        yearLabel.textContent = '年:';
                        yearSelect.id = yearId;
                        [ [ '', 'すべて' ] ].concat( years.map( function ( year ) {
                                return [ year, year + '年' ];
                        } ) ).forEach( function ( item ) {
                                var option = document.createElement( 'option' );
                                option.value = item[ 0 ];
                                option.textContent = item[ 1 ];
                                yearSelect.appendChild( option );
                        } );
                        versionLabel.htmlFor = versionId;
                        versionLabel.textContent = '作品の版:';
                        versionSelect.id = versionId;
                        [ [ '', 'すべて' ] ].concat( versions.map( function ( version ) {
                                return [ version, version ];
                        } ) ).forEach( function ( item ) {
                                var option = document.createElement( 'option' );
                                option.value = item[ 0 ];
                                option.textContent = item[ 1 ];
                                versionSelect.appendChild( option );
                        } );
                        conditionLabel.htmlFor = conditionId;
                        conditionLabel.textContent = '実施条件:';
                        conditionSelect.id = conditionId;
                        [
                                [ '', 'すべて' ],
                                [ 'normal', '通常RTA' ],
                                [ 'event', '大会・イベント記録' ],
                                [ 'race', '並走' ],
                                [ 'single-segment', 'シングルセグメント' ]
                        ].forEach( function ( item ) {
                                var option = document.createElement( 'option' );
                                option.value = item[ 0 ];
                                option.textContent = item[ 1 ];
                                conditionSelect.appendChild( option );
                        } );
                        verificationLabel.htmlFor = verificationId;
                        verificationLabel.textContent = '確認区分:';
                        verificationSelect.id = verificationId;
                        [
                                [ '', 'すべて' ],
                                [ '動画・配信で確認', '動画・配信で確認' ],
                                [ '動画なし・資料で確認', '動画なし・資料で確認' ],
                                [ '参考記録(確認資料なし)', '参考記録(確認資料なし)' ],
                                [ '区分未設定', '区分未設定' ]
                        ].forEach( function ( item ) {
                                var option = document.createElement( 'option' );
                                option.value = item[ 0 ];
                                option.textContent = item[ 1 ];
                                verificationSelect.appendChild( option );
                        } );
                        result.className = 'rta-record-filter__result';
                        control.append(
                                yearLabel, yearSelect,
                                versionLabel, versionSelect,
                                conditionLabel, conditionSelect,
                                verificationLabel, verificationSelect,
                                result
                        );
                        container.parentNode.insertBefore( control, container );
                        function applyFilters() {
                                var visibleCount = 0;
                                cards.forEach( function ( card ) {
                                        var event = card.dataset.rtaEvent === 'はい';
                                        var race = card.dataset.rtaRace === 'はい';
                                        var singleSegment = card.dataset.rtaSingleSegment === 'はい';
                                        var yearMatches = !yearSelect.value ||
                                                card.dataset.rtaYear === yearSelect.value;
                                        var versionMatches = !versionSelect.value ||
                                                card.dataset.rtaVersion === versionSelect.value;
                                        var conditionMatches = (
                                                !conditionSelect.value ||
                                                ( conditionSelect.value === 'normal' && !event && !race && !singleSegment ) ||
                                                ( conditionSelect.value === 'event' && event ) ||
                                                ( conditionSelect.value === 'race' && race ) ||
                                                ( conditionSelect.value === 'single-segment' && singleSegment )
                                        );
                                        var verificationMatches = !verificationSelect.value ||
                                                ( card.dataset.rtaVerification || '区分未設定' ) === verificationSelect.value;
                                        card.hidden = !(
                                                yearMatches &&
                                                versionMatches &&
                                                conditionMatches &&
                                                verificationMatches
                                        );
                                        if ( !card.hidden ) {
                                                visibleCount++;
                                        }
                                } );
                                result.textContent = visibleCount + ' / ' + cards.length + '件を表示';
                        }
                        yearSelect.addEventListener( 'change', applyFilters );
                        versionSelect.addEventListener( 'change', applyFilters );
                        conditionSelect.addEventListener( 'change', applyFilters );
                        verificationSelect.addEventListener( 'change', applyFilters );
                        applyFilters();
                } );
        }
        function start() {
                setUpRecordCardFilters();
                mw.hook( 'wikipage.content' ).add( setUpRecordCardFilters );
        }
        if ( document.readyState === 'loading' ) {
                document.addEventListener( 'DOMContentLoaded', start );
        } else {
                start();
        }
} );
/* RTA記録フォーム:確認区分に応じた必須入力 */
$( function () {
var form = document.getElementById( 'pfForm' );
if ( !form ) {
return;
}
var verification = form.querySelector( '#rta-verification select' );
var evidenceUrl = form.querySelector( '#rta-evidence-url input' );
var basis = form.querySelector( '#rta-confirmation-basis textarea' );
var evidenceMark = form.querySelector( '#rta-evidence-required' );
var basisMark = form.querySelector( '#rta-basis-required' );
if ( !verification || !evidenceUrl || !basis || !evidenceMark || !basisMark ) {
return;
}
function updateRequiredFields() {
var value = verification.value;
var requireUrl = value === '動画・配信で確認';
var requireBasis = value === '動画なし・資料で確認';
evidenceUrl.required = requireUrl;
basis.required = requireBasis;
evidenceUrl.setAttribute( 'aria-required', requireUrl ? 'true' : 'false' );
basis.setAttribute( 'aria-required', requireBasis ? 'true' : 'false' );
evidenceMark.style.display = requireUrl ? 'inline' : 'none';
basisMark.style.display = requireBasis ? 'inline' : 'none';
}
verification.addEventListener( 'change', updateRequiredFields );
updateRequiredFields();
} );
} );

2026年10月25日 (日) 22:51時点における最新版

/* RTA Wiki: PCの匿名利用者には各ページでメインメニューを左側に表示 */
(function () {
	if (
		mw.config.get( 'wgUserName' ) ||
		!window.matchMedia( '(min-width: 1000px)' ).matches
	) {
		return;
	}

	function pinMainMenu() {
		var pinButton = document.querySelector(
			'[data-event-name="pinnable-header.vector-main-menu.pin"]'
		);
		if ( pinButton ) {
			pinButton.click();
		}
	}

	if ( document.documentElement.classList.contains( 'vector-animations-ready' ) ) {
		pinMainMenu();
		return;
	}

	var observer = new MutationObserver( function () {
		if ( document.documentElement.classList.contains( 'vector-animations-ready' ) ) {
			pinMainMenu();
			observer.disconnect();
		}
	} );
	observer.observe( document.documentElement, {
		attributes: true,
		attributeFilter: [ 'class' ]
	} );
}());

/* RTA Wiki: 管理者以外には既存ページの編集導線を表示しない */
mw.loader.using( 'mediawiki.util' ).then( function () {
	function hideEditLinks() {
		if ( mw.config.get( 'wgUserGroups' ).includes( 'sysop' ) ) {
			return;
		}

		document.querySelectorAll(
			'#ca-edit, #ca-ve-edit, #ca-formedit, [id^="ca-formedit"], a[href*="action=formedit"]'
		).forEach( function ( element ) {
			( element.closest( 'li' ) || element ).remove();
		} );
	}

	if ( document.readyState === 'loading' ) {
		document.addEventListener( 'DOMContentLoaded', hideEditLinks );
	} else {
		hideEditLinks();
	}
} );

/* RTA Wiki: 時・分・秒・ミリ秒の入力から記録時間・記録秒を自動設定する */
mw.loader.using( 'mediawiki.util' ).then( function () {
	function setUpRecordTimeInputs() {
		var timeInput = document.querySelector(
			'input[name="RTA記録[記録時間]"], input[name$="[記録時間]"]'
		);
		var storedSecondsInput = document.querySelector(
			'input[name="RTA記録[記録秒]"], input[name$="[記録秒]"]'
		);
		var controls = document.getElementById( 'rta-time-inputs' );

		if (
			!timeInput || !storedSecondsInput || !controls ||
			controls.dataset.rtaTimeSetUp === '1'
		) {
			return;
		}

		controls.dataset.rtaTimeSetUp = '1';
		controls.style.display = 'flex';
		controls.style.gap = '10px';
		controls.style.alignItems = 'center';
		controls.style.flexWrap = 'wrap';

		function makeInput( id, labelText, maxLength, inputMode ) {
			var label = document.createElement( 'label' );
			var input = document.createElement( 'input' );

			input.id = id;
			input.type = 'text';
			input.inputMode = inputMode;
			input.size = 3;
			input.maxLength = maxLength;
			input.value = '0';
			input.autocomplete = 'off';
			input.style.width = '4.5em';
			input.style.boxSizing = 'border-box';

			label.style.display = 'inline-flex';
			label.style.alignItems = 'center';
			label.style.gap = '4px';

			label.appendChild( input );
			label.appendChild( document.createTextNode( labelText ) );
			controls.appendChild( label );

			return input;
		}

		var hoursInput = makeInput( 'rta-record-hours', '時', 6, 'numeric' );
		var minutesInput = makeInput( 'rta-record-minutes', '分', 2, 'numeric' );
		var secondsInput = makeInput( 'rta-record-seconds', '秒', 6, 'decimal' );

		var message = document.createElement( 'small' );
		message.style.display = 'block';
		message.style.marginTop = '4px';
		message.style.color = '#54595d';
		controls.insertAdjacentElement( 'afterend', message );

		function normalizeDigits( value ) {
			return value
				.replace( /[0-9]/g, function ( character ) {
					return String.fromCharCode( character.charCodeAt( 0 ) - 0xFEE0 );
				} )
				.replace( /[.。]/g, '.' )
				.trim();
		}

		function showMessage( text, isError ) {
			message.textContent = text;
			message.style.color = isError ? '#d33' : '#54595d';
		}

		function setVisibleValuesFromStoredTime() {
			var value = normalizeDigits( timeInput.value );
			var match = value.match( /^(\d+):([0-5]\d):([0-5]\d(?:\.\d{1,3})?)$/ );

			if ( !match ) {
				return;
			}

			hoursInput.value = String( Number( match[1] ) );
			minutesInput.value = String( Number( match[2] ) );
			secondsInput.value = match[3];
		}

		function updateRecordValues() {
			var hoursText = normalizeDigits( hoursInput.value );
			var minutesText = normalizeDigits( minutesInput.value );
			var secondsText = normalizeDigits( secondsInput.value );

			hoursInput.value = hoursText;
			minutesInput.value = minutesText;
			secondsInput.value = secondsText;

			if (
				!/^\d+$/.test( hoursText ) ||
				!/^\d+$/.test( minutesText ) ||
				!/^\d+(?:\.\d{1,3})?$/.test( secondsText )
			) {
				timeInput.value = '';
				storedSecondsInput.value = '';
				showMessage( '時・分・秒を正しく入力してください。秒は小数点以下3桁までです。', true );
				return false;
			}

			var hours = Number( hoursText );
			var minutes = Number( minutesText );
			var secondParts = secondsText.split( '.' );
			var wholeSeconds = Number( secondParts[0] );
			var milliseconds = Number( ( secondParts[1] || '' ).padEnd( 3, '0' ) );

			if (
				!Number.isSafeInteger( hours ) ||
				minutes > 59 ||
				wholeSeconds > 59
			) {
				timeInput.value = '';
				storedSecondsInput.value = '';
				showMessage( '分・秒は 0〜59 の範囲で入力してください。', true );
				return false;
			}

			var totalMilliseconds =
				( ( hours * 60 + minutes ) * 60 + wholeSeconds ) * 1000 +
				milliseconds;

			if ( !Number.isSafeInteger( totalMilliseconds ) || totalMilliseconds === 0 ) {
				timeInput.value = '';
				storedSecondsInput.value = '';
				showMessage( '0秒より大きい記録時間を入力してください。', true );
				return false;
			}

			var formattedSeconds = String( wholeSeconds ).padStart( 2, '0' );
			if ( milliseconds > 0 ) {
				formattedSeconds += '.' + String( milliseconds ).padStart( 3, '0' );
			}

			timeInput.value =
				String( hours ) + ':' +
				String( minutes ).padStart( 2, '0' ) + ':' +
				formattedSeconds;

			storedSecondsInput.value = ( totalMilliseconds / 1000 ).toFixed( 3 );

			showMessage(
				'保存値: ' + timeInput.value + '(' + totalMilliseconds + ' ミリ秒)',
				false
			);
			return true;
		}

		setVisibleValuesFromStoredTime();

		[ hoursInput, minutesInput, secondsInput ].forEach( function ( input ) {
			input.addEventListener( 'input', updateRecordValues );
			input.addEventListener( 'change', updateRecordValues );
			input.addEventListener( 'blur', updateRecordValues );
		} );

		if ( timeInput.form ) {
			timeInput.form.addEventListener( 'submit', function ( event ) {
				if ( !updateRecordValues() ) {
					event.preventDefault();
				}
			} );
		}

		if ( timeInput.value ) {
			updateRecordValues();
		} else {
			showMessage( '時・分・秒を入力してください。秒は小数点以下3桁まで入力できます。', false );
		}
	}

	function start() {
		setUpRecordTimeInputs();
		mw.hook( 'wikipage.content' ).add( setUpRecordTimeInputs );
	}

	if ( document.readyState === 'loading' ) {
		document.addEventListener( 'DOMContentLoaded', start );
	} else {
		start();
	}
} );
/* RTA Wiki: 記録カードの並べ替え */
mw.loader.using( 'mediawiki.util' ).then( function () {
	function setUpRecordCardSorts() {
		document.querySelectorAll( '.rta-record-cards--sortable' ).forEach( function ( container, index ) {
			if ( container.dataset.rtaSortable === '1' ) {
				return;
			}
			container.dataset.rtaSortable = '1';

			var control = document.createElement( 'div' );
			var label = document.createElement( 'label' );
			var select = document.createElement( 'select' );
			var selectId = 'rta-record-sort-' + index;

			control.className = 'rta-record-sort';
			label.htmlFor = selectId;
			label.textContent = '並び順:';
			select.id = selectId;

			[
				[ 'time-asc', 'タイムが速い順' ],
				[ 'time-desc', 'タイムが遅い順' ],
				[ 'date-desc', '記録日が新しい順' ],
				[ 'date-asc', '記録日が古い順' ]
			].forEach( function ( item ) {
				var option = document.createElement( 'option' );
				option.value = item[ 0 ];
				option.textContent = item[ 1 ];
				select.appendChild( option );
			} );

			control.appendChild( label );
			control.appendChild( select );
			container.parentNode.insertBefore( control, container );

			function sortCards() {
				var cards = Array.from( container.children ).filter( function ( element ) {
					return element.classList.contains( 'rta-record-card' );
				} );

				cards.sort( function ( a, b ) {
					var mode = select.value;
					var aSeconds = Number( a.dataset.rtaSeconds );
					var bSeconds = Number( b.dataset.rtaSeconds );
					var aDate = a.dataset.rtaDate || '';
					var bDate = b.dataset.rtaDate || '';

					if ( mode === 'time-asc' || mode === 'time-desc' ) {
						if ( !Number.isFinite( aSeconds ) ) {
							return 1;
						}
						if ( !Number.isFinite( bSeconds ) ) {
							return -1;
						}
						return mode === 'time-asc' ?
							aSeconds - bSeconds :
							bSeconds - aSeconds;
					}

					if ( !aDate ) {
						return 1;
					}
					if ( !bDate ) {
						return -1;
					}

					return mode === 'date-asc' ?
						aDate.localeCompare( bDate ) :
						bDate.localeCompare( aDate );
				} );

				cards.forEach( function ( card ) {
					container.appendChild( card );
				} );
			}

			select.addEventListener( 'change', sortCards );
		} );
	}

	function start() {
		setUpRecordCardSorts();
		mw.hook( 'wikipage.content' ).add( setUpRecordCardSorts );
	}

	if ( document.readyState === 'loading' ) {
		document.addEventListener( 'DOMContentLoaded', start );
	} else {
		start();
	}
} );

/* RTA Wiki: 記録カードの年・版・実施条件・確認区分による絞り込み */
mw.loader.using( 'mediawiki.util' ).then( function () {
        function setUpRecordCardFilters() {
                document.querySelectorAll( '.rta-record-cards--filterable' ).forEach( function ( container, index ) {
                        if ( container.dataset.rtaFilterable === '1' ) {
                                return;
                        }
                        container.dataset.rtaFilterable = '1';

                        var cards = Array.from( container.children ).filter( function ( element ) {
                                return element.classList.contains( 'rta-record-card' );
                        } );

                        var years = Array.from( new Set( cards.map( function ( card ) {
                                return card.dataset.rtaYear;
                        } ).filter( Boolean ) ) ).sort().reverse();

                        var versions = Array.from( new Set( cards.map( function ( card ) {
                                return card.dataset.rtaVersion;
                        } ).filter( Boolean ) ) ).sort();

                        var control = document.createElement( 'div' );
                        var yearLabel = document.createElement( 'label' );
                        var yearSelect = document.createElement( 'select' );
                        var versionLabel = document.createElement( 'label' );
                        var versionSelect = document.createElement( 'select' );
                        var conditionLabel = document.createElement( 'label' );
                        var conditionSelect = document.createElement( 'select' );
                        var verificationLabel = document.createElement( 'label' );
                        var verificationSelect = document.createElement( 'select' );
                        var result = document.createElement( 'span' );

                        var yearId = 'rta-record-year-filter-' + index;
                        var versionId = 'rta-record-version-filter-' + index;
                        var conditionId = 'rta-record-condition-filter-' + index;
                        var verificationId = 'rta-record-verification-filter-' + index;

                        control.className = 'rta-record-filter';

                        yearLabel.htmlFor = yearId;
                        yearLabel.textContent = '年:';
                        yearSelect.id = yearId;

                        [ [ '', 'すべて' ] ].concat( years.map( function ( year ) {
                                return [ year, year + '年' ];
                        } ) ).forEach( function ( item ) {
                                var option = document.createElement( 'option' );
                                option.value = item[ 0 ];
                                option.textContent = item[ 1 ];
                                yearSelect.appendChild( option );
                        } );

                        versionLabel.htmlFor = versionId;
                        versionLabel.textContent = '作品の版:';
                        versionSelect.id = versionId;

                        [ [ '', 'すべて' ] ].concat( versions.map( function ( version ) {
                                return [ version, version ];
                        } ) ).forEach( function ( item ) {
                                var option = document.createElement( 'option' );
                                option.value = item[ 0 ];
                                option.textContent = item[ 1 ];
                                versionSelect.appendChild( option );
                        } );

                        conditionLabel.htmlFor = conditionId;
                        conditionLabel.textContent = '実施条件:';
                        conditionSelect.id = conditionId;

                        [
                                [ '', 'すべて' ],
                                [ 'normal', '通常RTA' ],
                                [ 'event', '大会・イベント記録' ],
                                [ 'race', '並走' ],
                                [ 'single-segment', 'シングルセグメント' ]
                        ].forEach( function ( item ) {
                                var option = document.createElement( 'option' );
                                option.value = item[ 0 ];
                                option.textContent = item[ 1 ];
                                conditionSelect.appendChild( option );
                        } );

                        verificationLabel.htmlFor = verificationId;
                        verificationLabel.textContent = '確認区分:';
                        verificationSelect.id = verificationId;

                        [
                                [ '', 'すべて' ],
                                [ '動画・配信で確認', '動画・配信で確認' ],
                                [ '動画なし・資料で確認', '動画なし・資料で確認' ],
                                [ '参考記録(確認資料なし)', '参考記録(確認資料なし)' ],
                                [ '区分未設定', '区分未設定' ]
                        ].forEach( function ( item ) {
                                var option = document.createElement( 'option' );
                                option.value = item[ 0 ];
                                option.textContent = item[ 1 ];
                                verificationSelect.appendChild( option );
                        } );

                        result.className = 'rta-record-filter__result';

                        control.append(
                                yearLabel, yearSelect,
                                versionLabel, versionSelect,
                                conditionLabel, conditionSelect,
                                verificationLabel, verificationSelect,
                                result
                        );

                        container.parentNode.insertBefore( control, container );

                        function applyFilters() {
                                var visibleCount = 0;

                                cards.forEach( function ( card ) {
                                        var event = card.dataset.rtaEvent === 'はい';
                                        var race = card.dataset.rtaRace === 'はい';
                                        var singleSegment = card.dataset.rtaSingleSegment === 'はい';

                                        var yearMatches = !yearSelect.value ||
                                                card.dataset.rtaYear === yearSelect.value;

                                        var versionMatches = !versionSelect.value ||
                                                card.dataset.rtaVersion === versionSelect.value;

                                        var conditionMatches = (
                                                !conditionSelect.value ||
                                                ( conditionSelect.value === 'normal' && !event && !race && !singleSegment ) ||
                                                ( conditionSelect.value === 'event' && event ) ||
                                                ( conditionSelect.value === 'race' && race ) ||
                                                ( conditionSelect.value === 'single-segment' && singleSegment )
                                        );

                                        var verificationMatches = !verificationSelect.value ||
                                                ( card.dataset.rtaVerification || '区分未設定' ) === verificationSelect.value;

                                        card.hidden = !(
                                                yearMatches &&
                                                versionMatches &&
                                                conditionMatches &&
                                                verificationMatches
                                        );

                                        if ( !card.hidden ) {
                                                visibleCount++;
                                        }
                                } );

                                result.textContent = visibleCount + ' / ' + cards.length + '件を表示';
                        }

                        yearSelect.addEventListener( 'change', applyFilters );
                        versionSelect.addEventListener( 'change', applyFilters );
                        conditionSelect.addEventListener( 'change', applyFilters );
                        verificationSelect.addEventListener( 'change', applyFilters );

                        applyFilters();
                } );
        }

        function start() {
                setUpRecordCardFilters();
                mw.hook( 'wikipage.content' ).add( setUpRecordCardFilters );
        }

        if ( document.readyState === 'loading' ) {
                document.addEventListener( 'DOMContentLoaded', start );
        } else {
                start();
        }
} );

/* RTA記録フォーム:確認区分に応じた必須入力 */
$( function () {
	var form = document.getElementById( 'pfForm' );
	if ( !form ) {
		return;
	}

	var verification = form.querySelector( '#rta-verification select' );
	var evidenceUrl = form.querySelector( '#rta-evidence-url input' );
	var basis = form.querySelector( '#rta-confirmation-basis textarea' );
	var evidenceMark = form.querySelector( '#rta-evidence-required' );
	var basisMark = form.querySelector( '#rta-basis-required' );

	if ( !verification || !evidenceUrl || !basis || !evidenceMark || !basisMark ) {
		return;
	}

	function updateRequiredFields() {
		var value = verification.value;
		var requireUrl = value === '動画・配信で確認';
		var requireBasis = value === '動画なし・資料で確認';

		evidenceUrl.required = requireUrl;
		basis.required = requireBasis;
		evidenceUrl.setAttribute( 'aria-required', requireUrl ? 'true' : 'false' );
		basis.setAttribute( 'aria-required', requireBasis ? 'true' : 'false' );

		evidenceMark.style.display = requireUrl ? 'inline' : 'none';
		basisMark.style.display = requireBasis ? 'inline' : 'none';
	}

	verification.addEventListener( 'change', updateRequiredFields );
	updateRequiredFields();
} );