Page Menu
Home
WickedGov Phorge
Search
Configure Global Search
Log In
Files
F4131097
index.js
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
9 KB
Referenced Files
None
Subscribers
None
index.js
View Options
import
*
as
Redux
from
'redux'
;
import
*
as
ReduxThunk
from
'redux-thunk'
;
import
createPagePreviewGateway
from
'./gateway/page'
;
import
createUserSettings
from
'./userSettings'
;
import
createPreviewBehavior
from
'./previewBehavior'
;
import
createSettingsDialogRenderer
from
'./ui/settingsDialogRenderer'
;
import
registerChangeListener
from
'./changeListener'
;
import
createIsPagePreviewsEnabled
from
'./isPagePreviewsEnabled'
;
import
{
fromElement
as
titleFromElement
}
from
'./title'
;
import
{
init
as
rendererInit
,
registerPreviewUI
,
createPagePreview
,
createDisambiguationPreview
}
from
'./ui/renderer'
;
import
createExperiments
from
'./experiments'
;
import
{
isEnabled
as
isStatsvEnabled
}
from
'./instrumentation/statsv'
;
import
changeListeners
from
'./changeListeners'
;
import
*
as
actions
from
'./actions'
;
import
reducers
from
'./reducers'
;
import
createMediaWikiPopupsObject
from
'./integrations/mwpopups'
;
import
{
previewTypes
,
getPreviewType
,
registerModel
,
findNearestEligibleTarget
}
from
'./preview/model'
;
import
setUserConfigFlags
from
'./setUserConfigFlags'
;
import
{
registerGatewayForPreviewType
,
getGatewayForPreviewType
}
from
'./gateway'
;
import
{
FETCH_START_DELAY
,
FETCH_COMPLETE_TARGET_DELAY
}
from
'./constants'
;
/**
* @module popups
* @private
*/
const
EXCLUDED_LINK_SELECTORS
=
[
'.extiw'
,
// ignore links that point to the same article
'.mw-selflink'
,
'.image'
,
'.new'
,
'.internal'
,
'.external'
,
'.mw-cite-backlink a'
,
'.oo-ui-buttonElement-button'
,
'.ve-ce-surface a'
,
// T259889
'.ext-discussiontools-init-timestamplink'
,
'.cancelLink a'
,
// T198652: lists to hash fragments are ignored.
// Note links that include the path will still trigger a hover,
// e.g. <a href="Foo#foo"> will trigger a preview but <a href="#foo"> will not.
// This is intentional behaviour that will not be handled by page previews, to avoid
// introducing complex behaviour. If a link must include the path it should make use of
// the .mw-selflink-fragment class.
'.mw-selflink-fragment'
,
'[href^="#"]'
];
/**
* @typedef {Function} EventTracker
*
* An analytics event tracker, i.e. `mw.track`.
*
* @param {string} topic
* @param {Object} data
*/
/**
* Gets the appropriate analytics event tracker for logging metrics to StatsD
* via [the "StatsD timers and counters" analytics event protocol][0].
*
* If logging metrics to StatsD is enabled for the duration of the user's
* session, then the appriopriate function is `mw.track`; otherwise it's
* `() => {}`.
*
* [0]: https://github.com/wikimedia/mediawiki-extensions-WikimediaEvents/blob/29c864a0/modules/ext.wikimediaEvents.statsd.js
*
* @param {Object} user
* @param {Object} config
* @param {Experiments} experiments
* @return {EventTracker}
*/
function
getStatsvTracker
(
user
,
config
,
experiments
)
{
return
isStatsvEnabled
(
user
,
config
,
experiments
)
?
mw
.
track
:
()
=>
{};
}
/**
* Gets the appropriate analytics event tracker for logging virtual pageviews.
*
* @param {Object} config
* @return {EventTracker}
*/
function
getPageviewTracker
(
config
)
{
return
config
.
get
(
'wgPopupsVirtualPageViews'
)
?
mw
.
track
:
()
=>
{
// NOP
};
}
/**
* Subscribes the registered change listeners to the
* [store](http://redux.js.org/docs/api/Store.html#store).
*
* @param {Redux.Store} store
* @param {Object} registerActions
* @param {UserSettings} userSettings
* @param {Function} settingsDialog
* @param {PreviewBehavior} previewBehavior
* @param {EventTracker} statsvTracker
* @param {EventTracker} pageviewTracker
*/
function
registerChangeListeners
(
store
,
registerActions
,
userSettings
,
settingsDialog
,
previewBehavior
,
statsvTracker
,
pageviewTracker
)
{
registerChangeListener
(
store
,
changeListeners
.
footerLink
(
registerActions
)
);
registerChangeListener
(
store
,
changeListeners
.
linkTitle
()
);
registerChangeListener
(
store
,
changeListeners
.
render
(
previewBehavior
)
);
registerChangeListener
(
store
,
changeListeners
.
statsv
(
registerActions
,
statsvTracker
)
);
registerChangeListener
(
store
,
changeListeners
.
syncUserSettings
(
userSettings
)
);
registerChangeListener
(
store
,
changeListeners
.
settings
(
registerActions
,
settingsDialog
)
);
registerChangeListener
(
store
,
changeListeners
.
pageviews
(
registerActions
,
pageviewTracker
)
);
}
/**
* Creates an event handler that only executes if the current target
* is eligible for page previews and a title can be associated with the element.
*
* @param {Function} handler
* @return {Function}
*/
function
handleDOMEventIfEligible
(
handler
)
{
return
function
(
event
)
{
let
target
=
event
&&
event
.
target
;
if
(
!
target
)
{
return
;
}
// if the element is a text node, as events can be triggered on text nodes
// it won't have a closest method, so we get its parent element (T340081)
if
(
target
.
nodeType
===
3
)
{
target
=
target
.
parentNode
;
}
// If the event bubbles up all the way,
// document does not have closest method, so exit early (T336650).
if
(
target
===
document
)
{
return
;
}
// If the closest method is not defined, let's return early and
// understand this better by logging an error. (T340081)
if
(
target
&&
!
target
.
closest
)
{
const
err
=
new
Error
(
`T340081: Unexpected DOM element
${
target
.
tagName
}
with nodeType
${
target
.
nodeType
}
`
);
mw
.
errorLogger
.
logError
(
err
,
'error.web-team'
);
return
;
}
target
=
findNearestEligibleTarget
(
target
);
if
(
target
===
null
)
{
return
;
}
const
mwTitle
=
titleFromElement
(
target
,
mw
.
config
);
if
(
mwTitle
)
{
handler
(
target
,
mwTitle
,
event
);
}
};
}
/*
* Initialize the application by:
* 1. Initializing side-effects and "services"
* 2. Creating the state store
* 3. Binding the actions to such store
* 4. Registering change listeners
* 5. Triggering the boot action to bootstrap the system
* 6. When the page content is ready:
* - Initializing the renderer
* - Binding hover and click events to the eligible links to trigger actions
*/
(
function
init
()
{
setUserConfigFlags
(
mw
.
config
);
let
compose
=
Redux
.
compose
;
const
// So-called "services".
generateToken
=
mw
.
user
.
generateRandomSessionId
,
pagePreviewGateway
=
createPagePreviewGateway
(
mw
.
config
),
userSettings
=
createUserSettings
(
mw
.
storage
),
settingsDialog
=
createSettingsDialogRenderer
(),
experiments
=
createExperiments
(
mw
.
experiments
),
statsvTracker
=
getStatsvTracker
(
mw
.
user
,
mw
.
config
,
experiments
),
pageviewTracker
=
getPageviewTracker
(
mw
.
config
),
pagePreviewState
=
createIsPagePreviewsEnabled
(
mw
.
user
,
userSettings
,
mw
.
config
);
// If debug mode is enabled, then enable Redux DevTools.
if
(
mw
.
config
.
get
(
'debug'
)
||
/* global process */
process
.
env
.
NODE_ENV
!==
'production'
)
{
// eslint-disable-next-line no-underscore-dangle
compose
=
window
.
__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
||
compose
;
}
const
store
=
Redux
.
createStore
(
Redux
.
combineReducers
(
reducers
),
compose
(
Redux
.
applyMiddleware
(
ReduxThunk
.
default
)
)
);
const
boundActions
=
Redux
.
bindActionCreators
(
actions
,
store
.
dispatch
);
const
previewBehavior
=
createPreviewBehavior
(
mw
.
user
,
boundActions
);
registerChangeListeners
(
store
,
boundActions
,
userSettings
,
settingsDialog
,
previewBehavior
,
statsvTracker
,
pageviewTracker
);
boundActions
.
boot
(
{},
mw
.
user
,
userSettings
,
mw
.
config
,
window
.
location
.
href
);
/*
* Register external interface exposing popups internals so that other
* extensions can query it (T171287)
*/
mw
.
popups
=
createMediaWikiPopupsObject
(
store
,
registerModel
,
registerPreviewUI
,
registerGatewayForPreviewType
,
boundActions
.
registerSetting
,
userSettings
);
// Migrate any old preferences to new system.
// FIXME: This can be removed in 4 weeks time.
userSettings
.
migrateOldPreferences
();
if
(
pagePreviewState
!==
null
)
{
const
excludedLinksSelector
=
EXCLUDED_LINK_SELECTORS
.
join
(
', '
);
// Register default preview type
mw
.
popups
.
register
(
{
type
:
previewTypes
.
TYPE_PAGE
,
selector
:
`#mw-content-text a[href][title]:not(
${
excludedLinksSelector
}
)`
,
delay
:
FETCH_COMPLETE_TARGET_DELAY
-
FETCH_START_DELAY
,
gateway
:
pagePreviewGateway
,
renderFn
:
createPagePreview
,
subTypes
:
[
{
type
:
previewTypes
.
TYPE_DISAMBIGUATION
,
renderFn
:
createDisambiguationPreview
,
doNotRequireSummary
:
true
}
]
}
);
}
rendererInit
();
/*
* Binding hover and click events to the eligible links to trigger actions
*/
function
setupEventListeners
()
{
const
onHover
=
handleDOMEventIfEligible
(
function
(
target
,
mwTitle
,
event
)
{
const
type
=
getPreviewType
(
target
);
const
gateway
=
getGatewayForPreviewType
(
type
);
if
(
!
gateway
)
{
return
;
}
const
scrollTop
=
window
.
scrollY
;
const
bbox
=
target
.
getBoundingClientRect
();
const
offset
=
{
top
:
scrollTop
+
bbox
.
y
,
left
:
window
.
scrollX
+
bbox
.
x
};
const
measures
=
{
pageX
:
event
.
pageX
,
pageY
:
event
.
pageY
,
clientY
:
event
.
clientY
,
width
:
target
.
offsetWidth
,
height
:
target
.
offsetHeight
,
offset
,
clientRects
:
target
.
getClientRects
(),
windowWidth
:
window
.
innerWidth
,
windowHeight
:
window
.
innerHeight
,
scrollTop
};
boundActions
.
linkDwell
(
mwTitle
,
target
,
measures
,
gateway
,
generateToken
,
type
);
}
);
const
onHoverOut
=
handleDOMEventIfEligible
(
function
()
{
boundActions
.
abandon
();
}
);
const
onClick
=
handleDOMEventIfEligible
(
function
(
target
)
{
if
(
previewTypes
.
TYPE_PAGE
===
getPreviewType
(
target
)
)
{
boundActions
.
linkClick
(
target
);
}
}
);
document
.
addEventListener
(
'mouseover'
,
onHover
);
document
.
addEventListener
(
'keyup'
,
onHover
);
document
.
addEventListener
(
'mouseout'
,
onHoverOut
);
document
.
addEventListener
(
'blur'
,
onHoverOut
);
document
.
addEventListener
(
'click'
,
onClick
);
}
setupEventListeners
();
}()
);
window
.
Redux
=
Redux
;
window
.
ReduxThunk
=
ReduxThunk
;
File Metadata
Details
Attached
Mime Type
text/html
Expires
Wed, Aug 19, 10:43 (2 w, 6 d ago)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
20/e2/1df5bd4ca27be6e62e2b37ea56d0
Default Alt Text
index.js (9 KB)
Attached To
Mode
rMWPROD MediaWiki Production
Attached
Detach File
Event Timeline
Log In to Comment