Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /**
- * Oxygen Gutenberg Integration - iframed canvas fix.
- *
- * Oxygen Gutenberg Integration 1.4.6 predates the iframed block-editor canvas.
- * It binds its contenteditable/click handlers to the top document, and it feeds
- * its CSS in via absolute-URL add_editor_style() calls plus an inline <style>
- * in the admin footer. Since the canvas became an iframe, none of that reaches
- * the block. This restores both halves against the canvas document.
- */
- /* -------------------------------------------------------------------------
- * 1. CSS - hand Oxygen's stylesheets to the editor directly.
- *
- * Absolute-URL editor styles are fetched by WordPress over a loopback HTTP
- * request (get_block_editor_theme_styles()), which fails silently on local /
- * self-signed setups. Per-block CSS is never registered as an editor style at
- * all. Read both off disk instead.
- * ---------------------------------------------------------------------- */
- add_filter( 'block_editor_settings_all', function ( $settings, $context ) {
- if ( ! class_exists( 'Oxygen_Gutenberg' ) || ! defined( 'CT_FW_URI' ) ) {
- return $settings;
- }
- // Post editor only - the site editor has no Oxygen blocks.
- if ( empty( $context->post ) ) {
- return $settings;
- }
- $files = array();
- // Component framework base stylesheet.
- $files[] = str_replace(
- trailingslashit( plugins_url() ),
- trailingslashit( WP_PLUGIN_DIR ),
- CT_FW_URI
- ) . '/oxygen.css';
- $upload = wp_upload_dir();
- $css_dir = trailingslashit( $upload['basedir'] ) . 'oxygen/css/';
- // Oxygen's generated universal stylesheet (flex containers, sections...).
- $files[] = $css_dir . 'universal.css';
- // Per-post stylesheet for every post registered as a block, so any block
- // that can be inserted is styled once inserted.
- $block_ids = get_posts( array(
- 'post_type' => 'oxy_user_library',
- 'post_status' => 'publish',
- 'posts_per_page' => -1,
- 'fields' => 'ids',
- ) );
- $full_page_ids = get_posts( array(
- 'post_type' => 'page',
- 'post_status' => 'publish',
- 'posts_per_page' => -1,
- 'fields' => 'ids',
- 'meta_key' => '_ct_oxygenberg_full_page_block',
- 'meta_value' => '1',
- ) );
- foreach ( array_merge( $block_ids, $full_page_ids ) as $id ) {
- $files[] = $css_dir . $id . '.css';
- }
- $css = '';
- foreach ( $files as $file ) {
- if ( is_readable( $file ) ) {
- $css .= file_get_contents( $file ) . "\n";
- }
- }
- if ( '' === trim( $css ) ) {
- return $settings;
- }
- if ( ! isset( $settings['styles'] ) || ! is_array( $settings['styles'] ) ) {
- $settings['styles'] = array();
- }
- $settings['styles'][] = array(
- 'css' => $css,
- '__unstableType' => 'theme',
- 'isGlobalStyles' => false,
- );
- return $settings;
- }, 10, 2 );
- /* -------------------------------------------------------------------------
- * 2. Behaviour - re-implement in-canvas editing against the iframe document,
- * and mirror Oxygen's webfonts into it.
- * ---------------------------------------------------------------------- */
- add_action( 'admin_enqueue_scripts', function () {
- if ( ! wp_script_is( 'oxygen-vsb-gutenberg-blocks-generator', 'enqueued' ) ) {
- return;
- }
- $js = <<<'JS'
- ( function () {
- if ( ! window.wp || ! wp.data || ! wp.data.select ) { return; }
- if ( window.__oxyCanvasFixLoaded ) { return; }
- window.__oxyCanvasFixLoaded = true;
- // Oxygen attribute keys are "<elementId>_<type>". Only these types are
- // edited in the canvas; link/icon/alt/background come from the sidebar,
- // which lives in the top document and still works.
- var CANVAS_TYPES = [ 'string', 'richtext', 'image' ];
- var stats = { docs: 0, candidates: 0, resolved: 0, bound: 0, unresolved: [] };
- function canvasDocument() {
- var frame = document.querySelector( 'iframe[name="editor-canvas"]' );
- if ( ! frame ) { return null; }
- try {
- var doc = frame.contentDocument;
- return ( doc && doc.body ) ? doc : null;
- } catch ( e ) {
- return null;
- }
- }
- function getBlock( clientId ) {
- return wp.data.select( 'core/block-editor' ).getBlock( clientId );
- }
- // The integration rewrites the "oxygenberg-<selector>" class to
- // "oxygenberg-<clientId>" when it renders the block, so the class list
- // tells us which block instance an element belongs to.
- function resolve( el ) {
- var elementId = el.getAttribute( 'id' );
- if ( ! elementId ) { return null; }
- for ( var i = 0; i < el.classList.length; i++ ) {
- var cls = el.classList[ i ];
- if ( cls === 'oxygenberg-element' || cls.indexOf( 'oxygenberg-' ) !== 0 ) { continue; }
- var clientId = cls.slice( 'oxygenberg-'.length );
- var block = getBlock( clientId );
- if ( ! block || ! block.attributes ) { continue; }
- for ( var t = 0; t < CANVAS_TYPES.length; t++ ) {
- var key = elementId + '_' + CANVAS_TYPES[ t ];
- if ( Object.prototype.hasOwnProperty.call( block.attributes, key ) ) {
- return { clientId: clientId, key: key, type: CANVAS_TYPES[ t ] };
- }
- }
- }
- return null;
- }
- function makeTextEditable( el, info ) {
- // Mirrors the integration's own guard: skip elements that still hold
- // un-substituted markers for nested editable elements.
- if ( el.innerHTML.indexOf( '<oxygenberg' ) !== -1 ) { return; }
- el.setAttribute( 'contenteditable', 'true' );
- el.style.cursor = 'text';
- el.style.pointerEvents = 'auto';
- el.style.webkitUserSelect = 'text';
- el.style.userSelect = 'text';
- // The block editor's own mousedown handler selects the block and can
- // preventDefault, which stops the browser placing a caret inside our
- // contenteditable. Take the event, then select the block ourselves so
- // the sidebar still follows along.
- el.addEventListener( 'mousedown', function ( e ) {
- e.stopPropagation();
- wp.data.dispatch( 'core/block-editor' ).selectBlock( info.clientId );
- if ( el.ownerDocument.activeElement !== el ) { el.focus(); }
- } );
- el.addEventListener( 'blur', function () {
- var attrs = {};
- attrs[ info.key ] = el.innerHTML;
- wp.data.dispatch( 'core/block-editor' ).updateBlockAttributes( info.clientId, attrs );
- } );
- // Keep Backspace/Delete/Enter from reaching the block editor, which
- // would otherwise remove or split the block while you type in it.
- el.addEventListener( 'keydown', function ( e ) {
- if ( e.key === 'Backspace' || e.key === 'Delete' || e.key === 'Enter' ) {
- e.stopPropagation();
- }
- } );
- // Paste as plain text, as the integration does on non-iframed canvases.
- el.addEventListener( 'paste', function ( e ) {
- e.preventDefault();
- var text = ( e.clipboardData || window.clipboardData ).getData( 'text' );
- el.ownerDocument.execCommand( 'insertHTML', false, text );
- } );
- }
- function makeImageEditable( el, info ) {
- if ( ! wp.media ) { return; }
- el.removeAttribute( 'srcset' );
- el.style.cursor = 'pointer';
- el.title = 'Click to change the image';
- el.addEventListener( 'click', function ( e ) {
- e.preventDefault();
- e.stopPropagation();
- var frame = wp.media( {
- title: 'Select a replacement image',
- button: { text: 'Use this image' },
- multiple: false
- } );
- frame.on( 'select', function () {
- var attachment = frame.state().get( 'selection' ).first().toJSON();
- var attrs = {};
- attrs[ info.key ] = attachment.url;
- // Keep the matching alt attribute in sync when there is one.
- var altKey = info.key.slice( 0, -'image'.length ) + 'alt';
- var block = getBlock( info.clientId );
- if ( block && Object.prototype.hasOwnProperty.call( block.attributes, altKey ) ) {
- attrs[ altKey ] = attachment.alt || '';
- }
- wp.data.dispatch( 'core/block-editor' ).updateBlockAttributes( info.clientId, attrs );
- } );
- frame.open();
- } );
- }
- function mirrorFonts( doc ) {
- // Oxygen prints its Google Fonts <link>s into the admin footer of the
- // top document, where the canvas cannot see them.
- var sources = document.querySelectorAll( 'link[rel="stylesheet"][href*="fonts.googleapis.com"]' );
- for ( var i = 0; i < sources.length; i++ ) {
- var href = sources[ i ].getAttribute( 'href' );
- if ( ! href ) { continue; }
- if ( doc.querySelector( 'link[href="' + href.replace( /"/g, '\\"' ) + '"]' ) ) { continue; }
- var link = doc.createElement( 'link' );
- link.rel = 'stylesheet';
- link.href = href;
- doc.head.appendChild( link );
- }
- }
- function enhance( doc ) {
- if ( ! doc.__oxyLinkGuard ) {
- doc.__oxyLinkGuard = true;
- stats.docs++;
- // Anchors inside Oxygen blocks must not navigate the canvas away.
- doc.addEventListener( 'click', function ( e ) {
- var link = e.target && e.target.closest ? e.target.closest( '.oxygenberg-element a, a.oxygenberg-element' ) : null;
- if ( link ) { e.preventDefault(); }
- }, true );
- }
- var candidates = doc.querySelectorAll( '[class*="oxygenberg-"]' );
- stats.candidates = candidates.length;
- for ( var i = 0; i < candidates.length; i++ ) {
- var el = candidates[ i ];
- if ( el.__oxyCanvasBound ) { continue; }
- var info = resolve( el );
- if ( ! info ) {
- var id = el.getAttribute( 'id' );
- if ( id && stats.unresolved.indexOf( id ) === -1 ) { stats.unresolved.push( id ); }
- continue;
- }
- el.__oxyCanvasBound = true;
- stats.resolved++;
- if ( info.type === 'string' || info.type === 'richtext' ) {
- makeTextEditable( el, info );
- stats.bound++;
- } else if ( info.type === 'image' ) {
- makeImageEditable( el, info );
- stats.bound++;
- }
- }
- }
- // The canvas iframe is created, and its contents replaced, at unpredictable
- // times (block re-renders, device preview switches, editor remounts), so
- // poll rather than hooking a single lifecycle event.
- setInterval( function () {
- var doc = canvasDocument();
- if ( ! doc ) { return; }
- mirrorFonts( doc );
- enhance( doc );
- }, 500 );
- // Type oxyFixReport() in the browser console to see what the shim found.
- window.oxyFixReport = function () {
- var doc = canvasDocument();
- console.log( 'canvas iframe found:', !! doc );
- console.log( 'elements with an oxygenberg- class:', stats.candidates );
- console.log( 'resolved to a block attribute:', stats.resolved );
- console.log( 'made editable:', stats.bound );
- console.log( 'unresolved element ids:', stats.unresolved );
- if ( doc ) {
- console.log( 'contenteditable in canvas:', doc.querySelectorAll( '[contenteditable="true"]' ).length );
- }
- return stats;
- };
- } )();
- JS;
- wp_add_inline_script( 'oxygen-vsb-gutenberg-blocks-generator', $js );
- }, 100 );
Advertisement
Add Comment
Please, Sign In to add comment