|
7 | 7 | function initTabber( tabber, count ) {
|
8 | 8 | var tabPanels = tabber.querySelectorAll( ':scope > .tabber__section > .tabber__panel' );
|
9 | 9 |
|
10 |
| - var container = document.createElement( 'header' ), |
| 10 | + var config = require( './config.json' ), |
| 11 | + container = document.createElement( 'header' ), |
11 | 12 | tabList = document.createElement( 'nav' ),
|
12 | 13 | prevButton = document.createElement( 'div' ),
|
13 | 14 | nextButton = document.createElement( 'div' );
|
@@ -149,22 +150,62 @@ function initTabber( tabber, count ) {
|
149 | 150 | updateButtons();
|
150 | 151 | } );
|
151 | 152 |
|
152 |
| - // Listen for window resize |
153 |
| - window.addEventListener( 'resize', mw.util.debounce( 250, setupButtons ) ); |
| 153 | + // Listen for element resize |
| 154 | + if ( window.ResizeObserver ) { |
| 155 | + var tabListResizeObserver = new ResizeObserver( mw.util.debounce( 250, setupButtons ) ); |
| 156 | + tabListResizeObserver.observe( tabList ); |
| 157 | + } |
154 | 158 | };
|
155 | 159 |
|
| 160 | + // NOTE: Are there better ways to scope them? |
| 161 | + var xhr = new XMLHttpRequest(); |
| 162 | + var currentRequest = null, nextRequest = null; |
| 163 | + |
| 164 | + /** |
| 165 | + * Loads page contents into tab |
| 166 | + * |
| 167 | + * @param {HTMLElement} tab panel |
| 168 | + * @param {string} api URL |
| 169 | + */ |
| 170 | + function loadPage( targetPanel, url ) { |
| 171 | + var requestData = { |
| 172 | + url: url, |
| 173 | + targetPanel: targetPanel |
| 174 | + }; |
| 175 | + if ( currentRequest ) { |
| 176 | + if ( currentRequest.url != requestData.url ) { |
| 177 | + nextRequest = requestData; |
| 178 | + } |
| 179 | + // busy |
| 180 | + return; |
| 181 | + } |
| 182 | + xhr.open( 'GET', url ); |
| 183 | + currentRequest = requestData; |
| 184 | + xhr.send( null ); |
| 185 | + } |
| 186 | + |
156 | 187 | /**
|
157 | 188 | * Show panel based on target hash
|
158 | 189 | *
|
159 | 190 | * @param {string} targetHash
|
160 | 191 | */
|
161 |
| - function showPanel( targetHash ) { |
| 192 | + function showPanel( targetHash, allowRemoteLoad ) { |
162 | 193 | var ACTIVETABCLASS = 'tabber__tab--active',
|
163 | 194 | ACTIVEPANELCLASS = 'tabber__panel--active',
|
164 | 195 | targetPanel = document.getElementById( targetHash ),
|
165 | 196 | targetTab = document.getElementById( 'tab-' + targetHash ),
|
166 | 197 | section = targetPanel.parentElement,
|
167 |
| - activePanel = section.querySelector( ':scope > .' + ACTIVEPANELCLASS ); |
| 198 | + activePanel = section.querySelector( ':scope > .' + ACTIVEPANELCLASS ), |
| 199 | + parentPanel, parentSection; |
| 200 | + |
| 201 | + if ( allowRemoteLoad && targetPanel.dataset.tabberPendingLoad && targetPanel.dataset.tabberLoadUrl ) { |
| 202 | + var loading = document.createElement( 'div' ); |
| 203 | + loading.setAttribute( 'class', 'tabber__loading' ); |
| 204 | + loading.appendChild( document.createTextNode( mw.message( 'tabberneue-loading' ).text() ) ); |
| 205 | + targetPanel.textContent = ''; |
| 206 | + targetPanel.appendChild( loading ); |
| 207 | + loadPage( targetPanel, targetPanel.dataset.tabberLoadUrl ); |
| 208 | + } |
168 | 209 |
|
169 | 210 | /* eslint-disable mediawiki/class-doc */
|
170 | 211 | if ( activePanel ) {
|
@@ -212,6 +253,48 @@ function initTabber( tabber, count ) {
|
212 | 253 | /* eslint-enable mediawiki/class-doc */
|
213 | 254 | }
|
214 | 255 |
|
| 256 | + /** |
| 257 | + * Event handler for XMLHttpRequest where ends loading |
| 258 | + */ |
| 259 | + function onLoadEndPage() { |
| 260 | + var targetPanel = currentRequest.targetPanel; |
| 261 | + if ( xhr.status != 200 ) { |
| 262 | + var err = document.createElement( 'div' ); |
| 263 | + err.setAttribute( 'class', 'tabber__error' ); |
| 264 | + err.appendChild( document.createTextNode( mw.message( 'tabberneue-error' ).text() ) ); |
| 265 | + targetPanel.textContent = ''; |
| 266 | + targetPanel.appendChild( err ); |
| 267 | + } else { |
| 268 | + var result = JSON.parse( xhr.responseText ); |
| 269 | + targetPanel.innerHTML = result.parse.text; |
| 270 | + // wikipage.content hook requires a jQuery object |
| 271 | + mw.hook( 'wikipage.content' ).fire( $( targetPanel ) ); |
| 272 | + delete targetPanel.dataset.tabberPendingLoad; |
| 273 | + delete targetPanel.dataset.tabberLoadUrl; |
| 274 | + } |
| 275 | + |
| 276 | + var ACTIVEPANELCLASS = 'tabber__panel--active', |
| 277 | + targetHash = targetPanel.getAttribute( 'id' ), |
| 278 | + section = targetPanel.parentElement, |
| 279 | + activePanel = section.querySelector( ':scope > .' + ACTIVEPANELCLASS ); |
| 280 | + |
| 281 | + if ( nextRequest ) { |
| 282 | + currentRequest = nextRequest; |
| 283 | + nextRequest = null; |
| 284 | + xhr.open( 'GET', currentRequest.url ); |
| 285 | + xhr.send( null ); |
| 286 | + } else { |
| 287 | + currentRequest = null; |
| 288 | + } |
| 289 | + if ( activePanel ) { |
| 290 | + // Refresh height |
| 291 | + showPanel( targetHash, false ); |
| 292 | + } |
| 293 | + } |
| 294 | + |
| 295 | + xhr.timeout = 20000; |
| 296 | + xhr.addEventListener( 'loadend', onLoadEndPage ); |
| 297 | + |
215 | 298 | /**
|
216 | 299 | * Retrieve target hash and trigger show panel
|
217 | 300 | * If no targetHash is invalid, use the first panel
|
@@ -244,9 +327,11 @@ function initTabber( tabber, count ) {
|
244 | 327 | tab.addEventListener( 'click', function( event ) {
|
245 | 328 | var targetHash = tab.getAttribute( 'href' ).substring( 1 );
|
246 | 329 | event.preventDefault();
|
247 |
| - // Add hash to the end of the URL |
248 |
| - history.replaceState( null, null, '#' + targetHash ); |
249 |
| - showPanel( targetHash ); |
| 330 | + if ( !config || config.updateLocationOnTabChange ) { |
| 331 | + // Add hash to the end of the URL |
| 332 | + history.replaceState( null, null, '#' + targetHash ); |
| 333 | + } |
| 334 | + showPanel( targetHash, true ); |
250 | 335 | } );
|
251 | 336 | } );
|
252 | 337 |
|
|
0 commit comments