Page Menu
Home
WickedGov Phorge
Search
Configure Global Search
Log In
Files
F2753051
ReplyLinksController.js
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
12 KB
Referenced Files
None
Subscribers
None
ReplyLinksController.js
View Options
const
// LanguageData::getLocalData()
parserData
=
require
(
'./parser/data.json'
),
utils
=
require
(
'./utils.js'
);
const
featuresEnabled
=
mw
.
config
.
get
(
'wgDiscussionToolsFeaturesEnabled'
)
||
{};
function
tryInfuse
(
$element
)
{
if
(
$element
.
length
)
{
let
element
=
null
;
// $.data() might have already been cleared by jQuery if the elements were removed, ignore
// TODO: We should keep references to the OO.ui.ButtonWidget objects instead of infusing again,
// which would avoid this issue too
try
{
element
=
OO
.
ui
.
infuse
(
$element
);
}
catch
(
e
)
{}
return
element
;
}
return
null
;
}
function
ReplyLinksController
(
$pageContainer
)
{
// Mixin constructors
OO
.
EventEmitter
.
call
(
this
);
this
.
$pageContainer
=
$pageContainer
;
this
.
$body
=
$
(
document
.
body
);
this
.
onReplyLinkClickHandler
=
this
.
onReplyLinkClick
.
bind
(
this
);
this
.
onReplyButtonClickHandler
=
this
.
onReplyButtonClick
.
bind
(
this
);
this
.
onAddSectionLinkClickHandler
=
this
.
onAddSectionLinkClick
.
bind
(
this
);
this
.
onAnyLinkClickHandler
=
this
.
onAnyLinkClick
.
bind
(
this
);
// Reply links
this
.
$replyLinkSets
=
$pageContainer
.
find
(
'.ext-discussiontools-init-replylink-buttons[ data-mw-thread-id ]:not( :empty )'
);
this
.
$replyLinkSets
.
each
(
(
i
,
replyLinkContainer
)
=>
{
const
replyButton
=
tryInfuse
(
$
(
replyLinkContainer
).
find
(
'.ext-discussiontools-init-replybutton'
)
);
const
$replyLink
=
$
(
replyLinkContainer
).
find
(
'.ext-discussiontools-init-replylink-reply'
);
$replyLink
.
on
(
'click keypress'
,
this
.
onReplyLinkClickHandler
);
if
(
replyButton
)
{
replyButton
.
on
(
'click'
,
this
.
onReplyButtonClickHandler
,
[
replyButton
]
);
}
}
);
this
.
$replyLinkSets
.
on
(
'focusin mouseover touchstart'
,
()
=>
{
this
.
emit
(
'link-interact'
);
}
);
// "Add topic" link in the skin interface
if
(
featuresEnabled
.
newtopictool
)
{
// eslint-disable-next-line no-jquery/no-global-selector
const
$addSectionTab
=
$
(
'#ca-addsection'
);
if
(
$addSectionTab
.
length
)
{
this
.
$addSectionLink
=
$addSectionTab
.
find
(
'a'
);
this
.
$addSectionLink
.
on
(
'click keypress'
,
this
.
onAddSectionLinkClickHandler
);
this
.
$addSectionLink
.
on
(
'focusin mouseover touchstart'
,
()
=>
{
this
.
emit
(
'link-interact'
);
}
);
}
// Handle events on all links that potentially open the new section interface,
// including links in the page content (from templates) or from gadgets.
this
.
$body
.
on
(
'click keypress'
,
'a'
,
this
.
onAnyLinkClickHandler
);
}
}
OO
.
initClass
(
ReplyLinksController
);
OO
.
mixinClass
(
ReplyLinksController
,
OO
.
EventEmitter
);
/**
* @event link-click
* @param {string} id
* @param {jQuery} $linkSet
* @param {jQuery} $link
* @param {Object} [data]
*/
/* Methods */
ReplyLinksController
.
prototype
.
onReplyLinkClick
=
function
(
e
)
{
if
(
!
this
.
isActivationEvent
(
e
)
)
{
return
;
}
e
.
preventDefault
();
// Browser plugins (such as Google Translate) may add extra tags inside
// the link, so find the containing link tag with the data we need.
const
$linkSet
=
$
(
e
.
target
).
closest
(
'[data-mw-thread-id]'
);
if
(
!
$linkSet
.
length
)
{
return
;
}
this
.
emit
(
'link-click'
,
$linkSet
.
data
(
'mw-thread-id'
),
$linkSet
);
};
ReplyLinksController
.
prototype
.
onReplyButtonClick
=
function
(
button
)
{
const
$linkSet
=
button
.
$element
.
closest
(
'[data-mw-thread-id]'
);
this
.
emit
(
'link-click'
,
$linkSet
.
data
(
'mw-thread-id'
),
$linkSet
);
};
ReplyLinksController
.
prototype
.
onAddSectionLinkClick
=
function
(
e
)
{
if
(
!
this
.
isActivationEvent
(
e
)
)
{
return
;
}
// Disable VisualEditor's new section editor (in wikitext mode / NWE), to allow our own.
// We do this on first click, because we don't control the order in which our code and NWE code
// runs, so its event handlers may not be registered yet.
$
(
e
.
target
).
closest
(
'#ca-addsection'
).
off
(
'.ve-target'
);
// onAnyLinkClick() will also handle clicks on this element, so we don't emit() here to avoid
// doing it twice.
};
ReplyLinksController
.
prototype
.
onAnyLinkClick
=
function
(
e
)
{
if
(
$
(
e
.
currentTarget
).
closest
(
'[data-mw-thread-id]'
).
length
)
{
// Handled elsewhere
return
;
}
// Check query parameters to see if this is really a new topic link
const
href
=
e
.
currentTarget
.
href
;
if
(
!
href
)
{
return
;
}
const
data
=
this
.
parseNewTopicLink
(
href
);
if
(
!
data
)
{
return
;
}
if
(
!
this
.
isActivationEvent
(
e
)
)
{
return
;
}
e
.
preventDefault
();
this
.
emit
(
'link-click'
,
utils
.
NEW_TOPIC_COMMENT_ID
,
$
(
e
.
currentTarget
),
data
);
};
/**
* Check if the given URL is a new topic link, and if so, return parsed parameters.
*
* @param {string} href
* @return {Object|null} `null` if not a new topic link, parameters otherwise
*/
ReplyLinksController
.
prototype
.
parseNewTopicLink
=
function
(
href
)
{
const
searchParams
=
new
URL
(
href
).
searchParams
;
let
title
=
mw
.
Title
.
newFromText
(
utils
.
getTitleFromUrl
(
href
)
||
''
);
if
(
!
title
)
{
return
null
;
}
// Recognize links to add a new topic:
if
(
// Special:NewSection/...
title
.
getNamespaceId
()
===
mw
.
config
.
get
(
'wgNamespaceIds'
).
special
&&
title
.
getMainText
().
split
(
'/'
)[
0
]
===
parserData
.
specialNewSectionName
)
{
// Get the real title from the subpage parameter
const
param
=
title
.
getMainText
().
slice
(
parserData
.
specialNewSectionName
.
length
+
1
);
title
=
mw
.
Title
.
newFromText
(
param
);
if
(
!
title
)
{
return
null
;
}
}
else
if
(
// ?title=...&action=edit§ion=new
// ?title=...&veaction=editsource§ion=new
(
searchParams
.
get
(
'action'
)
===
'edit'
||
searchParams
.
get
(
'veaction'
)
===
'editsource'
)
&&
searchParams
.
get
(
'section'
)
===
'new'
&&
searchParams
.
get
(
'dtenable'
)
!==
'0'
)
{
// Do nothing
}
else
{
// Not a link to add a new topic
return
null
;
}
if
(
title
.
getPrefixedDb
()
!==
mw
.
config
.
get
(
'wgRelevantPageName'
)
)
{
// Link to add a section on another page, not supported yet (T282205)
return
null
;
}
const
data
=
{};
if
(
searchParams
.
get
(
'editintro'
)
)
{
data
.
editintro
=
searchParams
.
get
(
'editintro'
);
}
if
(
searchParams
.
get
(
'preload'
)
)
{
data
.
preload
=
searchParams
.
get
(
'preload'
);
}
if
(
mw
.
util
.
getArrayParam
(
'preloadparams'
,
searchParams
)
)
{
data
.
preloadparams
=
mw
.
util
.
getArrayParam
(
'preloadparams'
,
searchParams
);
}
if
(
searchParams
.
get
(
'preloadtitle'
)
)
{
data
.
preloadtitle
=
searchParams
.
get
(
'preloadtitle'
);
}
// Handle new topic with preloaded text only when requested (T269310)
if
(
!
searchParams
.
get
(
'dtpreload'
)
&&
!
$
.
isEmptyObject
(
data
)
)
{
return
null
;
}
return
data
;
};
ReplyLinksController
.
prototype
.
isActivationEvent
=
function
(
e
)
{
if
(
mw
.
config
.
get
(
'wgAction'
)
!==
'view'
)
{
// Don't do anything when we're editing/previewing
return
false
;
}
if
(
e
.
type
===
'keypress'
&&
e
.
which
!==
OO
.
ui
.
Keys
.
ENTER
&&
e
.
which
!==
OO
.
ui
.
Keys
.
SPACE
)
{
// Only handle keypresses on the "Enter" or "Space" keys
return
false
;
}
if
(
e
.
type
===
'click'
&&
!
utils
.
isUnmodifiedLeftClick
(
e
)
)
{
// Only handle unmodified left clicks
return
false
;
}
return
true
;
};
ReplyLinksController
.
prototype
.
focusLink
=
function
(
$linkSet
)
{
if
(
$linkSet
.
is
(
this
.
$replyLinkSets
)
)
{
const
button
=
tryInfuse
(
$linkSet
.
find
(
'.ext-discussiontools-init-replybutton'
)
);
// Focus whichever is visible, the link or the button
if
(
button
)
{
button
.
focus
();
}
$linkSet
.
find
(
'.ext-discussiontools-init-replylink-reply'
).
trigger
(
'focus'
);
}
};
ReplyLinksController
.
prototype
.
setActiveLink
=
function
(
$linkSet
)
{
this
.
$activeLink
=
$linkSet
;
let
isNewTopic
=
false
;
let
activeButton
;
if
(
this
.
$activeLink
.
is
(
this
.
$replyLinkSets
)
)
{
this
.
$activeLink
.
addClass
(
'ext-discussiontools-init-replylink-active'
);
activeButton
=
tryInfuse
(
this
.
$activeLink
.
find
(
'.ext-discussiontools-init-replybutton'
)
);
}
else
if
(
this
.
$addSectionLink
&&
this
.
$activeLink
.
is
(
this
.
$addSectionLink
)
)
{
isNewTopic
=
true
;
// eslint-disable-next-line no-jquery/no-global-selector
$
(
'#ca-addsection'
).
addClass
(
'selected'
);
// eslint-disable-next-line no-jquery/no-global-selector
$
(
'#ca-addsection-sticky-header'
).
addClass
(
'ext-discussiontools-fake-disabled'
);
// eslint-disable-next-line no-jquery/no-global-selector
$
(
'#ca-view'
).
removeClass
(
'selected'
);
}
const
title
=
mw
.
Title
.
newFromText
(
mw
.
config
.
get
(
'wgRelevantPageName'
)
);
const
pageTitleMsg
=
mw
.
message
(
'pagetitle'
,
mw
.
msg
(
isNewTopic
?
'discussiontools-pagetitle-newtopic'
:
'discussiontools-pagetitle-reply'
,
title
.
getPrefixedText
()
)
);
// T317600
if
(
pageTitleMsg
.
isParseable
()
)
{
this
.
originalDocumentTitle
=
document
.
title
;
document
.
title
=
pageTitleMsg
.
text
();
}
else
{
mw
.
log
.
warn
(
'DiscussionTools: MediaWiki:Pagetitle contains unsupported syntax. '
+
'https://www.mediawiki.org/wiki/Manual:Messages_API#Feature_support_in_JavaScript'
);
}
$
(
document
.
body
).
addClass
(
'ext-discussiontools-init-replylink-open'
);
this
.
$replyLinkSets
.
each
(
(
i
,
replyLinkContainer
)
=>
{
const
replyButton
=
tryInfuse
(
$
(
replyLinkContainer
).
find
(
'.ext-discussiontools-init-replybutton'
)
);
const
$replyLink
=
$
(
replyLinkContainer
).
find
(
'.ext-discussiontools-init-replylink-reply'
);
$replyLink
.
attr
(
'tabindex'
,
-
1
);
if
(
!
replyButton
)
{
return
;
}
if
(
replyButton
===
activeButton
)
{
replyButton
.
setFlags
(
{
progressive
:
false
}
);
}
else
{
replyButton
.
setDisabled
(
true
);
}
}
);
// Suppress page takeover behavior for VE editing so that our unload
// handler can warn of data loss.
// eslint-disable-next-line no-jquery/no-global-selector
$
(
'#ca-edit, #ca-ve-edit, .mw-editsection a, #ca-addsection'
).
off
(
'.ve-target'
);
};
ReplyLinksController
.
prototype
.
clearActiveLink
=
function
()
{
let
activeButton
;
if
(
this
.
$activeLink
.
is
(
this
.
$replyLinkSets
)
)
{
this
.
$activeLink
.
removeClass
(
'ext-discussiontools-init-replylink-active'
);
activeButton
=
tryInfuse
(
this
.
$activeLink
.
find
(
'.ext-discussiontools-init-replybutton'
)
);
}
else
if
(
this
.
$addSectionLink
&&
this
.
$activeLink
.
is
(
this
.
$addSectionLink
)
)
{
// eslint-disable-next-line no-jquery/no-global-selector
$
(
'#ca-addsection'
).
removeClass
(
'selected'
);
// eslint-disable-next-line no-jquery/no-global-selector
$
(
'#ca-addsection-sticky-header'
).
removeClass
(
'ext-discussiontools-fake-disabled'
);
// eslint-disable-next-line no-jquery/no-global-selector
$
(
'#ca-view'
).
addClass
(
'selected'
);
}
if
(
this
.
originalDocumentTitle
)
{
document
.
title
=
this
.
originalDocumentTitle
;
}
$
(
document
.
body
).
removeClass
(
'ext-discussiontools-init-replylink-open'
);
this
.
$replyLinkSets
.
each
(
(
i
,
replyLinkContainer
)
=>
{
const
$replyLink
=
$
(
replyLinkContainer
).
find
(
'.ext-discussiontools-init-replylink-reply'
);
$replyLink
.
attr
(
'tabindex'
,
0
);
const
replyButton
=
tryInfuse
(
$
(
replyLinkContainer
).
find
(
'.ext-discussiontools-init-replybutton'
)
);
if
(
!
replyButton
)
{
return
;
}
if
(
replyButton
===
activeButton
)
{
replyButton
.
setFlags
(
{
progressive
:
true
}
);
}
else
{
replyButton
.
setDisabled
(
false
);
}
}
);
// We deliberately mangled edit links earlier so VE can't steal our page;
// have it redo setup to fix those.
if
(
mw
.
libs
.
ve
&&
mw
.
libs
.
ve
.
setupEditLinks
)
{
mw
.
libs
.
ve
.
setupEditLinks
();
}
this
.
$activeLink
=
null
;
};
ReplyLinksController
.
prototype
.
teardown
=
function
()
{
if
(
this
.
$activeLink
)
{
this
.
clearActiveLink
();
}
this
.
$replyLinkSets
.
each
(
(
i
,
replyLinkContainer
)
=>
{
const
replyButton
=
tryInfuse
(
$
(
replyLinkContainer
).
find
(
'.ext-discussiontools-init-replybutton'
)
);
if
(
replyButton
)
{
replyButton
.
off
(
'click'
,
this
.
onReplyButtonClickHandler
);
}
const
$replyLink
=
$
(
this
).
find
(
'.ext-discussiontools-init-replylink-reply'
);
$replyLink
.
off
(
'click keypress'
,
this
.
onReplyLinkClickHandler
);
}
);
if
(
featuresEnabled
.
newtopictool
)
{
if
(
this
.
$addSectionLink
)
{
this
.
$addSectionLink
.
off
(
'click keypress'
,
this
.
onAddSectionLinkClickHandler
);
}
this
.
$body
.
off
(
'click keypress'
,
'a'
,
this
.
onAnyLinkClickHandler
);
}
};
ReplyLinksController
.
prototype
.
pageHasReplyLinks
=
function
()
{
return
this
.
$replyLinkSets
.
length
>
0
;
};
ReplyLinksController
.
prototype
.
pageHasNewTopicLink
=
function
()
{
// Note: this will miss if there are random on-page links that would
// trigger the new topic tool via onAnyLinkClick
return
featuresEnabled
.
newtopictool
&&
document
.
getElementById
(
'ca-addsection'
);
};
module
.
exports
=
ReplyLinksController
;
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Fri, Jul 3, 20:43 (1 d, 10 h)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
54/61/e58e02ed9a21f2ec078d6ea4e81c
Default Alt Text
ReplyLinksController.js (12 KB)
Attached To
Mode
rMWPROD MediaWiki Production
Attached
Detach File
Event Timeline
Log In to Comment