Page Menu
Home
WickedGov Phorge
Search
Configure Global Search
Log In
Files
F4148290
ve.ui.MWReferenceSearchWidget.js
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
8 KB
Referenced Files
None
Subscribers
None
ve.ui.MWReferenceSearchWidget.js
View Options
'use strict'
;
/*!
* VisualEditor UserInterface MWReferenceSearchWidget class.
*
* @copyright 2011-2018 VisualEditor Team's Cite sub-team and others; see AUTHORS.txt
* @license MIT
*/
/**
* Creates an ve.ui.MWReferenceSearchWidget object.
*
* @constructor
* @extends OO.ui.SearchWidget
* @param {Object} config Configuration options
* @param {jQuery} config.$overlay Layer to render reuse submenu outside of the parent dialog
* @property {Object[]|null} index Null when the index needs to be rebuild
*/
ve
.
ui
.
MWReferenceSearchWidget
=
function
VeUiMWReferenceSearchWidget
(
config
)
{
// Configuration initialization
config
=
ve
.
extendObject
(
{
placeholder
:
ve
.
msg
(
'cite-ve-reference-input-placeholder'
)
},
config
);
// Parent constructor
ve
.
ui
.
MWReferenceSearchWidget
.
super
.
call
(
this
,
config
);
// Properties
this
.
docRefs
=
null
;
this
.
index
=
null
;
this
.
wasUsedActively
=
false
;
this
.
$overlay
=
config
.
$overlay
;
// Initialization
this
.
$element
.
addClass
(
've-ui-mwReferenceSearchWidget'
);
this
.
$results
.
on
(
'scroll'
,
this
.
trackActiveUsage
.
bind
(
this
)
);
this
.
getResults
().
connect
(
this
,
{
choose
:
'onChoose'
}
);
// FIXME: T375053 Should just be temporary to test some UI changes
if
(
mw
.
config
.
get
(
'wgCiteSubReferencing'
)
)
{
this
.
$element
.
addClass
(
've-ui-mwReferenceSearchReuseHacks'
);
}
};
/* Inheritance */
OO
.
inheritClass
(
ve
.
ui
.
MWReferenceSearchWidget
,
OO
.
ui
.
SearchWidget
);
/* Events */
/**
* User chose a ref for reuse
*
* @event ve.ui.MWReferenceSearchWidget#reuse
* @param {ve.dm.MWReferenceModel} ref
*/
/**
* Extends popup menu item was clicked
*
* @event ve.ui.MWReferenceSearchWidget#extends
* @param {ve.dm.MWReferenceModel} ref
*/
/* Methods */
ve
.
ui
.
MWReferenceSearchWidget
.
prototype
.
onQueryChange
=
function
()
{
// Parent method
ve
.
ui
.
MWReferenceSearchWidget
.
super
.
prototype
.
onQueryChange
.
call
(
this
);
// Populate
this
.
getResults
().
addItems
(
this
.
buildSearchResults
(
this
.
getQuery
().
getValue
()
)
);
};
/**
* @param {jQuery.Event} e Key down event
*/
ve
.
ui
.
MWReferenceSearchWidget
.
prototype
.
onQueryKeydown
=
function
(
e
)
{
// Parent method
ve
.
ui
.
MWReferenceSearchWidget
.
super
.
prototype
.
onQueryKeydown
.
call
(
this
,
e
);
this
.
trackActiveUsage
();
};
ve
.
ui
.
MWReferenceSearchWidget
.
prototype
.
clearSearch
=
function
()
{
this
.
getQuery
().
setValue
(
''
);
this
.
wasUsedActively
=
false
;
};
/**
* Track when the user looks for references to reuse using scrolling or filtering results
*/
ve
.
ui
.
MWReferenceSearchWidget
.
prototype
.
trackActiveUsage
=
function
()
{
if
(
this
.
wasUsedActively
)
{
return
;
}
// https://phabricator.wikimedia.org/T362347
ve
.
track
(
'activity.reference'
,
{
action
:
'reuse-dialog-use'
}
);
this
.
wasUsedActively
=
true
;
};
ve
.
ui
.
MWReferenceSearchWidget
.
prototype
.
onChoose
=
function
(
item
)
{
this
.
emit
(
'reuse'
,
item
.
getData
()
);
};
/**
* Set the internal list and check if it contains any references
*
* @param {ve.dm.MWDocumentReferences} docRefs handle to all refs in the original document
*/
ve
.
ui
.
MWReferenceSearchWidget
.
prototype
.
setDocumentRefs
=
function
(
docRefs
)
{
this
.
results
.
unselectItem
();
this
.
docRefs
=
docRefs
;
};
/**
* Set the internal list and check if it contains any references
*
* @deprecated use #setDocumentRefs instead.
* @param {ve.dm.InternalList} internalList
*/
ve
.
ui
.
MWReferenceSearchWidget
.
prototype
.
setInternalList
=
function
(
internalList
)
{
this
.
setDocumentRefs
(
ve
.
dm
.
MWDocumentReferences
.
static
.
refsForDoc
(
internalList
.
getDocument
()
)
);
};
/**
* Manually re-build the index and re-populate the list of search results.
*/
ve
.
ui
.
MWReferenceSearchWidget
.
prototype
.
buildIndex
=
function
()
{
this
.
index
=
null
;
this
.
onQueryChange
();
};
/**
* @private
* @return {Object[]}
*/
ve
.
ui
.
MWReferenceSearchWidget
.
prototype
.
buildSearchIndex
=
function
()
{
const
groupNames
=
this
.
docRefs
.
getAllGroupNames
().
sort
();
// FIXME: Temporary hack, to be removed soon
// eslint-disable-next-line no-jquery/no-class-state
const
filterExtends
=
this
.
$element
.
hasClass
(
've-ui-citoidInspector-extends'
);
let
index
=
[];
for
(
let
i
=
0
;
i
<
groupNames
.
length
;
i
++
)
{
const
groupName
=
groupNames
[
i
];
if
(
groupName
.
indexOf
(
'mwReference/'
)
!==
0
)
{
// FIXME: Should be impossible to reach
continue
;
}
const
groupRefs
=
this
.
docRefs
.
getGroupRefs
(
groupName
);
const
flatNodes
=
groupRefs
.
getAllRefsInDocumentOrder
()
.
filter
(
(
node
)
=>
!
filterExtends
||
!
node
.
getAttribute
(
'extendsRef'
)
);
index
=
index
.
concat
(
flatNodes
.
map
(
(
node
)
=>
{
const
listKey
=
node
.
getAttribute
(
'listKey'
);
// remove `mwReference/` prefix
const
group
=
groupName
.
slice
(
12
);
const
footnoteNumber
=
this
.
docRefs
.
getIndexLabel
(
group
,
listKey
);
const
footnoteLabel
=
(
group
?
group
+
' '
:
''
)
+
footnoteNumber
;
// Use [\s\S]* instead of .* to catch esoteric whitespace (T263698)
const
matches
=
listKey
.
match
(
/^literal\/([\s\S]*)$/
);
const
name
=
matches
&&
matches
[
1
]
||
''
;
let
$refContent
;
// Make visible text, footnoteLabel and reference name searchable
let
refText
=
footnoteLabel
+
' '
+
name
;
const
itemNode
=
groupRefs
.
getInternalModelNode
(
listKey
);
if
(
itemNode
.
length
)
{
$refContent
=
new
ve
.
ui
.
MWPreviewElement
(
itemNode
,
{
useView
:
true
}
).
$element
;
refText
=
$refContent
.
text
()
+
' '
+
refText
;
// Make URLs searchable
$refContent
.
find
(
'a[href]'
).
each
(
(
_
,
element
)
=>
{
refText
+=
' '
+
element
.
getAttribute
(
'href'
);
}
);
}
else
{
$refContent
=
$
(
'<span>'
)
.
addClass
(
've-ce-mwReferencesListNode-muted'
)
.
text
(
ve
.
msg
(
'cite-ve-referenceslist-missingref-in-list'
)
);
}
return
{
$refContent
:
$refContent
,
searchableText
:
refText
.
toLowerCase
(),
// TODO: return a simple node
reference
:
ve
.
dm
.
MWReferenceModel
.
static
.
newFromReferenceNode
(
node
),
footnoteLabel
:
footnoteLabel
,
isSubRef
:
!!
node
.
getAttribute
(
'extendsRef'
),
name
:
name
};
}
)
);
}
return
index
;
};
/**
* @return {boolean} Index is empty
*/
ve
.
ui
.
MWReferenceSearchWidget
.
prototype
.
isIndexEmpty
=
function
()
{
return
!
this
.
docRefs
.
hasRefs
();
};
/**
* @private
* @param {ve.dm.MWReferenceModel} ref
* @return {OO.ui.ButtonMenuSelectWidget}
*/
ve
.
ui
.
MWReferenceSearchWidget
.
prototype
.
buildReuseOptionsMenu
=
function
(
ref
)
{
// TODO: Select the row on menu button click, so we don't have to wire ref
// through the closure.
const
reuseOptionsMenu
=
new
OO
.
ui
.
ButtonMenuSelectWidget
(
{
classes
:
[
've-ui-mwReferenceResultsReuseOptions'
],
framed
:
false
,
icon
:
'ellipsis'
,
// TODO: The […] button should have its own title, see T375053
title
:
''
,
invisibleLabel
:
true
,
menu
:
{
classes
:
[
've-ui-mwReferenceResultsReuseOptionsItem'
],
horizontalPosition
:
'end'
,
items
:
[
new
OO
.
ui
.
MenuOptionWidget
(
{
data
:
'reuse'
,
label
:
ve
.
msg
(
'cite-ve-dialog-reference-useexisting-long-tool'
)
}
),
new
OO
.
ui
.
MenuOptionWidget
(
{
data
:
'extends'
,
label
:
ve
.
msg
(
'cite-ve-dialog-reference-extend-long-tool'
)
}
)
]
},
// FIXME: Overlay clips to the dialog, should be full-screen.
$overlay
:
this
.
$overlay
}
);
// Hack to prevent a menu button click from being handled by the top-level
// select item.
reuseOptionsMenu
.
$element
.
on
(
'mousedown'
,
(
e
)
=>
{
e
.
stopPropagation
();
}
);
reuseOptionsMenu
.
getMenu
().
on
(
'choose'
,
(
menuOption
)
=>
{
// Emit 'reuse' or 'extends' events, with the chosen ref.
this
.
emit
(
menuOption
.
getData
(),
ref
);
}
);
return
reuseOptionsMenu
;
};
/**
* @private
* @param {string} query
* @return {ve.ui.MWReferenceResultWidget[]}
*/
ve
.
ui
.
MWReferenceSearchWidget
.
prototype
.
buildSearchResults
=
function
(
query
)
{
query
=
query
.
trim
().
toLowerCase
();
const
results
=
[];
if
(
!
this
.
index
)
{
this
.
index
=
this
.
buildSearchIndex
();
}
for
(
let
i
=
0
;
i
<
this
.
index
.
length
;
i
++
)
{
const
item
=
this
.
index
[
i
];
if
(
item
.
searchableText
.
includes
(
query
)
)
{
results
.
push
(
new
ve
.
ui
.
MWReferenceResultWidget
(
{
item
:
item
,
reuseMenu
:
item
.
isSubRef
?
undefined
:
this
.
buildReuseOptionsMenu
(
item
.
reference
)
}
)
);
}
}
return
results
;
};
File Metadata
Details
Attached
Mime Type
text/x-asm
Expires
Aug 19 2026, 18:44 (6 w, 3 d ago)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
e0/41/a81bef56bcc1e7a42f8a86816b60
Default Alt Text
ve.ui.MWReferenceSearchWidget.js (8 KB)
Attached To
Mode
rMWPROD MediaWiki Production
Attached
Detach File
Event Timeline
Log In to Comment