]> scripts.mit.edu Git - autoinstallsdev/mediawiki.git/blob - extensions/Cite/modules/ve-cite/ve.ui.MWReferenceSearchWidget.js
MediaWiki 1.30.2
[autoinstallsdev/mediawiki.git] / extensions / Cite / modules / ve-cite / ve.ui.MWReferenceSearchWidget.js
1 /*!
2  * VisualEditor UserInterface MWReferenceSearchWidget class.
3  *
4  * @copyright 2011-2017 Cite VisualEditor Team and others; see AUTHORS.txt
5  * @license The MIT License (MIT); see LICENSE.txt
6  */
7
8 /**
9  * Creates an ve.ui.MWReferenceSearchWidget object.
10  *
11  * @class
12  * @extends OO.ui.SearchWidget
13  *
14  * @constructor
15  * @param {Object} [config] Configuration options
16  */
17 ve.ui.MWReferenceSearchWidget = function VeUiMWReferenceSearchWidget( config ) {
18         // Configuration initialization
19         config = ve.extendObject( {
20                 placeholder: ve.msg( 'cite-ve-reference-input-placeholder' )
21         }, config );
22
23         // Parent constructor
24         ve.ui.MWReferenceSearchWidget.super.call( this, config );
25
26         // Properties
27         this.index = [];
28         this.indexEmpty = true;
29         this.built = false;
30
31         // Initialization
32         this.$element.addClass( 've-ui-mwReferenceSearchWidget' );
33 };
34
35 /* Inheritance */
36
37 OO.inheritClass( ve.ui.MWReferenceSearchWidget, OO.ui.SearchWidget );
38
39 /* Methods */
40
41 /**
42  * Handle query change events.
43  *
44  * @method
45  * @param {string} value New value
46  */
47 ve.ui.MWReferenceSearchWidget.prototype.onQueryChange = function () {
48         // Parent method
49         ve.ui.MWReferenceSearchWidget.super.prototype.onQueryChange.call( this );
50
51         // Populate
52         this.addResults();
53 };
54
55 /**
56  * Set the internal list and check if it contains any references
57  *
58  * @param {ve.dm.InternalList} internalList Internal list
59  */
60 ve.ui.MWReferenceSearchWidget.prototype.setInternalList = function ( internalList ) {
61         var i, iLen, groupNames, groupName, groups = internalList.getNodeGroups();
62
63         if ( this.results.getSelectedItem() ) {
64                 this.results.getSelectedItem().setSelected( false );
65         }
66
67         this.internalList = internalList;
68         this.internalList.connect( this, { update: 'onInternalListUpdate' } );
69         this.internalList.getListNode().connect( this, { update: 'onListNodeUpdate' } );
70
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 ) {
75                         continue;
76                 }
77                 if ( groups[ groupName ].indexOrder.length ) {
78                         this.indexEmpty = false;
79                         return;
80                 }
81         }
82         this.indexEmpty = true;
83 };
84
85 /**
86  * Handle the updating of the InternalList object.
87  *
88  * This will occur after a document transaction.
89  *
90  * @method
91  * @param {string[]} groupsChanged A list of groups which have changed in this transaction
92  */
93 ve.ui.MWReferenceSearchWidget.prototype.onInternalListUpdate = function ( groupsChanged ) {
94         var i, len;
95         for ( i = 0, len = groupsChanged.length; i < len; i++ ) {
96                 if ( groupsChanged[ i ].indexOf( 'mwReference/' ) === 0 ) {
97                         this.built = false;
98                         break;
99                 }
100         }
101 };
102
103 /**
104  * Handle the updating of the InternalListNode.
105  *
106  * This will occur after changes to any InternalItemNode.
107  *
108  * @method
109  */
110 ve.ui.MWReferenceSearchWidget.prototype.onListNodeUpdate = function () {
111         this.built = false;
112 };
113
114 /**
115  * Build a searchable index of references.
116  *
117  * @method
118  */
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();
123
124         if ( this.built ) {
125                 return;
126         }
127
128         function extractAttrs() {
129                 text += ' ' + this.getAttribute( 'href' );
130         }
131
132         this.index = [];
133         groupNames = Object.keys( groups ).sort();
134
135         for ( i = 0, iLen = groupNames.length; i < iLen; i++ ) {
136                 groupName = groupNames[ i ];
137                 if ( groupName.lastIndexOf( 'mwReference/' ) !== 0 ) {
138                         continue;
139                 }
140                 group = groups[ groupName ];
141                 firstNodes = group.firstNodes;
142                 indexOrder = group.indexOrder;
143
144                 n = 0;
145                 for ( j = 0, jLen = indexOrder.length; j < jLen; j++ ) {
146                         refNode = firstNodes[ indexOrder[ j ] ];
147                         // Exclude placeholder references
148                         if ( refNode.getAttribute( 'placeholder' ) ) {
149                                 continue;
150                         }
151                         // Only increment counter for real references
152                         n++;
153                         refModel = ve.dm.MWReferenceModel.static.newFromReferenceNode( refNode );
154                         view = new ve.ui.MWPreviewElement(
155                                 this.internalList.getItemNode( refModel.getListIndex() )
156                         );
157
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]+$/ ) ) {
164                                 name = '';
165                         }
166
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.
172
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 );
177
178                         this.index.push( {
179                                 $element: view.$element,
180                                 text: text,
181                                 reference: refModel,
182                                 citation: citation,
183                                 name: name
184                         } );
185                 }
186         }
187
188         // Re-populate
189         this.onQueryChange();
190
191         this.built = true;
192 };
193
194 /**
195  * Check whether buildIndex will create an empty index based on the current internalList.
196  *
197  * @return {boolean} Index is empty
198  */
199 ve.ui.MWReferenceSearchWidget.prototype.isIndexEmpty = function () {
200         return this.indexEmpty;
201 };
202
203 /**
204  * Handle media query response events.
205  *
206  * @method
207  */
208 ve.ui.MWReferenceSearchWidget.prototype.addResults = function () {
209         var i, len, item, $citation, $name,
210                 query = this.query.getValue().trim().toLowerCase(),
211                 items = [];
212
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 + ']' );
219                         $name = $( '<div>' )
220                                 .addClass( 've-ui-mwReferenceSearchWidget-name' )
221                                 .text( item.name );
222                         items.push(
223                                 new ve.ui.MWReferenceResultWidget( {
224                                         data: item.reference,
225                                         label: $citation.add( $name ).add( item.$element )
226                                 } )
227                         );
228                 }
229         }
230
231         this.results.addItems( items );
232 };