Page Menu
Home
WickedGov Phorge
Search
Configure Global Search
Log In
Files
F2751622
tabs.js
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
7 KB
Referenced Files
None
Subscribers
None
tabs.js
View Options
/*!
* JavaScript for Special:Preferences: Tab navigation.
*/
(
function
()
{
const
nav
=
require
(
'./nav.js'
);
$
(
()
=>
{
const
$tabNavigationHint
=
nav
.
insertHints
(
mw
.
msg
(
'prefs-tabs-navigation-hint'
)
);
const
tabs
=
OO
.
ui
.
infuse
(
$
(
'.mw-prefs-tabs'
)
);
// Support: Chrome
// https://bugs.chromium.org/p/chromium/issues/detail?id=1252507
//
// Infusing the tabs above involves detaching all the tabs' content from the DOM momentarily,
// which causes the :target selector (used in mediawiki.special.preferences.styles.ooui.less)
// not to match anything inside the tabs in Chrome. Twiddling location.href makes it work.
// Only do it when a fragment is present, otherwise the page would be reloaded.
if
(
location
.
href
.
indexOf
(
'#'
)
!==
-
1
)
{
// eslint-disable-next-line no-self-assign
location
.
href
=
location
.
href
;
}
tabs
.
$element
.
addClass
(
'mw-prefs-tabs-infused'
);
function
enhancePanel
(
panel
)
{
if
(
!
panel
.
$element
.
data
(
'mw-section-infused'
)
)
{
panel
.
$element
.
removeClass
(
'mw-htmlform-autoinfuse-lazy'
);
mw
.
hook
(
'htmlform.enhance'
).
fire
(
panel
.
$element
);
panel
.
$element
.
data
(
'mw-section-infused'
,
true
);
}
}
function
onTabPanelSet
(
panel
)
{
if
(
nav
.
switchingNoHash
)
{
return
;
}
// Handle hash manually to prevent jumping,
// therefore save and restore scrollTop to prevent jumping.
const
scrollTop
=
$
(
window
).
scrollTop
();
// Changing the hash apparently causes keyboard focus to be lost?
// Save and restore it. This makes no sense though.
const
active
=
document
.
activeElement
;
location
.
hash
=
'#'
+
panel
.
getName
();
if
(
active
)
{
active
.
focus
();
}
$
(
window
).
scrollTop
(
scrollTop
);
}
tabs
.
on
(
'set'
,
onTabPanelSet
);
// Hash navigation callback
const
setSection
=
function
(
sectionName
,
fieldset
)
{
tabs
.
setTabPanel
(
sectionName
);
enhancePanel
(
tabs
.
getCurrentTabPanel
()
);
// Scroll to a fieldset if provided.
if
(
fieldset
)
{
fieldset
.
scrollIntoView
();
}
};
// onSubmit callback
const
onSubmit
=
function
()
{
const
value
=
tabs
.
getCurrentTabPanelName
();
mw
.
storage
.
session
.
set
(
'mwpreferences-prevTab'
,
value
);
};
nav
.
onLoad
(
setSection
,
'mw-prefsection-personal'
);
nav
.
restorePrevSection
(
setSection
,
onSubmit
);
// Search index
let
index
,
texts
;
function
buildIndex
()
{
index
=
{};
const
$fields
=
tabs
.
contentPanel
.
$element
.
find
(
'[class^=mw-htmlform-field-]:not( .mw-prefs-search-noindex )'
);
const
$descFields
=
$fields
.
filter
(
'.oo-ui-fieldsetLayout-group > .oo-ui-widget > .mw-htmlform-field-HTMLInfoField'
);
$fields
.
not
(
$descFields
).
each
(
function
()
{
let
$field
=
$
(
this
);
const
$wrapper
=
$field
.
parents
(
'.mw-prefs-fieldset-wrapper'
);
const
$tabPanel
=
$field
.
closest
(
'.oo-ui-tabPanelLayout'
);
const
$labels
=
$field
.
find
(
'.oo-ui-labelElement-label, .oo-ui-textInputWidget .oo-ui-inputWidget-input, p'
).
add
(
$wrapper
.
find
(
'> .oo-ui-fieldsetLayout > .oo-ui-fieldsetLayout-header .oo-ui-labelElement-label'
)
);
$field
=
$field
.
add
(
$tabPanel
.
find
(
$descFields
)
);
function
addToIndex
(
$label
,
$highlight
)
{
const
text
=
$label
.
val
()
||
$label
[
0
].
textContent
.
toLowerCase
().
trim
().
replace
(
/\s+/
,
' '
);
if
(
text
)
{
index
[
text
]
=
index
[
text
]
||
[];
index
[
text
].
push
(
{
$highlight
:
$highlight
||
$label
,
$field
:
$field
,
$wrapper
:
$wrapper
,
$tabPanel
:
$tabPanel
}
);
}
}
$labels
.
each
(
function
()
{
addToIndex
(
$
(
this
)
);
// Check if there we are in an infusable dropdown and collect other options
const
$dropdown
=
$
(
this
).
closest
(
'.oo-ui-dropdownInputWidget[data-ooui],.mw-widget-selectWithInputWidget[data-ooui]'
);
if
(
$dropdown
.
length
)
{
const
dropdown
=
OO
.
ui
.
infuse
(
$dropdown
[
0
]
);
const
dropdownWidget
=
(
dropdown
.
dropdowninput
||
dropdown
).
dropdownWidget
;
if
(
dropdownWidget
)
{
dropdownWidget
.
getMenu
().
getItems
().
forEach
(
(
option
)
=>
{
// Highlight the dropdown handle and the matched label, for when the dropdown is opened
addToIndex
(
option
.
$label
,
dropdownWidget
.
$handle
);
addToIndex
(
option
.
$label
,
option
.
$label
);
}
);
}
}
}
);
}
);
mw
.
hook
(
'prefs.search.buildIndex'
).
fire
(
index
);
texts
=
Object
.
keys
(
index
);
}
function
infuseAllPanels
()
{
tabs
.
stackLayout
.
items
.
forEach
(
(
tabPanel
)
=>
{
const
wasVisible
=
tabPanel
.
isVisible
();
// Force panel to be visible while infusing
tabPanel
.
toggle
(
true
);
enhancePanel
(
tabPanel
);
// Restore visibility
tabPanel
.
toggle
(
wasVisible
);
}
);
}
const
searchWrapper
=
OO
.
ui
.
infuse
(
$
(
'.mw-prefs-search'
)
);
const
search
=
searchWrapper
.
fieldWidget
;
search
.
$input
.
on
(
'focus'
,
()
=>
{
if
(
!
index
)
{
// Lazy-build index on first focus
// Infuse all widgets as we may end up showing a large subset of them
infuseAllPanels
();
buildIndex
();
}
}
);
const
$noResults
=
$
(
'<div>'
).
addClass
(
'mw-prefs-noresults'
).
text
(
mw
.
msg
(
'searchprefs-noresults'
)
);
search
.
on
(
'change'
,
(
val
)
=>
{
if
(
!
index
)
{
// In case 'focus' hasn't fired yet
infuseAllPanels
();
buildIndex
();
}
const
isSearching
=
!!
val
;
tabs
.
$element
.
toggleClass
(
'mw-prefs-tabs-searching'
,
isSearching
);
tabs
.
tabSelectWidget
.
toggle
(
!
isSearching
);
tabs
.
contentPanel
.
setContinuous
(
isSearching
);
$
(
'.mw-prefs-search-matched'
).
removeClass
(
'mw-prefs-search-matched'
);
$
(
'.mw-prefs-search-highlight'
).
removeClass
(
'mw-prefs-search-highlight'
);
let
countResults
=
0
;
if
(
isSearching
)
{
val
=
val
.
toLowerCase
();
texts
.
forEach
(
(
text
)
=>
{
// TODO: Could use Intl.Collator.prototype.compare like OO.ui.mixin.LabelElement.static.highlightQuery
// but might be too slow.
if
(
text
.
indexOf
(
val
)
!==
-
1
)
{
index
[
text
].
forEach
(
(
item
)
=>
{
// eslint-disable-next-line no-jquery/no-class-state
if
(
!
item
.
$field
.
hasClass
(
'mw-prefs-search-matched'
)
)
{
// Count each matched preference as one result, not the number of matches in the text
countResults
++
;
}
item
.
$highlight
.
addClass
(
'mw-prefs-search-highlight'
);
item
.
$field
.
addClass
(
'mw-prefs-search-matched'
);
item
.
$wrapper
.
addClass
(
'mw-prefs-search-matched'
);
item
.
$tabPanel
.
addClass
(
'mw-prefs-search-matched'
);
}
);
}
}
);
}
// We hide the tabs when searching, so hide this tip about them as well
$tabNavigationHint
.
toggle
(
!
isSearching
);
// Update invisible label to give screenreader users live feedback while they're typing
if
(
!
isSearching
)
{
searchWrapper
.
setLabel
(
mw
.
msg
(
'searchprefs'
)
);
}
else
if
(
countResults
===
0
)
{
searchWrapper
.
setLabel
(
mw
.
msg
(
'searchprefs-noresults'
)
);
}
else
{
searchWrapper
.
setLabel
(
mw
.
msg
(
'searchprefs-results'
,
countResults
)
);
}
// Update visible label
if
(
isSearching
&&
countResults
===
0
)
{
tabs
.
$element
.
append
(
$noResults
);
}
else
{
$noResults
.
detach
();
}
// Make Enter jump to the results, if there are any
if
(
isSearching
&&
countResults
!==
0
)
{
search
.
on
(
'enter'
,
()
=>
{
tabs
.
focusFirstFocusable
();
}
);
}
else
{
search
.
off
(
'enter'
);
}
}
);
// Handle the initial value in case the user started typing before this JS code loaded,
// or the browser restored the value for a closed tab
if
(
search
.
getValue
()
)
{
search
.
emit
(
'change'
,
search
.
getValue
()
);
}
}
);
}()
);
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Fri, Jul 3, 18:54 (1 d, 17 h)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
50/f2/d0977ca2296669002828886e58f6
Default Alt Text
tabs.js (7 KB)
Attached To
Mode
rMWPROD MediaWiki Production
Attached
Detach File
Event Timeline
Log In to Comment