| @@ -20,8 +20,84 @@ | ||
| 20 | 20 | <head> |
| 21 | 21 | <meta charset="UTF-8"> |
| 22 | 22 | <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| 23 | 23 | <link rel='shortcut icon' href='<?php echo esc_url($pxlbsadminify_favicon) ?>' type='image/x-icon' /> |
| 24 | + <script id="frame-adminify-app--guard"> | |
| 25 | + /* | |
| 26 | + * Adminify serves the admin inside an iframe. The inner document is normally | |
| 27 | + * recognised server-side through the `Sec-Fetch-Dest: iframe` request header | |
| 28 | + * (Utils::is_iframe()). Setups that serve WordPress from a Service Worker - | |
| 29 | + * WordPress Playground, offline/proxy installs - never hand that header to | |
| 30 | + * PHP, because the browser adds it below the Service Worker layer. The inner | |
| 31 | + * request is then treated as a top-level one and this whole shell (sidebar, | |
| 32 | + * toolbar and yet another iframe) is printed a second time inside the frame: | |
| 33 | + * the duplicated menu. | |
| 34 | + * | |
| 35 | + * Detect the situation on the client, where `window.frameElement` always | |
| 36 | + * tells the truth, and turn this document back into a plain framed admin | |
| 37 | + * page: drop the shell root before the bundle can mount into it and apply | |
| 38 | + * the same rules `assets/admin/css/frame.css` would have applied. | |
| 39 | + */ | |
| 40 | + (function () { | |
| 41 | + var frame; | |
| 42 | + | |
| 43 | + try { | |
| 44 | + frame = window.frameElement; | |
| 45 | + } catch (e) { | |
| 46 | + return; // Cross-origin parent - not an Adminify frame. | |
| 47 | + } | |
| 48 | + | |
| 49 | + // Only the iframe Adminify itself creates. Any other embedding (the | |
| 50 | + // Playground viewport hosting the whole admin, for one) must keep the | |
| 51 | + // shell, so an id check - not `window.self !== window.top`. | |
| 52 | + if (!frame || frame.id !== 'frame-adminify-app--iframe') { | |
| 53 | + return; | |
| 54 | + } | |
| 55 | + | |
| 56 | + window.PXLBSADMINIFY_FRAME_FALLBACK = true; | |
| 57 | + document.documentElement.setAttribute('frame-adminify-iframe', 'true'); | |
| 58 | + | |
| 59 | + // WordPress prints its own <html> tag further down the response and the | |
| 60 | + // parser merges `frame-adminify-app="true"` onto this element, which | |
| 61 | + // pulls in admin.css's "hide the real admin" rules. Two attribute | |
| 62 | + // selectors outrank those single-attribute rules whatever the load order. | |
| 63 | + var css = 'html[frame-adminify-iframe="true"] { padding-top: 0 !important; }' | |
| 64 | + + 'html[frame-adminify-iframe="true"][frame-adminify-app="true"] #adminmenumain,' | |
| 65 | + + 'html[frame-adminify-iframe="true"][frame-adminify-app="true"] #wpadminbar,' | |
| 66 | + + 'html[frame-adminify-iframe="true"] #frame-adminify-app { display: none !important; }' | |
| 67 | + + 'html[frame-adminify-iframe="true"][frame-adminify-app="true"] #wpcontent,' | |
| 68 | + + 'html[frame-adminify-iframe="true"][frame-adminify-app="true"] #wpfooter {' | |
| 69 | + + 'display: block !important; margin-left: 0 !important; margin-right: 0 !important; }'; | |
| 70 | + | |
| 71 | + var style = document.createElement('style'); | |
| 72 | + style.id = 'frame-adminify-app--guard-style'; | |
| 73 | + style.appendChild(document.createTextNode(css)); | |
| 74 | + document.head.appendChild(style); | |
| 75 | + | |
| 76 | + // Remove the mount point as soon as the parser inserts it, so the shell | |
| 77 | + // bundle - which loads in the footer, long before DOMContentLoaded - | |
| 78 | + // finds nothing to mount and never creates a nested iframe. | |
| 79 | + var dropShellRoot = function () { | |
| 80 | + var root = document.getElementById('frame-adminify-app'); | |
| 81 | + if (!root) { | |
| 82 | + return false; | |
| 83 | + } | |
| 84 | + root.parentNode.removeChild(root); | |
| 85 | + return true; | |
| 86 | + }; | |
| 87 | + | |
| 88 | + if (window.MutationObserver) { | |
| 89 | + var observer = new MutationObserver(function () { | |
| 90 | + if (dropShellRoot()) { | |
| 91 | + observer.disconnect(); | |
| 92 | + } | |
| 93 | + }); | |
| 94 | + observer.observe(document.documentElement, { childList: true, subtree: true }); | |
| 95 | + } | |
| 96 | + | |
| 97 | + document.addEventListener('DOMContentLoaded', dropShellRoot); | |
| 98 | + })(); | |
| 99 | + </script> | |
| 24 | 100 | </head> |
| 25 | 101 | <body> |
| 26 | 102 | <div id="frame-adminify-app" class="frame-adminify-app"></div> |
| 27 | 103 | </body> |