PluginProbe
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin / 1.1.10
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin v1.1.10
1.1.11 1.1.10 1.1.9 1.1.8 1.1.7 1.1.6 1.1.5 1.1.4 1.1.3 1.1.2 1.1.1 1.1.0 1.0.1 1.0.0 0.9.8 0.9.7 0.9.6 0.9.4 0.9.5 0.9.3 0.9.2 0.9.1 0.9.0 0.8.9 0.8.8 All 35 releases
← All changes | assets/js/admin-bar.js +76 -43 0.9.2 → 1.1.10 View file →
@@ -1,10 +1,10 @@
1 1 ( function() {
2 - var toggle = document.getElementById( 'wp-admin-bar-desktop-mode-toggle' );
2 + var toggle = document.getElementById( 'wp-admin-bar-os-toggle' );
3 3 if ( ! toggle ) {
4 4 return;
5 5 }
6 - var cfg = window.desktopModeAdminBar || {};
6 + var cfg = window.openStationAdminBar || {};
7 7 toggle.addEventListener( 'click', function( e ) {
8 8 e.preventDefault();
9 9 var isActive = !! cfg.active;
10 10 var newValue = isActive ? '' : '1';
@@ -26,11 +26,16 @@
26 26 window.location.href = url;
27 27 }
28 28 }
29 29 var body = new URLSearchParams();
30 - body.set( 'action', 'save-desktop-mode' );
30 + body.set( 'action', 'save-openstation' );
31 31 body.set( 'nonce', cfg.nonce );
32 32 body.set( 'enabled', newValue );
33 + // Tells the handler which admin this click came from; see the
34 + // note in `includes/ajax.php`.
35 + if ( cfg.network ) {
36 + body.set( 'network', '1' );
37 + }
33 38 var xhr = new XMLHttpRequest();
34 39 xhr.open( 'POST', cfg.ajaxUrl, true );
35 40 xhr.setRequestHeader( 'Content-Type', 'application/x-www-form-urlencoded' );
36 41 xhr.onload = function() {
@@ -64,9 +69,9 @@
64 69 if ( ! layoutMenu ) return;
65 70
66 71 function paintSnapCheckbox( enabled ) {
67 72 var node = document.querySelector(
68 - '#wp-admin-bar-desktop-layout-snap .desktop-mode-layout-checkbox'
73 + '#wp-admin-bar-desktop-layout-snap .os-layout-checkbox'
69 74 );
70 75 if ( ! node ) return;
71 76 node.textContent = enabled ? '☑' : '☐'; // ☑ / ☐
72 77 var item = document.getElementById( 'wp-admin-bar-desktop-layout-snap' );
@@ -76,9 +81,9 @@
76 81 }
77 82 }
78 83
79 84 function getManager() {
80 - return window.wp && window.wp.desktop && window.wp.desktop.windowManager;
85 + return window.wp && window.wp.os && window.wp.os.windowManager;
81 86 }
82 87
83 88 // Initial paint — wait for the shell to publish the manager,
84 89 // then mirror the persisted snap preference. Polled rather than
@@ -83,12 +88,28 @@
83 88 // Initial paint — wait for the shell to publish the manager,
84 89 // then mirror the persisted snap preference. Polled rather than
85 90 // hooked because the inline script ships with the admin bar
86 91 // (loads early) and the shell's WindowManager arrives later.
92 + //
93 + // Bounded, because a poll with no exit is a timer that runs for
94 + // the life of the page. The manager lands within a frame or two of
95 + // the shell bundle executing; if it has not arrived in ten seconds
96 + // it is not coming — the shell failed to boot, or a plugin
97 + // surfaced this admin bar somewhere the shell never loads — and
98 + // repainting one checkbox does not justify waking the event loop
99 + // sixteen times a second until the tab closes. Giving up leaves
100 + // the server-rendered box exactly as it is, which is what polling
101 + // forever achieves anyway.
102 + var SNAP_POLL_INTERVAL_MS = 60;
103 + var SNAP_POLL_TIMEOUT_MS = 10000;
104 + var snapPollWaited = 0;
87 105 function initFromManager() {
88 106 var wm = getManager();
89 107 if ( ! wm || typeof wm.isSnapEnabled !== 'function' ) {
90 - window.setTimeout( initFromManager, 60 );
108 + snapPollWaited += SNAP_POLL_INTERVAL_MS;
109 + if ( snapPollWaited < SNAP_POLL_TIMEOUT_MS ) {
110 + window.setTimeout( initFromManager, SNAP_POLL_INTERVAL_MS );
111 + }
91 112 return;
92 113 }
93 114 paintSnapCheckbox( wm.isSnapEnabled() );
94 115 }
@@ -97,9 +118,9 @@
97 118 layoutMenu.addEventListener( 'click', function( e ) {
98 119 var t = e.target;
99 120 if ( ! t || ! t.closest ) return;
100 121
101 - var snapItem = t.closest( '.desktop-mode-layout-snap' );
122 + var snapItem = t.closest( '.os-layout-snap' );
102 123 if ( snapItem ) {
103 124 // Stop propagation so WP's own "click closes submenu"
104 125 // chain never fires. preventDefault keeps the `#` href
105 126 // from scrolling the page to top.
@@ -112,9 +133,9 @@
112 133 paintSnapCheckbox( next );
113 134 return;
114 135 }
115 136
116 - var actionLink = t.closest( '.desktop-mode-layout-action > .ab-item, .desktop-mode-layout-action' );
137 + var actionLink = t.closest( '.os-layout-action > .ab-item, .os-layout-action' );
117 138 if ( ! actionLink ) return;
118 139 e.preventDefault();
119 140 var id = actionLink.closest( '[id^="wp-admin-bar-desktop-layout-"]' );
120 141 if ( ! id ) return;
@@ -129,13 +150,13 @@
129 150 } else if ( id.id.indexOf( 'wp-admin-bar-desktop-layout-custom-' ) === 0 ) {
130 151 // Plugin-registered custom item. Strip the shared prefix to
131 152 // recover the `id` the plugin supplied via the PHP filter,
132 153 // then dispatch the public JS action. Plugins subscribe
133 - // via wp.hooks.addAction( 'desktop-mode.arrange.custom-action', ... ).
154 + // via wp.hooks.addAction( 'os.arrange.custom-action', ... ).
134 155 var customId = id.id.replace( 'wp-admin-bar-desktop-layout-custom-', '' );
135 156 var hooks = window.wp && window.wp.hooks;
136 157 if ( hooks && typeof hooks.doAction === 'function' ) {
137 - hooks.doAction( 'desktop-mode.arrange.custom-action', { id: customId } );
158 + hooks.doAction( 'os.arrange.custom-action', { id: customId } );
138 159 }
139 160 }
140 161 // After running an action, dismiss the submenu so the user
141 162 // lands in the newly arranged desktop instead of the menu
@@ -150,20 +171,8 @@
150 171 document.activeElement.blur();
151 172 }
152 173 } );
153 174
154 - // AI Assistant button — dispatches the `desktop-mode-open-ai` event that
155 - // the AiAssistant class listens for. Using an event instead of a direct
156 - // call decouples the admin-bar inline script (which runs early, before
157 - // the desktop shell has initialised) from the AiAssistant instance.
158 - var aiBtn = document.getElementById( 'wp-admin-bar-desktop-ai-assistant' );
159 - if ( aiBtn ) {
160 - aiBtn.addEventListener( 'click', function( e ) {
161 - e.preventDefault();
162 - document.dispatchEvent( new CustomEvent( 'desktop-mode-open-ai' ) );
163 - } );
164 - }
165 -
166 175 // Fullscreen button — toggles browser fullscreen on the document
167 176 // element so the shell occupies the whole screen with no browser
168 177 // chrome. The click counts as a user gesture, which is required by
169 178 // the Fullscreen API. We listen for `fullscreenchange` instead of
@@ -191,17 +200,17 @@
191 200 // which sit too close to the screen-corner reveal hot zones
192 201 // in browser fullscreen — the OS / browser intercept clicks
193 202 // in those bands. Hiding them shifts the Fullscreen button
194 203 // further from the right edge so its own click lands.
195 - document.body.classList.toggle( 'desktop-mode-browser-fs', !! on );
204 + document.body.classList.toggle( 'os-browser-fs', !! on );
196 205 if ( on ) {
197 206 fsBtn.classList.add( 'is-fullscreen' );
198 207 if ( fsLabel ) fsLabel.textContent = fsI18n.exitFullscreen || 'Exit fullscreen';
199 - if ( fsLink ) fsLink.setAttribute( 'title', fsI18n.exitTitle || 'Exit fullscreen' );
208 + repaintLabel( fsLink, fsI18n.exitTitle || 'Exit fullscreen' );
200 209 } else {
201 210 fsBtn.classList.remove( 'is-fullscreen' );
202 211 if ( fsLabel ) fsLabel.textContent = fsI18n.enterFullscreen || 'Fullscreen';
203 - if ( fsLink ) fsLink.setAttribute( 'title', fsI18n.enterTitle || 'Enter fullscreen' );
212 + repaintLabel( fsLink, fsI18n.enterTitle || 'Enter fullscreen' );
204 213 }
205 214 }
206 215 fsBtn.addEventListener( 'click', function( e ) {
207 216 e.preventDefault();
@@ -238,15 +247,15 @@
238 247 } catch ( err ) {}
239 248 }
240 249
241 250 // Bug Report button — same decoupling pattern as Ask AI. Dispatches
242 - // `desktop-mode-open-bug-report`; the shell answers by opening the
251 + // `os-open-bug-report`; the shell answers by opening the
243 252 // Bug Report native window. Wired in `src/desktop.ts`.
244 253 var bugBtn = document.getElementById( 'wp-admin-bar-desktop-bug-report' );
245 254 if ( bugBtn ) {
246 255 bugBtn.addEventListener( 'click', function( e ) {
247 256 e.preventDefault();
248 - document.dispatchEvent( new CustomEvent( 'desktop-mode-open-bug-report' ) );
257 + document.dispatchEvent( new CustomEvent( 'os-open-bug-report' ) );
249 258 } );
250 259 }
251 260
252 261 // Keyboard Shortcuts button — toggles a floating popover anchored
@@ -269,10 +278,10 @@
269 278 /**
270 279 * Re-anchors a native `title` attribute to a `data-desktop-tooltip`
271 280 * data attribute on the same node, plus mirrors it to `aria-label`
272 281 * so assistive tech keeps the description. Pure-CSS tooltip then
273 - * renders from the data attribute via the `[data-desktop-tooltip]
274 - * .ab-item::after` rule in admin-bar.php.
282 + * renders from the data attribute via the
283 + * `.ab-item[ data-desktop-tooltip ]::after` rule in admin-bar.php.
275 284 */
276 285 function wireTooltipsFor( ids ) {
277 286 for ( var i = 0; i < ids.length; i++ ) {
278 287 var node = document.getElementById( ids[ i ] );
@@ -288,8 +297,32 @@
288 297 }
289 298 }
290 299 }
291 300
301 + /**
302 + * Relabels a button whose action changed under it (today: Fullscreen,
303 + * which flips between enter and exit). Writing only `title` is wrong
304 + * once `wireTooltipsFor()` has run: the visible tooltip renders from
305 + * `data-desktop-tooltip` and the accessible name comes from
306 + * `aria-label`, so a stale pair describes the opposite action while
307 + * the freshly re-added `title` brings the native OS tooltip back on
308 + * top of ours (GH#493).
309 + *
310 + * `title` is only touched when the node is still wearing one, which
311 + * makes this safe to call before wiring as well — the label lands on
312 + * `title` and `wireTooltipsFor()` re-anchors it from there.
313 + */
314 + function repaintLabel( link, label ) {
315 + if ( ! link ) return;
316 + if ( link.hasAttribute( 'title' ) ) {
317 + link.setAttribute( 'title', label );
318 + }
319 + if ( link.hasAttribute( 'data-desktop-tooltip' ) ) {
320 + link.setAttribute( 'data-desktop-tooltip', label );
321 + }
322 + link.setAttribute( 'aria-label', label );
323 + }
324 +
292 325 function wireShortcutsPopover( btn, data ) {
293 326 if ( ! btn || ! data ) {
294 327 return;
295 328 }
@@ -342,9 +375,9 @@
342 375 }
343 376
344 377 function buildShortcutsPopover( data ) {
345 378 var root = document.createElement( 'div' );
346 - root.className = 'desktop-mode-shortcuts-popover';
379 + root.className = 'os-shortcuts-popover';
347 380 root.setAttribute( 'role', 'dialog' );
348 381 if ( data.title ) {
349 382 root.setAttribute( 'aria-label', data.title );
350 383 }
@@ -359,19 +392,19 @@
359 392 }
360 393
361 394 function buildShortcutsTable( section ) {
362 395 var wrap = document.createElement( 'section' );
363 - wrap.className = 'desktop-mode-shortcuts-popover__section';
396 + wrap.className = 'os-shortcuts-popover__section';
364 397
365 398 if ( section.heading ) {
366 399 var h = document.createElement( 'h3' );
367 - h.className = 'desktop-mode-shortcuts-popover__heading';
400 + h.className = 'os-shortcuts-popover__heading';
368 401 h.textContent = section.heading;
369 402 wrap.appendChild( h );
370 403 }
371 404
372 405 var table = document.createElement( 'table' );
373 - table.className = 'desktop-mode-shortcuts-popover__table';
406 + table.className = 'os-shortcuts-popover__table';
374 407
375 408 var thead = document.createElement( 'thead' );
376 409 var headRow = document.createElement( 'tr' );
377 410 [ 'key', 'outside', 'inside', 'showDesktop' ].forEach( function( col ) {
@@ -387,13 +420,13 @@
387 420 ( section.rows || [] ).forEach( function( row ) {
388 421 var tr = document.createElement( 'tr' );
389 422
390 423 var keyCell = document.createElement( 'td' );
391 - keyCell.className = 'desktop-mode-shortcuts-popover__key-cell';
424 + keyCell.className = 'os-shortcuts-popover__key-cell';
392 425 keyCell.appendChild( renderKeys( row.keys || [] ) );
393 426 if ( row.note ) {
394 427 var note = document.createElement( 'span' );
395 - note.className = 'desktop-mode-shortcuts-popover__note';
428 + note.className = 'os-shortcuts-popover__note';
396 429 note.textContent = ' ' + row.note;
397 430 keyCell.appendChild( note );
398 431 }
399 432 tr.appendChild( keyCell );
@@ -413,25 +446,25 @@
413 446 }
414 447
415 448 function buildShortcutsList( section ) {
416 449 var wrap = document.createElement( 'section' );
417 - wrap.className = 'desktop-mode-shortcuts-popover__section';
450 + wrap.className = 'os-shortcuts-popover__section';
418 451
419 452 if ( section.heading ) {
420 453 var h = document.createElement( 'h3' );
421 - h.className = 'desktop-mode-shortcuts-popover__heading';
454 + h.className = 'os-shortcuts-popover__heading';
422 455 h.textContent = section.heading;
423 456 wrap.appendChild( h );
424 457 }
425 458
426 459 var list = document.createElement( 'ul' );
427 - list.className = 'desktop-mode-shortcuts-popover__list';
460 + list.className = 'os-shortcuts-popover__list';
428 461 ( section.items || [] ).forEach( function( item ) {
429 462 var li = document.createElement( 'li' );
430 - li.className = 'desktop-mode-shortcuts-popover__item';
463 + li.className = 'os-shortcuts-popover__item';
431 464 li.appendChild( renderKeys( item.keys || [] ) );
432 465 var desc = document.createElement( 'span' );
433 - desc.className = 'desktop-mode-shortcuts-popover__description';
466 + desc.className = 'os-shortcuts-popover__description';
434 467 desc.textContent = item.description || '';
435 468 li.appendChild( desc );
436 469 list.appendChild( li );
437 470 } );
@@ -441,19 +474,19 @@
441 474 }
442 475
443 476 function renderKeys( keys ) {
444 477 var span = document.createElement( 'span' );
445 - span.className = 'desktop-mode-shortcuts-popover__keys';
478 + span.className = 'os-shortcuts-popover__keys';
446 479 keys.forEach( function( key, idx ) {
447 480 if ( idx > 0 ) {
448 481 var plus = document.createElement( 'span' );
449 - plus.className = 'desktop-mode-shortcuts-popover__plus';
482 + plus.className = 'os-shortcuts-popover__plus';
450 483 plus.textContent = '+';
451 484 plus.setAttribute( 'aria-hidden', 'true' );
452 485 span.appendChild( plus );
453 486 }
454 487 var kbd = document.createElement( 'kbd' );
455 - kbd.className = 'desktop-mode-shortcuts-popover__kbd';
488 + kbd.className = 'os-shortcuts-popover__kbd';
456 489 kbd.textContent = key;
457 490 span.appendChild( kbd );
458 491 } );
459 492 return span;