PluginProbe
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin / 0.9.8
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin v0.9.8
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 0.8.7 All 34 releases
desktop-mode / includes / window-chrome.php

window-chrome.php in OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin 0.9.8, at includes/window-chrome.php

915 lines 25.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Window-chrome customization framework — server-side registration APIs.
4 *
5 * Mirrors the commands / settings-tabs / title-bar-buttons registration
6 * pattern across four surfaces:
7 *
8 * - **Themes** (Layer 1) — per-window CSS-variable maps.
9 * `desktop_mode_register_window_theme_script()` /
10 * `desktop_mode_register_window_theme()`.
11 *
12 * - **Controls** (Layer 2) — title-bar buttons (close / minimize /
13 * maximize plus plugin custom controls).
14 * `desktop_mode_register_window_control_script()` /
15 * `desktop_mode_register_window_control()`.
16 *
17 * - **Slots** (Layer 3) — named title-bar regions plugins can
18 * replace (icon, title, before-controls, …).
19 * `desktop_mode_register_window_slot_script()` /
20 * `desktop_mode_register_window_slot()`.
21 *
22 * - **Chrome** (Layer 4, Experimental) — full title-bar render
23 * replacement.
24 * `desktop_mode_register_window_chrome_script()` /
25 * `desktop_mode_register_window_chrome()`.
26 *
27 * Each surface contributes a `serverWindow*Scripts` and (optionally)
28 * `serverWindow*` array to the shell payload, consumed by the matching
29 * sync module under `src/window-chrome/{themes,controls,slots,chrome}/server-sync.ts`.
30 *
31 * @package WPDesktopMode
32 */
33
34 defined( 'ABSPATH' ) || exit;
35
36 /* ============================================================
37 * Layer 1 — Themes
38 * ============================================================ */
39
40 /**
41 * Declare a WP-registered script handle as a window-theme provider.
42 *
43 * The script's JS calls `wp.desktop.registerWindowTheme( { id, tokens,
44 * match, owner } )` as usual. Plugins that pass `owner` matching this
45 * handle get live unregister on deactivation.
46 *
47 * @param string $handle WP-registered script handle.
48 * @return true|WP_Error `true` on success; `WP_Error` on validation failure.
49 */
50 function desktop_mode_register_window_theme_script( $handle ) {
51 $handle = (string) $handle;
52 if ( '' === $handle ) {
53 return desktop_mode_registration_error(
54 'desktop_mode_missing_handle',
55 __( 'Window theme script registration requires a non-empty script handle.', 'desktop-mode' )
56 );
57 }
58
59 desktop_mode_window_theme_script_registry( $handle, true );
60
61 /**
62 * Fires after a desktop window-theme script handle is registered.
63 *
64 * @param string $handle The registered script handle.
65 */
66 do_action( 'desktop_mode_window_theme_script_registered', $handle );
67
68 return true;
69 }
70
71 /**
72 * Declare a window theme server-side. Optional companion to
73 * `desktop_mode_register_window_theme_script()` for plugins that want
74 * to ship a tokens map without writing JS — designers can hand off
75 * a single PHP-array of CSS variables and call it done.
76 *
77 * @param array $args {
78 * @type string $id Unique theme id (`vendor/sub-id`). Required.
79 * @type string $label Human-readable label. Default empty.
80 * @type array $tokens CSS-variable map (keys must start with `--`). Required.
81 * @type int $priority Override priority (higher wins). Default 100.
82 * @type string $script Optional script handle (also registers it).
83 * }
84 * @return true|WP_Error
85 */
86 function desktop_mode_register_window_theme( $args = array() ) {
87 $defaults = array(
88 'id' => '',
89 'label' => '',
90 'tokens' => array(),
91 'priority' => 100,
92 'script' => '',
93 );
94 $args = wp_parse_args( $args, $defaults );
95
96 $id = (string) $args['id'];
97 if ( '' === $id ) {
98 return desktop_mode_registration_error(
99 'desktop_mode_missing_id',
100 __( 'Window theme registration requires a non-empty `id`.', 'desktop-mode' )
101 );
102 }
103 if ( ! is_array( $args['tokens'] ) || empty( $args['tokens'] ) ) {
104 return desktop_mode_registration_error(
105 'desktop_mode_missing_tokens',
106 __( 'Window theme registration requires a non-empty `tokens` map.', 'desktop-mode' ),
107 array( 'id' => $id )
108 );
109 }
110 $tokens = array();
111 foreach ( $args['tokens'] as $key => $value ) {
112 $key = (string) $key;
113 if ( '' === $key || 0 !== strpos( $key, '--' ) ) {
114 return desktop_mode_registration_error(
115 'desktop_mode_invalid_token',
116 __( 'Window theme tokens must use CSS custom-property keys (start with "--").', 'desktop-mode' ),
117 array(
118 'id' => $id,
119 'key' => $key,
120 )
121 );
122 }
123 $tokens[ $key ] = (string) $value;
124 }
125
126 $entry = array(
127 'id' => $id,
128 'label' => (string) $args['label'],
129 'tokens' => $tokens,
130 'priority' => (int) $args['priority'],
131 'script' => (string) $args['script'],
132 );
133 desktop_mode_window_theme_registry( $id, $entry );
134
135 if ( '' !== $entry['script'] ) {
136 desktop_mode_window_theme_script_registry( $entry['script'], true );
137 }
138
139 /**
140 * Fires after a desktop window-theme is successfully registered.
141 *
142 * @param string $id The theme id.
143 * @param array $entry The stored registry entry.
144 */
145 do_action( 'desktop_mode_window_theme_registered', $id, $entry );
146
147 return true;
148 }
149
150 /**
151 * Internal module-level registry for theme script handles.
152 *
153 * @internal
154 *
155 * @param string $handle Script handle to read or write.
156 * @param bool|null $value Pass `true` to register; `null` to read only.
157 * @return array|bool When called with no args returns the full store.
158 */
159 function desktop_mode_window_theme_script_registry( $handle = '', $value = null ) {
160 static $store = array();
161
162 if ( '__flush__' === (string) $handle ) {
163 $store = array();
164 return array();
165 }
166 if ( '' === (string) $handle ) {
167 return $store;
168 }
169 if ( null !== $value ) {
170 $store[ (string) $handle ] = (bool) $value;
171 }
172 return isset( $store[ (string) $handle ] ) ? $store[ (string) $handle ] : false;
173 }
174
175 /** Flush the theme-script registry. Tests only. */
176 function desktop_mode_flush_window_theme_script_registry() {
177 desktop_mode_window_theme_script_registry( '__flush__' );
178 }
179
180 /**
181 * Internal module-level registry for window themes.
182 *
183 * @internal
184 *
185 * @param string $id Theme id to read or write.
186 * @param array|null $entry Entry to store, or `null` to read.
187 * @return array|null
188 */
189 function desktop_mode_window_theme_registry( $id = '', $entry = null ) {
190 static $store = array();
191
192 if ( '__flush__' === (string) $id ) {
193 $store = array();
194 return array();
195 }
196 if ( '' === (string) $id ) {
197 return $store;
198 }
199 if ( null !== $entry ) {
200 $store[ (string) $id ] = $entry;
201 }
202 return isset( $store[ (string) $id ] ) ? $store[ (string) $id ] : null;
203 }
204
205 /** Flush the theme registry. Tests only. */
206 function desktop_mode_flush_window_theme_registry() {
207 desktop_mode_window_theme_registry( '__flush__' );
208 }
209
210 /**
211 * Build the theme-script payload. Same shape as
212 * `desktop_mode_build_desktop_command_scripts_payload()`.
213 *
214 * @return array[] List of `{ handle, scriptUrl }` entries.
215 */
216 function desktop_mode_build_window_theme_scripts_payload() {
217 $registry = desktop_mode_window_theme_script_registry();
218 if ( ! is_array( $registry ) || empty( $registry ) ) {
219 return array();
220 }
221
222 $out = array();
223 $seen = array();
224 foreach ( $registry as $handle => $active ) {
225 if ( ! $active || isset( $seen[ $handle ] ) ) {
226 continue;
227 }
228 $payload = desktop_mode_resolve_script_payload( $handle );
229 if ( '' === $payload['url'] ) {
230 desktop_mode_warn_unresolvable_script_handle(
231 'desktop_mode_register_window_theme_script',
232 'Window theme',
233 (string) $handle
234 );
235 continue;
236 }
237 $out[] = array(
238 'handle' => (string) $handle,
239 'scriptUrl' => $payload['url'],
240 'scriptBefore' => $payload['before'],
241 'scriptAfter' => $payload['after'],
242 'scriptL10n' => $payload['l10n'],
243 'scriptTranslations' => $payload['translations'],
244 );
245 $seen[ $handle ] = true;
246 }
247 return $out;
248 }
249
250 /**
251 * Build the theme-metadata payload. Resolves the optional companion
252 * script handle for each entry.
253 *
254 * @return array[]
255 */
256 function desktop_mode_build_window_themes_payload() {
257 $registry = desktop_mode_window_theme_registry();
258 if ( ! is_array( $registry ) || empty( $registry ) ) {
259 return array();
260 }
261
262 $out = array();
263 foreach ( $registry as $entry ) {
264 $handle = (string) $entry['script'];
265 $payload = '' !== $handle
266 ? desktop_mode_resolve_script_payload( $handle )
267 : array( 'url' => '', 'before' => array(), 'after' => array(), 'l10n' => array(), 'translations' => '' );
268 $out[] = array(
269 'id' => (string) $entry['id'],
270 'label' => (string) $entry['label'],
271 'tokens' => (array) $entry['tokens'],
272 'priority' => (int) $entry['priority'],
273 'scriptUrl' => $payload['url'],
274 'scriptHandle' => $handle,
275 'scriptBefore' => $payload['before'],
276 'scriptAfter' => $payload['after'],
277 'scriptL10n' => $payload['l10n'],
278 'scriptTranslations' => $payload['translations'],
279 );
280 }
281 return $out;
282 }
283
284 /* ============================================================
285 * Layer 2 — Controls
286 * ============================================================ */
287
288 /**
289 * Declare a WP-registered script handle as a window-control provider.
290 *
291 * @param string $handle WP-registered script handle.
292 * @return true|WP_Error
293 */
294 function desktop_mode_register_window_control_script( $handle ) {
295 $handle = (string) $handle;
296 if ( '' === $handle ) {
297 return desktop_mode_registration_error(
298 'desktop_mode_missing_handle',
299 __( 'Window control script registration requires a non-empty script handle.', 'desktop-mode' )
300 );
301 }
302
303 desktop_mode_window_control_script_registry( $handle, true );
304
305 /**
306 * Fires after a window-control script handle is registered.
307 *
308 * @param string $handle The registered script handle.
309 */
310 do_action( 'desktop_mode_window_control_script_registered', $handle );
311
312 return true;
313 }
314
315 /**
316 * Declare a window control server-side. The control's `onClick` /
317 * `render` callback always lives JS-side.
318 *
319 * @param array $args {
320 * @type string $id Required, `vendor/sub-id`.
321 * @type string $label Required.
322 * @type string $icon Dashicons class / built-in key / SVG.
323 * @type string $placement `'left' | 'right' | 'controls'`. Default `'left'`.
324 * @type int $order Sort within placement. Default 100.
325 * @type string $script Optional companion script handle.
326 * }
327 * @return true|WP_Error
328 */
329 function desktop_mode_register_window_control( $args = array() ) {
330 $defaults = array(
331 'id' => '',
332 'label' => '',
333 'icon' => '',
334 'placement' => 'left',
335 'order' => 100,
336 'script' => '',
337 );
338 $args = wp_parse_args( $args, $defaults );
339
340 $id = (string) $args['id'];
341 if ( '' === $id ) {
342 return desktop_mode_registration_error(
343 'desktop_mode_missing_id',
344 __( 'Window control registration requires a non-empty `id`.', 'desktop-mode' )
345 );
346 }
347 if ( '' === (string) $args['label'] ) {
348 return desktop_mode_registration_error(
349 'desktop_mode_missing_label',
350 __( 'Window control registration requires a non-empty `label`.', 'desktop-mode' ),
351 array( 'id' => $id )
352 );
353 }
354 $placement = (string) $args['placement'];
355 if ( ! in_array( $placement, array( 'left', 'right', 'controls' ), true ) ) {
356 return desktop_mode_registration_error(
357 'desktop_mode_invalid_placement',
358 __( 'Window control `placement` must be one of "left", "right", "controls".', 'desktop-mode' ),
359 array(
360 'id' => $id,
361 'placement' => $placement,
362 )
363 );
364 }
365
366 $entry = array(
367 'id' => $id,
368 'label' => (string) $args['label'],
369 'icon' => (string) $args['icon'],
370 'placement' => $placement,
371 'order' => (int) $args['order'],
372 'script' => (string) $args['script'],
373 );
374 desktop_mode_window_control_registry( $id, $entry );
375
376 if ( '' !== $entry['script'] ) {
377 desktop_mode_window_control_script_registry( $entry['script'], true );
378 }
379
380 /**
381 * Fires after a window-control is successfully registered.
382 *
383 * @param string $id The control id.
384 * @param array $entry The stored registry entry.
385 */
386 do_action( 'desktop_mode_window_control_registered', $id, $entry );
387
388 return true;
389 }
390
391 /** @internal */
392 function desktop_mode_window_control_script_registry( $handle = '', $value = null ) {
393 static $store = array();
394
395 if ( '__flush__' === (string) $handle ) {
396 $store = array();
397 return array();
398 }
399 if ( '' === (string) $handle ) {
400 return $store;
401 }
402 if ( null !== $value ) {
403 $store[ (string) $handle ] = (bool) $value;
404 }
405 return isset( $store[ (string) $handle ] ) ? $store[ (string) $handle ] : false;
406 }
407
408 /** Tests only. */
409 function desktop_mode_flush_window_control_script_registry() {
410 desktop_mode_window_control_script_registry( '__flush__' );
411 }
412
413 /** @internal */
414 function desktop_mode_window_control_registry( $id = '', $entry = null ) {
415 static $store = array();
416
417 if ( '__flush__' === (string) $id ) {
418 $store = array();
419 return array();
420 }
421 if ( '' === (string) $id ) {
422 return $store;
423 }
424 if ( null !== $entry ) {
425 $store[ (string) $id ] = $entry;
426 }
427 return isset( $store[ (string) $id ] ) ? $store[ (string) $id ] : null;
428 }
429
430 /** Tests only. */
431 function desktop_mode_flush_window_control_registry() {
432 desktop_mode_window_control_registry( '__flush__' );
433 }
434
435 /**
436 * @return array[] List of `{ handle, scriptUrl }` entries.
437 */
438 function desktop_mode_build_window_control_scripts_payload() {
439 $registry = desktop_mode_window_control_script_registry();
440 if ( ! is_array( $registry ) || empty( $registry ) ) {
441 return array();
442 }
443
444 $out = array();
445 $seen = array();
446 foreach ( $registry as $handle => $active ) {
447 if ( ! $active || isset( $seen[ $handle ] ) ) {
448 continue;
449 }
450 $payload = desktop_mode_resolve_script_payload( $handle );
451 if ( '' === $payload['url'] ) {
452 desktop_mode_warn_unresolvable_script_handle(
453 'desktop_mode_register_window_control_script',
454 'Window control',
455 (string) $handle
456 );
457 continue;
458 }
459 $out[] = array(
460 'handle' => (string) $handle,
461 'scriptUrl' => $payload['url'],
462 'scriptBefore' => $payload['before'],
463 'scriptAfter' => $payload['after'],
464 'scriptL10n' => $payload['l10n'],
465 'scriptTranslations' => $payload['translations'],
466 );
467 $seen[ $handle ] = true;
468 }
469 return $out;
470 }
471
472 /**
473 * @return array[]
474 */
475 function desktop_mode_build_window_controls_payload() {
476 $registry = desktop_mode_window_control_registry();
477 if ( ! is_array( $registry ) || empty( $registry ) ) {
478 return array();
479 }
480
481 $out = array();
482 foreach ( $registry as $entry ) {
483 $handle = (string) $entry['script'];
484 $payload = '' !== $handle
485 ? desktop_mode_resolve_script_payload( $handle )
486 : array( 'url' => '', 'before' => array(), 'after' => array(), 'l10n' => array(), 'translations' => '' );
487 $out[] = array(
488 'id' => (string) $entry['id'],
489 'label' => (string) $entry['label'],
490 'icon' => (string) $entry['icon'],
491 'placement' => (string) $entry['placement'],
492 'order' => (int) $entry['order'],
493 'scriptUrl' => $payload['url'],
494 'scriptHandle' => $handle,
495 'scriptBefore' => $payload['before'],
496 'scriptAfter' => $payload['after'],
497 'scriptL10n' => $payload['l10n'],
498 'scriptTranslations' => $payload['translations'],
499 );
500 }
501 return $out;
502 }
503
504 /* ============================================================
505 * Layer 3 — Slots
506 * ============================================================ */
507
508 /**
509 * Canonical slot names. Mirrors the `WindowSlotName` TypeScript
510 * union in `src/types.ts`.
511 *
512 * @return string[]
513 */
514 function desktop_mode_window_slot_names() {
515 return array(
516 'before-titlebar',
517 'before-icon',
518 'icon',
519 'title',
520 'after-title',
521 'before-controls',
522 'controls',
523 'after-controls',
524 'after-titlebar',
525 );
526 }
527
528 /**
529 * @param string $handle WP-registered script handle.
530 * @return true|WP_Error
531 */
532 function desktop_mode_register_window_slot_script( $handle ) {
533 $handle = (string) $handle;
534 if ( '' === $handle ) {
535 return desktop_mode_registration_error(
536 'desktop_mode_missing_handle',
537 __( 'Window slot script registration requires a non-empty script handle.', 'desktop-mode' )
538 );
539 }
540
541 desktop_mode_window_slot_script_registry( $handle, true );
542
543 /**
544 * Fires after a window-slot script handle is registered.
545 *
546 * @param string $handle The registered script handle.
547 */
548 do_action( 'desktop_mode_window_slot_script_registered', $handle );
549
550 return true;
551 }
552
553 /**
554 * @param array $args {
555 * @type string $id Required.
556 * @type string $slot Required, one of {@see desktop_mode_window_slot_names()}.
557 * @type int $order Default 100.
558 * @type string $script Optional script handle.
559 * }
560 * @return true|WP_Error
561 */
562 function desktop_mode_register_window_slot( $args = array() ) {
563 $defaults = array(
564 'id' => '',
565 'slot' => '',
566 'order' => 100,
567 'script' => '',
568 );
569 $args = wp_parse_args( $args, $defaults );
570
571 $id = (string) $args['id'];
572 if ( '' === $id ) {
573 return desktop_mode_registration_error(
574 'desktop_mode_missing_id',
575 __( 'Window slot registration requires a non-empty `id`.', 'desktop-mode' )
576 );
577 }
578 $slot = (string) $args['slot'];
579 if ( ! in_array( $slot, desktop_mode_window_slot_names(), true ) ) {
580 return desktop_mode_registration_error(
581 'desktop_mode_invalid_slot',
582 __( 'Window slot registration requires a known `slot` name.', 'desktop-mode' ),
583 array(
584 'id' => $id,
585 'slot' => $slot,
586 )
587 );
588 }
589
590 $entry = array(
591 'id' => $id,
592 'slot' => $slot,
593 'order' => (int) $args['order'],
594 'script' => (string) $args['script'],
595 );
596 desktop_mode_window_slot_registry( $id, $entry );
597
598 if ( '' !== $entry['script'] ) {
599 desktop_mode_window_slot_script_registry( $entry['script'], true );
600 }
601
602 /**
603 * Fires after a window-slot is successfully registered.
604 *
605 * @param string $id The slot-renderer id.
606 * @param array $entry The stored registry entry.
607 */
608 do_action( 'desktop_mode_window_slot_registered', $id, $entry );
609
610 return true;
611 }
612
613 /** @internal */
614 function desktop_mode_window_slot_script_registry( $handle = '', $value = null ) {
615 static $store = array();
616
617 if ( '__flush__' === (string) $handle ) {
618 $store = array();
619 return array();
620 }
621 if ( '' === (string) $handle ) {
622 return $store;
623 }
624 if ( null !== $value ) {
625 $store[ (string) $handle ] = (bool) $value;
626 }
627 return isset( $store[ (string) $handle ] ) ? $store[ (string) $handle ] : false;
628 }
629
630 /** Tests only. */
631 function desktop_mode_flush_window_slot_script_registry() {
632 desktop_mode_window_slot_script_registry( '__flush__' );
633 }
634
635 /** @internal */
636 function desktop_mode_window_slot_registry( $id = '', $entry = null ) {
637 static $store = array();
638
639 if ( '__flush__' === (string) $id ) {
640 $store = array();
641 return array();
642 }
643 if ( '' === (string) $id ) {
644 return $store;
645 }
646 if ( null !== $entry ) {
647 $store[ (string) $id ] = $entry;
648 }
649 return isset( $store[ (string) $id ] ) ? $store[ (string) $id ] : null;
650 }
651
652 /** Tests only. */
653 function desktop_mode_flush_window_slot_registry() {
654 desktop_mode_window_slot_registry( '__flush__' );
655 }
656
657 /**
658 * @return array[] List of `{ handle, scriptUrl }` entries.
659 */
660 function desktop_mode_build_window_slot_scripts_payload() {
661 $registry = desktop_mode_window_slot_script_registry();
662 if ( ! is_array( $registry ) || empty( $registry ) ) {
663 return array();
664 }
665
666 $out = array();
667 $seen = array();
668 foreach ( $registry as $handle => $active ) {
669 if ( ! $active || isset( $seen[ $handle ] ) ) {
670 continue;
671 }
672 $payload = desktop_mode_resolve_script_payload( $handle );
673 if ( '' === $payload['url'] ) {
674 desktop_mode_warn_unresolvable_script_handle(
675 'desktop_mode_register_window_slot_script',
676 'Window slot',
677 (string) $handle
678 );
679 continue;
680 }
681 $out[] = array(
682 'handle' => (string) $handle,
683 'scriptUrl' => $payload['url'],
684 'scriptBefore' => $payload['before'],
685 'scriptAfter' => $payload['after'],
686 'scriptL10n' => $payload['l10n'],
687 'scriptTranslations' => $payload['translations'],
688 );
689 $seen[ $handle ] = true;
690 }
691 return $out;
692 }
693
694 /**
695 * @return array[]
696 */
697 function desktop_mode_build_window_slots_payload() {
698 $registry = desktop_mode_window_slot_registry();
699 if ( ! is_array( $registry ) || empty( $registry ) ) {
700 return array();
701 }
702
703 $out = array();
704 foreach ( $registry as $entry ) {
705 $handle = (string) $entry['script'];
706 $payload = '' !== $handle
707 ? desktop_mode_resolve_script_payload( $handle )
708 : array( 'url' => '', 'before' => array(), 'after' => array(), 'l10n' => array(), 'translations' => '' );
709 $out[] = array(
710 'id' => (string) $entry['id'],
711 'slot' => (string) $entry['slot'],
712 'order' => (int) $entry['order'],
713 'scriptUrl' => $payload['url'],
714 'scriptHandle' => $handle,
715 'scriptBefore' => $payload['before'],
716 'scriptAfter' => $payload['after'],
717 'scriptL10n' => $payload['l10n'],
718 'scriptTranslations' => $payload['translations'],
719 );
720 }
721 return $out;
722 }
723
724 /* ============================================================
725 * Layer 4 — Custom chrome (Experimental)
726 * ============================================================ */
727
728 /**
729 * Declare a WP-registered script handle as a window-chrome provider.
730 *
731 * **Experimental** — chrome render contract may change.
732 *
733 * @param string $handle WP-registered script handle.
734 * @return true|WP_Error
735 */
736 function desktop_mode_register_window_chrome_script( $handle ) {
737 $handle = (string) $handle;
738 if ( '' === $handle ) {
739 return desktop_mode_registration_error(
740 'desktop_mode_missing_handle',
741 __( 'Window chrome script registration requires a non-empty script handle.', 'desktop-mode' )
742 );
743 }
744
745 desktop_mode_window_chrome_script_registry( $handle, true );
746
747 /**
748 * Fires after a window-chrome script handle is registered.
749 *
750 * @param string $handle The registered script handle.
751 */
752 do_action( 'desktop_mode_window_chrome_script_registered', $handle );
753
754 return true;
755 }
756
757 /**
758 * Declare a custom chrome server-side. **Experimental** — chrome
759 * render contract may change.
760 *
761 * @param array $args {
762 * @type string $id Required.
763 * @type string $label Default empty.
764 * @type string $script Optional script handle.
765 * }
766 * @return true|WP_Error
767 */
768 function desktop_mode_register_window_chrome( $args = array() ) {
769 $defaults = array(
770 'id' => '',
771 'label' => '',
772 'script' => '',
773 );
774 $args = wp_parse_args( $args, $defaults );
775
776 $id = (string) $args['id'];
777 if ( '' === $id ) {
778 return desktop_mode_registration_error(
779 'desktop_mode_missing_id',
780 __( 'Window chrome registration requires a non-empty `id`.', 'desktop-mode' )
781 );
782 }
783
784 $entry = array(
785 'id' => $id,
786 'label' => (string) $args['label'],
787 'script' => (string) $args['script'],
788 );
789 desktop_mode_window_chrome_registry( $id, $entry );
790
791 if ( '' !== $entry['script'] ) {
792 desktop_mode_window_chrome_script_registry( $entry['script'], true );
793 }
794
795 /**
796 * Fires after a window-chrome is successfully registered.
797 *
798 * @param string $id The chrome id.
799 * @param array $entry The stored registry entry.
800 */
801 do_action( 'desktop_mode_window_chrome_registered', $id, $entry );
802
803 return true;
804 }
805
806 /** @internal */
807 function desktop_mode_window_chrome_script_registry( $handle = '', $value = null ) {
808 static $store = array();
809
810 if ( '__flush__' === (string) $handle ) {
811 $store = array();
812 return array();
813 }
814 if ( '' === (string) $handle ) {
815 return $store;
816 }
817 if ( null !== $value ) {
818 $store[ (string) $handle ] = (bool) $value;
819 }
820 return isset( $store[ (string) $handle ] ) ? $store[ (string) $handle ] : false;
821 }
822
823 /** Tests only. */
824 function desktop_mode_flush_window_chrome_script_registry() {
825 desktop_mode_window_chrome_script_registry( '__flush__' );
826 }
827
828 /** @internal */
829 function desktop_mode_window_chrome_registry( $id = '', $entry = null ) {
830 static $store = array();
831
832 if ( '__flush__' === (string) $id ) {
833 $store = array();
834 return array();
835 }
836 if ( '' === (string) $id ) {
837 return $store;
838 }
839 if ( null !== $entry ) {
840 $store[ (string) $id ] = $entry;
841 }
842 return isset( $store[ (string) $id ] ) ? $store[ (string) $id ] : null;
843 }
844
845 /** Tests only. */
846 function desktop_mode_flush_window_chrome_registry() {
847 desktop_mode_window_chrome_registry( '__flush__' );
848 }
849
850 /**
851 * @return array[] List of `{ handle, scriptUrl }` entries.
852 */
853 function desktop_mode_build_window_chrome_scripts_payload() {
854 $registry = desktop_mode_window_chrome_script_registry();
855 if ( ! is_array( $registry ) || empty( $registry ) ) {
856 return array();
857 }
858
859 $out = array();
860 $seen = array();
861 foreach ( $registry as $handle => $active ) {
862 if ( ! $active || isset( $seen[ $handle ] ) ) {
863 continue;
864 }
865 $payload = desktop_mode_resolve_script_payload( $handle );
866 if ( '' === $payload['url'] ) {
867 desktop_mode_warn_unresolvable_script_handle(
868 'desktop_mode_register_window_chrome_script',
869 'Window chrome',
870 (string) $handle
871 );
872 continue;
873 }
874 $out[] = array(
875 'handle' => (string) $handle,
876 'scriptUrl' => $payload['url'],
877 'scriptBefore' => $payload['before'],
878 'scriptAfter' => $payload['after'],
879 'scriptL10n' => $payload['l10n'],
880 'scriptTranslations' => $payload['translations'],
881 );
882 $seen[ $handle ] = true;
883 }
884 return $out;
885 }
886
887 /**
888 * @return array[]
889 */
890 function desktop_mode_build_window_chromes_payload() {
891 $registry = desktop_mode_window_chrome_registry();
892 if ( ! is_array( $registry ) || empty( $registry ) ) {
893 return array();
894 }
895
896 $out = array();
897 foreach ( $registry as $entry ) {
898 $handle = (string) $entry['script'];
899 $payload = '' !== $handle
900 ? desktop_mode_resolve_script_payload( $handle )
901 : array( 'url' => '', 'before' => array(), 'after' => array(), 'l10n' => array(), 'translations' => '' );
902 $out[] = array(
903 'id' => (string) $entry['id'],
904 'label' => (string) $entry['label'],
905 'scriptUrl' => $payload['url'],
906 'scriptHandle' => $handle,
907 'scriptBefore' => $payload['before'],
908 'scriptAfter' => $payload['after'],
909 'scriptL10n' => $payload['l10n'],
910 'scriptTranslations' => $payload['translations'],
911 );
912 }
913 return $out;
914 }
915