2 * VisualEditor UserInterface MWReferenceSearchWidget class.
4 * @copyright 2011-2017 Cite VisualEditor Team and others; see AUTHORS.txt
5 * @license The MIT License (MIT); see LICENSE.txt
9 * Creates an ve.ui.MWReferenceSearchWidget object.
12 * @extends OO.ui.SearchWidget
15 * @param {Object} [config] Configuration options
17 ve.ui.MWReferenceSearchWidget = function VeUiMWReferenceSearchWidget( config ) {
18 // Configuration initialization
19 config = ve.extendObject( {
20 placeholder: ve.msg( 'cite-ve-reference-input-placeholder' )
24 ve.ui.MWReferenceSearchWidget.super.call( this, config );
28 this.indexEmpty = true;
32 this.$element.addClass( 've-ui-mwReferenceSearchWidget' );
37 OO.inheritClass( ve.ui.MWReferenceSearchWidget, OO.ui.SearchWidget );
42 * Handle query change events.
45 * @param {string} value New value
47 ve.ui.MWReferenceSearchWidget.prototype.onQueryChange = function () {
49 ve.ui.MWReferenceSearchWidget.super.prototype.onQueryChange.call( this );
56 * Set the internal list and check if it contains any references
58 * @param {ve.dm.InternalList} internalList Internal list
60 ve.ui.MWReferenceSearchWidget.prototype.setInternalList = function ( internalList ) {
61 var i, iLen, groupNames, groupName, groups = internalList.getNodeGroups();
63 if ( this.results.getSelectedItem() ) {
64 this.results.getSelectedItem().setSelected( false );
67 this.internalList = internalList;
68 this.internalList.connect( this, { update: 'onInternalListUpdate' } );
69 this.internalList.getListNode().connect( this, { update: 'onListNodeUpdate' } );
71 groupNames = Object.keys( groups );
72 for ( i = 0, iLen = groupNames.length; i < iLen; i++ ) {
73 groupName = groupNames[ i ];
74 if ( groupName.lastIndexOf( 'mwReference/' ) !== 0 ) {
77 if ( groups[ groupName ].indexOrder.length ) {
78 this.indexEmpty = false;
82 this.indexEmpty = true;
86 * Handle the updating of the InternalList object.
88 * This will occur after a document transaction.
91 * @param {string[]} groupsChanged A list of groups which have changed in this transaction
93 ve.ui.MWReferenceSearchWidget.prototype.onInternalListUpdate = function ( groupsChanged ) {
95 for ( i = 0, len = groupsChanged.length; i < len; i++ ) {
96 if ( groupsChanged[ i ].indexOf( 'mwReference/' ) === 0 ) {
104 * Handle the updating of the InternalListNode.
106 * This will occur after changes to any InternalItemNode.
110 ve.ui.MWReferenceSearchWidget.prototype.onListNodeUpdate = function () {
115 * Build a searchable index of references.
119 ve.ui.MWReferenceSearchWidget.prototype.buildIndex = function () {
120 var n, i, iLen, j, jLen, refModel, group, groupName, groupNames, view, text, firstNodes, indexOrder,
121 refGroup, refNode, matches, name, citation,
122 groups = this.internalList.getNodeGroups();
128 function extractAttrs() {
129 text += ' ' + this.getAttribute( 'href' );
133 groupNames = Object.keys( groups ).sort();
135 for ( i = 0, iLen = groupNames.length; i < iLen; i++ ) {
136 groupName = groupNames[ i ];
137 if ( groupName.lastIndexOf( 'mwReference/' ) !== 0 ) {
140 group = groups[ groupName ];
141 firstNodes = group.firstNodes;
142 indexOrder = group.indexOrder;
145 for ( j = 0, jLen = indexOrder.length; j < jLen; j++ ) {
146 refNode = firstNodes[ indexOrder[ j ] ];
147 // Exclude placeholder references
148 if ( refNode.getAttribute( 'placeholder' ) ) {
151 // Only increment counter for real references
153 refModel = ve.dm.MWReferenceModel.static.newFromReferenceNode( refNode );
154 view = new ve.ui.MWPreviewElement(
155 this.internalList.getItemNode( refModel.getListIndex() )
158 refGroup = refModel.getGroup();
159 citation = ( refGroup && refGroup.length ? refGroup + ' ' : '' ) + n;
160 matches = refModel.getListKey().match( /^literal\/(.*)$/ );
161 name = matches && matches[ 1 ] || '';
162 // Hide previously auto-generated reference names
163 if ( name.match( /^:[0-9]+$/ ) ) {
167 // TODO: At some point we need to make sure this text is updated in
168 // case the view node is still rendering. This shouldn't happen because
169 // all references are supposed to be in the store and therefore are
170 // immediately rendered, but we shouldn't trust that on principle to
171 // account for edge cases.
173 // Make visible text, citation and reference name searchable
174 text = [ view.$element.text().toLowerCase(), citation, name ].join( ' ' );
175 // Make URLs searchable
176 view.$element.find( 'a[href]' ).each( extractAttrs );
179 $element: view.$element,
189 this.onQueryChange();
195 * Check whether buildIndex will create an empty index based on the current internalList.
197 * @return {boolean} Index is empty
199 ve.ui.MWReferenceSearchWidget.prototype.isIndexEmpty = function () {
200 return this.indexEmpty;
204 * Handle media query response events.
208 ve.ui.MWReferenceSearchWidget.prototype.addResults = function () {
209 var i, len, item, $citation, $name,
210 query = this.query.getValue().trim().toLowerCase(),
213 for ( i = 0, len = this.index.length; i < len; i++ ) {
214 item = this.index[ i ];
215 if ( item.text.indexOf( query ) >= 0 ) {
216 $citation = $( '<div>' )
217 .addClass( 've-ui-mwReferenceSearchWidget-citation' )
218 .text( '[' + item.citation + ']' );
220 .addClass( 've-ui-mwReferenceSearchWidget-name' )
223 new ve.ui.MWReferenceResultWidget( {
224 data: item.reference,
225 label: $citation.add( $name ).add( item.$element )
231 this.results.addItems( items );