父节点
c0bd16bda3
当前提交
346b68ed77
|
@ -210,7 +210,7 @@ function bindDismissOnFocusLoss( window, checkbox, button, target ) {
|
|||
* @ignore
|
||||
*/
|
||||
function bindDismissOnEscape( window, checkbox ) {
|
||||
const onKeyup = ( /** @type {KeyboardEvent} */ event ) => {
|
||||
const onKeyup = function ( /** @type {KeyboardEvent} */ event ) {
|
||||
// Only handle ESCAPE
|
||||
if ( event.key !== 'Escape' ) {
|
||||
return;
|
||||
|
|
|
@ -11,7 +11,7 @@ function initDirectionObserver( onScrollDown, onScrollUp, threshold ) {
|
|||
|
||||
let lastScrollTop = window.scrollY;
|
||||
|
||||
const onScroll = () => {
|
||||
function onScroll() {
|
||||
const scrollTop = window.scrollY;
|
||||
|
||||
if ( Math.abs( scrollTop - lastScrollTop ) < threshold ) {
|
||||
|
@ -24,7 +24,7 @@ function initDirectionObserver( onScrollDown, onScrollUp, threshold ) {
|
|||
onScrollUp();
|
||||
}
|
||||
lastScrollTop = scrollTop;
|
||||
};
|
||||
}
|
||||
|
||||
window.addEventListener( 'scroll', throttle( onScroll, 250 ) );
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ function initDirectionObserver( onScrollDown, onScrollUp, threshold ) {
|
|||
*/
|
||||
function initIntersectionObserver( onHidden, onVisible ) {
|
||||
/* eslint-disable-next-line compat/compat */
|
||||
return new IntersectionObserver( ( entries ) => {
|
||||
return new IntersectionObserver( function ( entries ) {
|
||||
if ( !entries[ 0 ].isIntersecting && entries[ 0 ].boundingClientRect.top < 0 ) {
|
||||
// Viewport has crossed the bottom edge of the target element.
|
||||
onHidden();
|
||||
|
|
|
@ -15,10 +15,10 @@ const SEARCH_LOADING_CLASS = 'citizen-loading';
|
|||
* @param {function(): void} afterLoadFn function to execute after search module loads.
|
||||
*/
|
||||
function loadSearchModule( element, moduleName, afterLoadFn ) {
|
||||
const requestSearchModule = () => {
|
||||
function requestSearchModule() {
|
||||
mw.loader.using( moduleName, afterLoadFn );
|
||||
element.removeEventListener( 'focus', requestSearchModule );
|
||||
};
|
||||
}
|
||||
|
||||
if ( document.activeElement === element ) {
|
||||
requestSearchModule();
|
||||
|
@ -123,7 +123,7 @@ function isFormField( element ) {
|
|||
* @return {void}
|
||||
*/
|
||||
function bindExpandOnSlash( window, checkbox, input ) {
|
||||
const onExpandOnSlash = ( /** @type {KeyboardEvent} */ event ) => {
|
||||
const onExpandOnSlash = function ( /** @type {KeyboardEvent} */ event ) {
|
||||
// Only handle SPACE and ENTER.
|
||||
if ( event.key === '/' && !isFormField( event.target ) ) {
|
||||
// Since Firefox quickfind interfere with this
|
||||
|
@ -149,10 +149,9 @@ function initSearch( window ) {
|
|||
return;
|
||||
}
|
||||
|
||||
searchBoxes.forEach( ( searchBox ) => {
|
||||
searchBoxes.forEach( function ( searchBox ) {
|
||||
const
|
||||
input = searchBox.querySelector( 'input[name="search"]' ),
|
||||
isPrimarySearch = input && input.getAttribute( 'id' ) === 'searchInput';
|
||||
input = searchBox.querySelector( 'input[name="search"]' ), isPrimarySearch = input && input.getAttribute( 'id' ) === 'searchInput';
|
||||
|
||||
if ( !input ) {
|
||||
return;
|
||||
|
@ -163,13 +162,13 @@ function initSearch( window ) {
|
|||
const checkbox = document.getElementById( 'citizen-search__checkbox' );
|
||||
bindExpandOnSlash( window, checkbox, input );
|
||||
// Focus when toggled
|
||||
checkbox.addEventListener( 'input', () => {
|
||||
checkbox.addEventListener( 'input', function () {
|
||||
focusOnChecked( checkbox, input );
|
||||
} );
|
||||
}
|
||||
|
||||
setLoadingIndicatorListeners( searchBox, true, renderSearchLoadingIndicator );
|
||||
loadSearchModule( input, searchModule, () => {
|
||||
loadSearchModule( input, searchModule, function () {
|
||||
setLoadingIndicatorListeners( searchBox, false, renderSearchLoadingIndicator );
|
||||
} );
|
||||
} );
|
||||
|
|
|
@ -48,44 +48,37 @@ function sectionObserver( props ) {
|
|||
props = Object.assign( {
|
||||
topMargin: 0,
|
||||
throttleMs: 200,
|
||||
onIntersection: () => {}
|
||||
onIntersection: function () { }
|
||||
}, props );
|
||||
|
||||
let /** @type {boolean} */ inThrottle = false;
|
||||
let /** @type {HTMLElement | undefined} */ current;
|
||||
// eslint-disable-next-line compat/compat
|
||||
const observer = new IntersectionObserver( ( entries ) => {
|
||||
const observer = new IntersectionObserver( function ( entries ) {
|
||||
let /** @type {IntersectionObserverEntry | undefined} */ closestNegativeEntry;
|
||||
let /** @type {IntersectionObserverEntry | undefined} */ closestPositiveEntry;
|
||||
const topMargin = /** @type {number} */ ( props.topMargin );
|
||||
|
||||
entries.forEach( ( entry ) => {
|
||||
const top =
|
||||
entry.boundingClientRect.top - topMargin;
|
||||
if (
|
||||
top > 0 &&
|
||||
(
|
||||
closestPositiveEntry === undefined ||
|
||||
top < closestPositiveEntry.boundingClientRect.top - topMargin
|
||||
)
|
||||
entries.forEach( function ( entry ) {
|
||||
const top = entry.boundingClientRect.top - topMargin;
|
||||
if ( top > 0 &&
|
||||
( closestPositiveEntry === undefined ||
|
||||
top < closestPositiveEntry.boundingClientRect.top - topMargin )
|
||||
) {
|
||||
closestPositiveEntry = entry;
|
||||
}
|
||||
|
||||
if (
|
||||
top <= 0 &&
|
||||
(
|
||||
closestNegativeEntry === undefined ||
|
||||
top > closestNegativeEntry.boundingClientRect.top - topMargin
|
||||
)
|
||||
if ( top <= 0 &&
|
||||
( closestNegativeEntry === undefined ||
|
||||
top > closestNegativeEntry.boundingClientRect.top - topMargin )
|
||||
) {
|
||||
closestNegativeEntry = entry;
|
||||
}
|
||||
} );
|
||||
|
||||
const closestTag =
|
||||
/** @type {HTMLElement} */ ( closestNegativeEntry ? closestNegativeEntry.target :
|
||||
/** @type {IntersectionObserverEntry} */ ( closestPositiveEntry ).target
|
||||
/** @type {HTMLElement} */ ( closestNegativeEntry ? closestNegativeEntry.target :
|
||||
/** @type {IntersectionObserverEntry} */ ( closestPositiveEntry ).target
|
||||
);
|
||||
|
||||
// If the intersection is new, fire the `onIntersection` callback.
|
||||
|
@ -109,8 +102,8 @@ function sectionObserver( props ) {
|
|||
function calcIntersection() {
|
||||
// IntersectionObserver will asynchronously calculate the boundingClientRect
|
||||
// of each observed element off the main thread after `observe` is called.
|
||||
props.elements.forEach( ( element ) => {
|
||||
observer.observe( /** @type {HTMLElement} */ ( element ) );
|
||||
props.elements.forEach( function ( element ) {
|
||||
observer.observe( /** @type {HTMLElement} */( element ) );
|
||||
} );
|
||||
}
|
||||
|
||||
|
@ -119,7 +112,7 @@ function sectionObserver( props ) {
|
|||
if ( !inThrottle ) {
|
||||
inThrottle = true;
|
||||
|
||||
setTimeout( () => {
|
||||
setTimeout( function () {
|
||||
calcIntersection();
|
||||
inThrottle = false;
|
||||
}, props.throttleMs );
|
||||
|
|
|
@ -28,9 +28,8 @@ function bind() {
|
|||
// Search for all dropdown containers using the CHECKBOX_HACK_CONTAINER_SELECTOR.
|
||||
const containers = document.querySelectorAll( CHECKBOX_HACK_CONTAINER_SELECTOR );
|
||||
|
||||
containers.forEach( ( container ) => {
|
||||
const
|
||||
checkbox = container.querySelector( CHECKBOX_HACK_CHECKBOX_SELECTOR ),
|
||||
containers.forEach( function ( container ) {
|
||||
const checkbox = container.querySelector( CHECKBOX_HACK_CHECKBOX_SELECTOR ),
|
||||
button = container.querySelector( CHECKBOX_HACK_BUTTON_SELECTOR ),
|
||||
target = container.querySelector( CHECKBOX_HACK_TARGET_SELECTOR );
|
||||
|
||||
|
@ -50,7 +49,7 @@ function bind() {
|
|||
function uncheckCheckboxHacks() {
|
||||
const checkboxes = document.querySelectorAll( CHECKBOX_HACK_CHECKBOX_SELECTOR + ':checked' );
|
||||
|
||||
checkboxes.forEach( ( checkbox ) => {
|
||||
checkboxes.forEach( function ( checkbox ) {
|
||||
/** @type {HTMLInputElement} */ ( checkbox ).checked = false;
|
||||
} );
|
||||
}
|
||||
|
@ -66,11 +65,11 @@ function initStickyHeader( document ) {
|
|||
|
||||
// Detect scroll direction and add the right class
|
||||
// scrollObserver.initDirectionObserver(
|
||||
// () => {
|
||||
// function () {
|
||||
// document.body.classList.remove( 'citizen-scroll--up' );
|
||||
// document.body.classList.add( 'citizen-scroll--down' );
|
||||
// },
|
||||
// () => {
|
||||
// function () {
|
||||
// document.body.classList.remove( 'citizen-scroll--down' );
|
||||
// document.body.classList.add( 'citizen-scroll--up' );
|
||||
// },
|
||||
|
@ -83,10 +82,10 @@ function initStickyHeader( document ) {
|
|||
// Do not start observer if it is set to display:none
|
||||
if ( sentinel && getComputedStyle( sentinel ).getPropertyValue( 'display' ) !== 'none' ) {
|
||||
const observer = scrollObserver.initIntersectionObserver(
|
||||
() => {
|
||||
function () {
|
||||
document.body.classList.add( 'citizen-body-header--sticky' );
|
||||
},
|
||||
() => {
|
||||
function () {
|
||||
document.body.classList.remove( 'citizen-body-header--sticky' );
|
||||
}
|
||||
);
|
||||
|
@ -122,7 +121,7 @@ function main( window ) {
|
|||
sections.init();
|
||||
}
|
||||
|
||||
window.addEventListener( 'beforeunload', () => {
|
||||
window.addEventListener( 'beforeunload', function () {
|
||||
// T295085: Close all dropdown menus when page is unloaded to prevent them
|
||||
// from being open when navigating back to a page.
|
||||
uncheckCheckboxHacks();
|
||||
|
|
|
@ -8,10 +8,8 @@ let /** @type {HTMLElement | undefined} */ activeSection;
|
|||
function changeActiveSection( id ) {
|
||||
const toc = document.getElementById( 'mw-panel-toc' );
|
||||
|
||||
const getLink = ( hash ) => {
|
||||
const
|
||||
prefix = 'a[href="#',
|
||||
suffix = '"]';
|
||||
const getLink = function ( hash ) {
|
||||
const prefix = 'a[href="#', suffix = '"]';
|
||||
|
||||
let el = toc.querySelector( prefix + hash + suffix );
|
||||
|
||||
|
@ -45,7 +43,7 @@ function initToC() {
|
|||
|
||||
// We use scroll-padding-top to handle scrolling with fixed header
|
||||
// It is better to respect that so it is consistent
|
||||
const getTopMargin = () => {
|
||||
const getTopMargin = function () {
|
||||
return Number(
|
||||
window.getComputedStyle( document.documentElement )
|
||||
.getPropertyValue( 'scroll-padding-top' )
|
||||
|
@ -59,7 +57,9 @@ function initToC() {
|
|||
/* T13555 */
|
||||
elements: bodyContent.querySelectorAll( '.mw-headline' ) ? bodyContent.querySelectorAll( '.mw-headline' ) : bodyContent.querySelectorAll( '.mw-heading' ),
|
||||
topMargin: getTopMargin(),
|
||||
onIntersection: ( section ) => { changeActiveSection( section.id ); }
|
||||
onIntersection: function ( section ) {
|
||||
changeActiveSection( section.id );
|
||||
}
|
||||
} );
|
||||
|
||||
// TODO: Pause section observer on ToC link click
|
||||
|
|
|
@ -61,7 +61,7 @@ function getUrl( input ) {
|
|||
* @return {Object} Results
|
||||
*/
|
||||
function convertDataToResults( data ) {
|
||||
const getDisplayTitle = ( item ) => {
|
||||
const getDisplayTitle = function ( item ) {
|
||||
if ( item.pageprops && item.pageprops.displaytitle ) {
|
||||
return item.pageprops.displaytitle;
|
||||
} else {
|
||||
|
@ -69,7 +69,7 @@ function convertDataToResults( data ) {
|
|||
}
|
||||
};
|
||||
|
||||
const getDescription = ( item ) => {
|
||||
const getDescription = function ( item ) {
|
||||
switch ( descriptionSource ) {
|
||||
case 'wikidata':
|
||||
return item.description || '';
|
||||
|
@ -90,7 +90,7 @@ function convertDataToResults( data ) {
|
|||
data = Object.values( data.query.pages );
|
||||
|
||||
// Sort the data with the index property since it is not in order
|
||||
data.sort( ( a, b ) => {
|
||||
data.sort( function ( a, b ) {
|
||||
return a.index - b.index;
|
||||
} );
|
||||
|
||||
|
|
|
@ -120,15 +120,15 @@ function keyboardEvents( event ) {
|
|||
}
|
||||
|
||||
/**
|
||||
* Bind mouseenter and mouseleave event to reproduce mouse hover event
|
||||
* Bind mouseenter and mouseleave event to reproduce mouse hover event
|
||||
*
|
||||
* @param {HTMLElement} element
|
||||
*/
|
||||
function bindMouseHoverEvent( element ) {
|
||||
element.addEventListener( 'mouseenter', ( event ) => {
|
||||
element.addEventListener( 'mouseenter', function ( event ) {
|
||||
toggleActive( event.currentTarget );
|
||||
} );
|
||||
element.addEventListener( 'mouseleave', ( event ) => {
|
||||
element.addEventListener( 'mouseleave', function ( event ) {
|
||||
toggleActive( event.currentTarget );
|
||||
} );
|
||||
}
|
||||
|
@ -146,7 +146,7 @@ function clearSuggestions() {
|
|||
fragment = new DocumentFragment(),
|
||||
template = document.getElementById( `${PREFIX}-template` );
|
||||
|
||||
[ ...typeaheadItems ].forEach( ( item ) => {
|
||||
[ ...typeaheadItems ].forEach( function ( item ) {
|
||||
if ( !item.classList.contains( `${ITEM_CLASS}--page` ) ) {
|
||||
fragment.append( item );
|
||||
}
|
||||
|
@ -169,18 +169,17 @@ function clearSuggestions() {
|
|||
* @param {HTMLElement} placeholder
|
||||
*/
|
||||
function getSuggestions( searchQuery, htmlSafeSearchQuery, placeholder ) {
|
||||
const renderSuggestions = ( results ) => {
|
||||
function renderSuggestions( results ) {
|
||||
if ( results.length > 0 ) {
|
||||
const
|
||||
fragment = document.createDocumentFragment(),
|
||||
suggestionLinkPrefix = `${config.wgScriptPath}/index.php?title=Special:Search&search=`;
|
||||
fragment = document.createDocumentFragment(), suggestionLinkPrefix = `${config.wgScriptPath}/index.php?title=Special:Search&search=`;
|
||||
/**
|
||||
* Return the redirect title with search query highlight
|
||||
*
|
||||
* @param {string} text
|
||||
* @return {string}
|
||||
*/
|
||||
const highlightTitle = ( text ) => {
|
||||
const highlightTitle = function ( text ) {
|
||||
const regex = new RegExp( mw.util.escapeRegExp( htmlSafeSearchQuery ), 'i' );
|
||||
return text.replace( regex, `<span class="${PREFIX}__highlight">$&</span>` );
|
||||
};
|
||||
|
@ -191,24 +190,23 @@ function getSuggestions( searchQuery, htmlSafeSearchQuery, placeholder ) {
|
|||
* @param {string} matchedTitle
|
||||
* @return {string}
|
||||
*/
|
||||
const getRedirectLabel = ( title, matchedTitle ) => {
|
||||
const getRedirectLabel = function ( title, matchedTitle ) {
|
||||
/**
|
||||
* Check if the redirect is useful (T303013)
|
||||
*
|
||||
* @return {boolean}
|
||||
*/
|
||||
const isRedirectUseful = () => {
|
||||
const isRedirectUseful = function () {
|
||||
// Change to lowercase then remove space and dashes
|
||||
const cleanup = ( text ) => {
|
||||
const cleanup = function ( text ) {
|
||||
return text.toLowerCase().replace( /-|\s/g, '' );
|
||||
};
|
||||
const
|
||||
cleanTitle = cleanup( title ),
|
||||
cleanMatchedTitle = cleanup( matchedTitle );
|
||||
cleanTitle = cleanup( title ), cleanMatchedTitle = cleanup( matchedTitle );
|
||||
|
||||
return !(
|
||||
cleanTitle.includes( cleanMatchedTitle ) ||
|
||||
cleanMatchedTitle.includes( cleanTitle )
|
||||
cleanMatchedTitle.includes( cleanTitle )
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -226,7 +224,7 @@ function getSuggestions( searchQuery, htmlSafeSearchQuery, placeholder ) {
|
|||
};
|
||||
|
||||
// Create suggestion items
|
||||
results.forEach( ( result, index ) => {
|
||||
results.forEach( function ( result, index ) {
|
||||
const data = {
|
||||
id: `${PREFIX}-suggestion-${index}`,
|
||||
type: 'page',
|
||||
|
@ -262,7 +260,7 @@ function getSuggestions( searchQuery, htmlSafeSearchQuery, placeholder ) {
|
|||
);
|
||||
placeholder.classList.remove( HIDDEN_CLASS );
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Add loading animation
|
||||
searchInput.parentNode.classList.add( SEARCH_LOADING_CLASS );
|
||||
|
@ -270,7 +268,7 @@ function getSuggestions( searchQuery, htmlSafeSearchQuery, placeholder ) {
|
|||
const
|
||||
// eslint-disable-next-line compat/compat
|
||||
controller = new AbortController(),
|
||||
abortFetch = () => {
|
||||
abortFetch = function () {
|
||||
controller.abort();
|
||||
};
|
||||
|
||||
|
@ -282,13 +280,13 @@ function getSuggestions( searchQuery, htmlSafeSearchQuery, placeholder ) {
|
|||
// So that fetch request won't be queued up
|
||||
searchInput.addEventListener( 'input', abortFetch, { once: true } );
|
||||
|
||||
getResults.then( ( results ) => {
|
||||
getResults.then( function ( results ) {
|
||||
searchInput.removeEventListener( 'input', abortFetch );
|
||||
clearSuggestions();
|
||||
if ( results !== null ) {
|
||||
renderSuggestions( results );
|
||||
}
|
||||
} ).catch( ( error ) => {
|
||||
} ).catch( function ( error ) {
|
||||
searchInput.removeEventListener( 'input', abortFetch );
|
||||
searchInput.parentNode.classList.remove( SEARCH_LOADING_CLASS );
|
||||
// User can trigger the abort when the fetch event is pending
|
||||
|
@ -382,11 +380,9 @@ function updateTypeahead( messages ) {
|
|||
*
|
||||
* @param {Object} data
|
||||
*/
|
||||
const updateToolItem = ( data ) => {
|
||||
const updateToolItem = function ( data ) {
|
||||
const
|
||||
itemId = `${PREFIX}-${data.id}`,
|
||||
query = `<span class="citizen-typeahead__query">${htmlSafeSearchQuery}</span>`,
|
||||
itemLink = data.link + htmlSafeSearchQuery,
|
||||
itemId = `${PREFIX}-${data.id}`, query = `<span class="citizen-typeahead__query">${htmlSafeSearchQuery}</span>`, itemLink = data.link + htmlSafeSearchQuery,
|
||||
/* eslint-disable-next-line mediawiki/msg-doc */
|
||||
itemDesc = mw.message( data.msg, query );
|
||||
|
||||
|
@ -478,13 +474,13 @@ function initTypeahead( searchForm, input ) {
|
|||
'msg-citizen-search-empty-desc': messages.emptyDesc
|
||||
};
|
||||
|
||||
const onBlur = ( event ) => {
|
||||
const onBlur = function ( event ) {
|
||||
const focusIn = typeahead.contains( event.relatedTarget );
|
||||
|
||||
if ( !focusIn ) {
|
||||
// HACK: On Safari, users are unable to click any links because the blur
|
||||
// event dismiss the links before it is clicked. This should fix it.
|
||||
setTimeout( () => {
|
||||
setTimeout( function () {
|
||||
searchInput.setAttribute( 'aria-activedescendant', '' );
|
||||
typeahead.classList.remove( EXPANDED_CLASS );
|
||||
searchInput.removeEventListener( 'keydown', keyboardEvents );
|
||||
|
@ -493,7 +489,7 @@ function initTypeahead( searchForm, input ) {
|
|||
}
|
||||
};
|
||||
|
||||
const onFocus = () => {
|
||||
const onFocus = function () {
|
||||
// Refresh the typeahead since the query will be emptied when blurred
|
||||
updateTypeahead( messages );
|
||||
typeahead.classList.add( EXPANDED_CLASS );
|
||||
|
@ -519,7 +515,7 @@ function initTypeahead( searchForm, input ) {
|
|||
updateTypeahead( messages );
|
||||
}
|
||||
|
||||
searchInput.addEventListener( 'input', () => {
|
||||
searchInput.addEventListener( 'input', function () {
|
||||
mw.util.debounce( 100, updateTypeahead( messages ) );
|
||||
} );
|
||||
|
||||
|
|
正在加载...
在新工单中引用