├── README.md ├── composer.json ├── inc ├── namespace.php ├── react-dom-server.js ├── ssr.js └── url-search-params.js └── plugin.php /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | 12 | 15 | 16 |
4 | Block Editor SSR
5 | Server Side render your interactive React Gutenberg blocks. 6 |
10 | A Human Made project. Maintained by @joehoyle. 11 | 13 | 14 |
17 | 18 | Block Editor SSR adds support for rendering interactive (front-end React powered) Gutenberg blocks server side. If you want to build custom blocks that render in React on the front end, _and_ then have them also render server-side with hydration, this is the library for you. 19 | 20 | Block Editor SSR is experimental, uses the PHP V8JS module and is not production ready. If that doesn't scare you, read on. 21 | 22 | --- 23 | 24 | Building blocks that will render as a React-app on the front end has many possible architectures and solutions. Block Editor SSR expects blocks to be built in a _certain_ way (the way that made most sense to me). Before detailing how Block Editor SSR will server-render and hydrate your custom React block, first let's go over how building custom blocks in React (front end) is expected to go. 25 | 26 | ## Register the block 27 | 28 | Like all good things in WordPress, it starts with registering something. In this case, a new custom Gutenbern block. This is done in PHP with `register_block_type()`. For example, say I have a block that is a TV Shows directory or something like that: 29 | 30 | ```php 31 | register_block_type( 32 | 'joe/tv-shows', 33 | [ 34 | 'title' => 'TV Shows', 35 | 'category' => 'embed', 36 | 'editor_script' => 'tv-shows-editor', 37 | 'editor_style' => 'tv-shows-editor', 38 | 'script' => 'tv-shows', 39 | 'style' => 'tv-shows', 40 | ] 41 | ); 42 | ``` 43 | 44 | That makes the block exist, and I've set script / style handles for the editor and the front-end. If you were not familier, WordPress will auto-enqueue my `editor_` scripts in Gutenberg, and will auto-enqueue the `script` / `style` on the front-end whenever the block is rendered on a page. 45 | 46 | Of course the scripts / styles also need registering via `wp_register_script` / `wp_enqueue_style`. We'll also assume that the `tv-shows-editor` script has been setup, and registered the block in JavaScrip via `registerBlockType`. 47 | 48 | When it comes to showing the block on the front-end, we'll make use of the `render_callback` argument for `register_block_type`. The objective is to output a placeholder `
` that the React component will render to (via `ReactDOM.render`). The most basic version would be something like: 49 | 50 | 51 | ```php 52 | register_block_type( 53 | 'joe/tv-shows', 54 | [ 55 | ..., 56 | 'render_callback' => function ( array $attributes ) : string { 57 | return '
'; 58 | }, 59 | ] 60 | ); 61 | ``` 62 | 63 | Now when our block is added to a page, we'll have a `
` to render the React component into. 64 | 65 | ## Render the React component 66 | 67 | Building / bundling the React component for the custom block has many options and patterns. I've detailed what I think is the most basic way to get a React build process up and running. This leverages the in-build WordPress scripts for React etc. If you are unfamiliar with `@wordpress/element` and `@wordpress/scripts` it may be a good idea to research those. `@wordpress/scripts` provides a zero-config way to build + bundle JSX / React. Simply create a new `package.json` and add it as a dev dependency. A barebones example to transpile and bundle both the editor script and frontend scripts as seperate bundles: 68 | 69 | 70 | ```json 71 | { 72 | "name": "joe/tv-shows", 73 | "scripts": { 74 | "build": "wp-scripts build ./src/editor.js ./src/frontend.js", 75 | "start": "wp-scripts start ./src/editor.js ./src/frontend.js" 76 | }, 77 | "devDependencies": { 78 | "@wordpress/scripts": "^18.0.1" 79 | } 80 | } 81 | ``` 82 | 83 | That's basically it! `npm run start` will auto-rebuild on file changes. No local dev server etc, keep it simple. The above would mean the editor and frontend scripts are `wp_register_script`ed with the `build/editor.js` etc URLs. 84 | 85 | Speaking of which, the `tv-shows` frontend script would then looking like: 86 | 87 | 88 | ```jsx 89 | import { render } from '@wordpress/element'; 90 | 91 | render(

Hello World

, document.getElementById( 'tv-shows' ) ); // Grab the container that was rendered in `render_callback`. 92 | ``` 93 | 94 | The above assumed the `@wordpress/element` is added as a dep of the frontend script via `wp_register_script`. `@wordpress/scripts` will automatically "external" the WordPress scripts from the bundle. This means there's only ever one copy of React used across many "React apps" / blocks. 95 | 96 | Voila! That's pretty much, what I think, is the simplest way to get a custom block rendered as a React component on the frontend. It's quite likely you'd make use of `@wordpress/api-fetch` for fetching data from the REST API as part of your React component too. 97 | 98 | ## Rendering on the server 99 | 100 | So, once we've jumoed through many hoops and technologies to get a frontend JavaScript component, we are at a position of `-1`, as our block requires JavaScript to render (SEO, etc etc). This is where Block Editor SSR comes in. To have a custom block be rendered on the server, you must set the `ssr` flag in `register_block_type`: 101 | 102 | ```php 103 | register_block_type( 104 | 'joe/tv-shows', 105 | [ 106 | ..., 107 | 'ssr' => true, 108 | ] 109 | ); 110 | ``` 111 | 112 | The library will now know to (basically) override the `render_callback` of the custom block, render the React component via PHP V8JS and put the output directly into the page output. This works by taking the `script` property of the registered block and executing it (with all registered deps). You _can_ also disable "frontend rendering" of the block, so the JavaScript is only executed on the server, and not in the browser by setting the `fsr` property in `register_block_type` to `false`. Why would one want to do that? Well, it can be useful for debugging / verifying that SSR is working correctly. It may also be that you want to write your frontend components in React, but not actually execute any JavaScript in the browser. 113 | 114 | You must also adapt your React DOM `render` call to make use of server side rendering too. When any block has the `ssr` flag, the `window.BlockEditorSSR` object will be available. Adapt the render call like so: 115 | 116 | ```jsx 117 | import { render } from '@wordpress/element'; 118 | 119 | if ( window.BlockEditorSSR ) { 120 | window.BlockEditorSSR.render(

Hello World

, document.getElementById( 'tv-shows' ) ) 121 | } else { 122 | render(

Hello World

, document.getElementById( 'tv-shows' ) ); 123 | } 124 | ``` 125 | 126 | `window.BlockEditorSSR.render` will handler the server-side rendering on the server, and when run in the browser, it will automatically hydrate the React app. 127 | 128 | ## Data Loading Superpowers 129 | 130 | When you are going to the lengths of creating "mini React apps" per component, it's quite possible you'll be doing data-loading as part of the component intialization. In my TV Shows example, I'd load `/wp/v2/shows` from the REST API. WordPress provides the `@wordpress/api-fetch` package for that kind of thing. Block Editor SSR ships with a custom React hook called `useApiFetch` which will provided sychronous data loading when rendered on the server, and use `@wordpress/api-fetch` when loading data in the browser. This means you can write React components that will load data from the REST API and have it auto-load data on the server. What's more, any data loaded via server rendering will automatically be included in the page payload, so the React frontend hydration will have all the data instantly available for the client-side render. 131 | 132 | `useApiFetch` is available via the `window.BlockEditorSSR` global. For example, to load all `tv-show` posts: 133 | 134 | ```jsx 135 | let useApiFetch = window.BlockEditorSSR.useApiFetch; 136 | 137 | export default function TvShowsList() { 138 | const [ isLoading, tvShows, error ] = useApiFetch( { path: '/wp/v2/tv-shows' } ); 139 | if ( isLoading ) { 140 | return
Loading...
141 | } 142 | if ( error ) { 143 | return
{ error.message }
144 | } 145 | 146 | return 151 | } 152 | ``` 153 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "humnanmade/block-editor-ssr", 3 | "description": "Server Side render your interactive React Gutenberg blocks", 4 | "type": "library", 5 | "authors": [ 6 | { 7 | "name": "Joe Hoyle", 8 | "email": "joehoyle@gmail.com" 9 | } 10 | ], 11 | "require": {} 12 | } 13 | -------------------------------------------------------------------------------- /inc/namespace.php: -------------------------------------------------------------------------------- 1 | get_all_registered() as $block_name => $block_type ) { 44 | if ( ! isset( $block_type->ssr ) ) { 45 | continue; 46 | } 47 | // Inject "block-editor-ssr" as another dep for 48 | // the block's script. 49 | if ( $block_type->editor_script ) { 50 | $script_handle = $block_type->editor_script; 51 | $script = wp_scripts()->query( $script_handle ); 52 | $script->deps[] = 'block-editor-ssr'; 53 | } 54 | 55 | if ( $block_type->script ) { 56 | $script_handle = $block_type->script; 57 | $script = wp_scripts()->query( $script_handle ); 58 | $script->deps[] = 'block-editor-ssr'; 59 | } 60 | } 61 | } 62 | 63 | /** 64 | * Render a block on the `render_block` action. 65 | * 66 | * @param string $block_content The current block content. 67 | * @param array $parsed_block The parsed block data. 68 | * @return string 69 | */ 70 | function on_render_block( string $block_content, array $parsed_block ) : string { 71 | $registry = WP_Block_Type_Registry::get_instance(); 72 | $block_type = $registry->get_registered( $parsed_block['blockName'] ); 73 | if ( ! isset( $block_type->ssr ) ) { 74 | return $block_content; 75 | } 76 | 77 | if ( ! class_exists( 'V8Js' ) ) { 78 | trigger_error( 'react-wp-ssr requires the v8js extension, skipping server-side rendering.', E_USER_NOTICE ); 79 | return $block_content; 80 | } 81 | $script_handle = $block_type->script; 82 | 83 | // Inject "block-editor-ssr" as another dep for 84 | // the block's script. 85 | $script = wp_scripts()->query( $script_handle ); 86 | $script->deps[] = 'block-editor-ssr'; 87 | 88 | if ( $block_type->ssr ) { 89 | $v8js = get_v8js(); 90 | 91 | $block_ssr_data = [ 92 | 'blockContent' => $block_content, 93 | 'attributes' => $block_type->prepare_attributes_for_render( $parsed_block['attrs'] ), 94 | 'parsedBlock' => $parsed_block, 95 | ]; 96 | 97 | try { 98 | $v8js->executeString( 'var blockSsrData = ' . json_encode( $block_ssr_data ) . ';' ); 99 | $output = execute_script( $v8js, $script_handle ); 100 | } catch ( Exception $e ) { 101 | var_dump($e); 102 | $output = '
' . esc_html( $e->getMessage() ) . '
'; 103 | } 104 | } else { 105 | $output = ''; 106 | } 107 | 108 | $attributes = $parsed_block['attrs']; 109 | 110 | // If using block props, then get the class names from the block's render method. 111 | if ( preg_match( '/class="([^"]*)"/', $parsed_block['innerHTML'], $matches ) ) { 112 | $class_string = $matches[1]; 113 | } else { 114 | $class_string = $attributes['className']; 115 | 116 | if ( isset( $attributes['align'] ) && $attributes['align'] === 'full' ) { 117 | $class_string .= ' alignfull'; 118 | } 119 | } 120 | 121 | $output = sprintf( 122 | '
%s
', 123 | esc_attr( $script_handle ), 124 | $output ? 'data-rendered=""' : '', 125 | esc_attr( $class_string ), 126 | $output, // phpcs:ignore 127 | ); 128 | 129 | $handle = $script_handle; 130 | // Ensure the live script also receives the container. 131 | add_filter( 132 | 'script_loader_tag', 133 | function ( $tag, $script_handle ) use ( $handle, $block_type, $block_ssr_data ) { 134 | if ( $script_handle !== $handle ) { 135 | return $tag; 136 | } 137 | // Allow disabling frontend rendering for debugging. 138 | if ( ( isset( $block_type->fsr ) && $block_type->fsr === false ) || ( defined( 'SSR_DEBUG_SERVER_ONLY' ) && SSR_DEBUG_SERVER_ONLY ) ) { 139 | return ''; 140 | } 141 | 142 | $new_tag = sprintf( ' 292 | query( $handle ); 305 | if ( $script->deps ) { 306 | foreach ( $script->deps as $dep ) { 307 | // Dont't load deps more than once. 308 | if ( in_array( $dep, $v8js->loaded_scripts ) ) { 309 | continue; 310 | } 311 | execute_script( $v8js, $dep ); 312 | } 313 | } 314 | 315 | if ( $handle === 'react-dom' ) { 316 | $path = __DIR__ . '/react-dom-server.js'; 317 | } else { 318 | $path = get_path_for_script_url( $script->src ); 319 | } 320 | 321 | $source = file_get_contents( $path ); 322 | 323 | // Lodash will incorrectly assume it's running in Node rather 324 | // than a browser, and set global._ rather than window.lodash. 325 | if ( $handle === 'lodash' ) { 326 | $source .= ' window.lodash = global._;'; 327 | } 328 | ob_start(); 329 | try { 330 | $v8js->loaded_scripts[] = $handle; 331 | $v8js->executeString( $source, $path ); 332 | } catch ( V8JsScriptException $e ) { 333 | ob_clean(); 334 | if ( WP_DEBUG || true ) { 335 | handle_exception( $e, $path ); 336 | } else { 337 | // Trigger a warning, but otherwise do nothing. 338 | trigger_error( 'SSR error: ' . $e->getMessage(), E_USER_WARNING ); // phpcs:ignore 339 | } 340 | 341 | throw $e; 342 | } 343 | 344 | $output = ob_get_clean(); 345 | 346 | return $output; 347 | } 348 | 349 | /** 350 | * Get a script file path from the enqueued url. 351 | * 352 | * @param string $url The script URL 353 | * @return string 354 | */ 355 | function get_path_for_script_url( string $url ) : string { 356 | if ( strpos( $url, WP_CONTENT_URL ) === 0 ) { 357 | return str_replace( WP_CONTENT_URL, WP_CONTENT_DIR, $url ); 358 | } 359 | 360 | if ( strpos( $url, '/' ) === 0 ) { 361 | return untrailingslashit( ABSPATH ) . $url; 362 | } 363 | 364 | return $url; 365 | } 366 | 367 | /** 368 | * Render JS exception handler. 369 | * 370 | * @param V8JsScriptException $e Exception to handle. 371 | */ 372 | function handle_exception( V8JsScriptException $e ) { 373 | $file = $e->getJsFileName(); 374 | ?> 375 | 376 |
377 |
Failed to render
378 |
getJsTrace();
382 | 			if ( $trace ) {
383 | 				$trace_lines = $error = explode( "\n", $e->getJsTrace() );
384 | 				echo esc_html( $trace_lines[0] ) . "\n\n";
385 | 			} else {
386 | 				echo $e->getMessage() . "\n\n";
387 | 			}
388 | 
389 | 			// Replace tabs with tab character.
390 | 			$prefix = '> ' . (int) $e->getJsLineNumber() . ' | ';
391 | 			echo $prefix . str_replace(
392 | 				"\t",
393 | 				'',
394 | 				esc_html( $e->getJsSourceLine() )
395 | 			) . "\n";
396 | 			echo str_repeat( " ", strlen( $prefix ) + $e->getJsStartColumn() );
397 | 			echo str_repeat( "^", $e->getJsEndColumn() - $e->getJsStartColumn() ) . "\n";
398 | 			?>
399 | 407 |
408 | b; 141 | } 142 | return !1; 143 | } 144 | function n(a, b, c, d, g, h) { 145 | this.acceptsBooleans = 2 === b || 3 === b || 4 === b; 146 | this.attributeName = d; 147 | this.attributeNamespace = g; 148 | this.mustUseProperty = c; 149 | this.propertyName = a; 150 | this.type = b; 151 | this.sanitizeURL = h; 152 | } 153 | function C(a) { 154 | if ("boolean" === typeof a || "number" === typeof a) return "" + a; 155 | a = "" + a; 156 | var b = za.exec(a); 157 | if (b) { 158 | var c = "", 159 | d, 160 | g = 0; 161 | for (d = b.index; d < a.length; d++) { 162 | switch (a.charCodeAt(d)) { 163 | case 34: 164 | b = """; 165 | break; 166 | case 38: 167 | b = "&"; 168 | break; 169 | case 39: 170 | b = "'"; 171 | break; 172 | case 60: 173 | b = "<"; 174 | break; 175 | case 62: 176 | b = ">"; 177 | break; 178 | default: 179 | continue; 180 | } 181 | g !== d && (c += a.substring(g, d)); 182 | g = d + 1; 183 | c += b; 184 | } 185 | a = g !== d ? c + a.substring(g, d) : c; 186 | } 187 | return a; 188 | } 189 | function Aa(a, b) { 190 | var c = m.hasOwnProperty(a) ? m[a] : null; 191 | var d; 192 | if ((d = "style" !== a)) 193 | d = 194 | null !== c 195 | ? 0 === c.type 196 | : !(2 < a.length) || 197 | ("o" !== a[0] && "O" !== a[0]) || 198 | ("n" !== a[1] && "N" !== a[1]) 199 | ? !1 200 | : !0; 201 | if (d || ya(a, b, c, !1)) return ""; 202 | if (null !== c) { 203 | a = c.attributeName; 204 | d = c.type; 205 | if (3 === d || (4 === d && !0 === b)) return a + '=""'; 206 | c.sanitizeURL && (b = "" + b); 207 | return a + '="' + (C(b) + '"'); 208 | } 209 | return fa(a) ? a + '="' + (C(b) + '"') : ""; 210 | } 211 | function Ba(a, b) { 212 | return ( 213 | (a === b && (0 !== a || 1 / a === 1 / b)) || (a !== a && b !== b) 214 | ); 215 | } 216 | function A() { 217 | if (null === u) throw Error(p(321)); 218 | return u; 219 | } 220 | function ka() { 221 | if (0 < I) throw Error(p(312)); 222 | return { memoizedState: null, queue: null, next: null }; 223 | } 224 | function O() { 225 | null === e 226 | ? null === J 227 | ? ((F = !1), (J = e = ka())) 228 | : ((F = !0), (e = J)) 229 | : null === e.next 230 | ? ((F = !1), (e = e.next = ka())) 231 | : ((F = !0), (e = e.next)); 232 | return e; 233 | } 234 | function la(a, b, c, d) { 235 | for (; P; ) (P = !1), (I += 1), (e = null), (c = a(b, d)); 236 | J = u = null; 237 | I = 0; 238 | e = t = null; 239 | return c; 240 | } 241 | function ma(a, b) { 242 | return "function" === typeof b ? b(a) : b; 243 | } 244 | function na(a, b, c) { 245 | u = A(); 246 | e = O(); 247 | if (F) { 248 | var d = e.queue; 249 | b = d.dispatch; 250 | if (null !== t && ((c = t.get(d)), void 0 !== c)) { 251 | t.delete(d); 252 | d = e.memoizedState; 253 | do (d = a(d, c.action)), (c = c.next); 254 | while (null !== c); 255 | e.memoizedState = d; 256 | return [d, b]; 257 | } 258 | return [e.memoizedState, b]; 259 | } 260 | a = 261 | a === ma 262 | ? "function" === typeof b 263 | ? b() 264 | : b 265 | : void 0 !== c 266 | ? c(b) 267 | : b; 268 | e.memoizedState = a; 269 | a = e.queue = { last: null, dispatch: null }; 270 | a = a.dispatch = Ca.bind(null, u, a); 271 | return [e.memoizedState, a]; 272 | } 273 | function Ca(a, b, c) { 274 | if (!(25 > I)) throw Error(p(301)); 275 | if (a === u) 276 | if ( 277 | ((P = !0), 278 | (a = { action: c, next: null }), 279 | null === t && (t = new Map()), 280 | (c = t.get(b)), 281 | void 0 === c) 282 | ) 283 | t.set(b, a); 284 | else { 285 | for (b = c; null !== b.next; ) b = b.next; 286 | b.next = a; 287 | } 288 | } 289 | function Q() {} 290 | function oa(a) { 291 | switch (a) { 292 | case "svg": 293 | return "http://www.w3.org/2000/svg"; 294 | case "math": 295 | return "http://www.w3.org/1998/Math/MathML"; 296 | default: 297 | return "http://www.w3.org/1999/xhtml"; 298 | } 299 | } 300 | function Da(a) { 301 | if (void 0 === a || null === a) return a; 302 | var b = ""; 303 | k.Children.forEach(a, function (a) { 304 | null != a && (b += a); 305 | }); 306 | return b; 307 | } 308 | function pa(a, b) { 309 | if (void 0 === a) throw Error(p(152, D(b) || "Component")); 310 | } 311 | function Ea(a, b, c) { 312 | function d(d, h) { 313 | var f = h.prototype && h.prototype.isReactComponent, 314 | g = va(h, b, c, f), 315 | v = [], 316 | z = !1, 317 | e = { 318 | isMounted: function (a) { 319 | return !1; 320 | }, 321 | enqueueForceUpdate: function (a) { 322 | if (null === v) return null; 323 | }, 324 | enqueueReplaceState: function (a, c) { 325 | z = !0; 326 | v = [c]; 327 | }, 328 | enqueueSetState: function (a, c) { 329 | if (null === v) return null; 330 | v.push(c); 331 | }, 332 | }; 333 | if (f) { 334 | if ( 335 | ((f = new h(d.props, g, e)), 336 | "function" === typeof h.getDerivedStateFromProps) 337 | ) { 338 | var l = h.getDerivedStateFromProps.call( 339 | null, 340 | d.props, 341 | f.state 342 | ); 343 | null != l && (f.state = x({}, f.state, l)); 344 | } 345 | } else if ( 346 | ((u = {}), 347 | (f = h(d.props, g, e)), 348 | (f = la(h, d.props, f, g)), 349 | null == f || null == f.render) 350 | ) { 351 | a = f; 352 | pa(a, h); 353 | return; 354 | } 355 | f.props = d.props; 356 | f.context = g; 357 | f.updater = e; 358 | e = f.state; 359 | void 0 === e && (f.state = e = null); 360 | if ( 361 | "function" === typeof f.UNSAFE_componentWillMount || 362 | "function" === typeof f.componentWillMount 363 | ) 364 | if ( 365 | ("function" === typeof f.componentWillMount && 366 | "function" !== typeof h.getDerivedStateFromProps && 367 | f.componentWillMount(), 368 | "function" === typeof f.UNSAFE_componentWillMount && 369 | "function" !== typeof h.getDerivedStateFromProps && 370 | f.UNSAFE_componentWillMount(), 371 | v.length) 372 | ) { 373 | e = v; 374 | var m = z; 375 | v = null; 376 | z = !1; 377 | if (m && 1 === e.length) f.state = e[0]; 378 | else { 379 | l = m ? e[0] : f.state; 380 | var q = !0; 381 | for (m = m ? 1 : 0; m < e.length; m++) { 382 | var r = e[m]; 383 | r = 384 | "function" === typeof r 385 | ? r.call(f, l, d.props, g) 386 | : r; 387 | null != r && 388 | (q ? ((q = !1), (l = x({}, l, r))) : x(l, r)); 389 | } 390 | f.state = l; 391 | } 392 | } else v = null; 393 | a = f.render(); 394 | pa(a, h); 395 | if ( 396 | "function" === typeof f.getChildContext && 397 | ((d = h.childContextTypes), "object" === typeof d) 398 | ) { 399 | var k = f.getChildContext(); 400 | for (var n in k) 401 | if (!(n in d)) throw Error(p(108, D(h) || "Unknown", n)); 402 | } 403 | k && (b = x({}, b, k)); 404 | } 405 | for (; k.isValidElement(a); ) { 406 | var g = a, 407 | h = g.type; 408 | if ("function" !== typeof h) break; 409 | d(g, h); 410 | } 411 | return { child: a, context: b }; 412 | } 413 | var x = k.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.assign, 414 | l = "function" === typeof Symbol && Symbol.for, 415 | W = l ? Symbol.for("react.portal") : 60106, 416 | L = l ? Symbol.for("react.fragment") : 60107, 417 | Y = l ? Symbol.for("react.strict_mode") : 60108, 418 | X = l ? Symbol.for("react.profiler") : 60114, 419 | N = l ? Symbol.for("react.provider") : 60109, 420 | aa = l ? Symbol.for("react.context") : 60110, 421 | Fa = l ? Symbol.for("react.concurrent_mode") : 60111, 422 | ba = l ? Symbol.for("react.forward_ref") : 60112, 423 | M = l ? Symbol.for("react.suspense") : 60113, 424 | Z = l ? Symbol.for("react.suspense_list") : 60120, 425 | ca = l ? Symbol.for("react.memo") : 60115, 426 | da = l ? Symbol.for("react.lazy") : 60116, 427 | ua = l ? Symbol.for("react.block") : 60121, 428 | Ga = l ? Symbol.for("react.fundamental") : 60117, 429 | Ha = l ? Symbol.for("react.scope") : 60119; 430 | l = k.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED; 431 | l.hasOwnProperty("ReactCurrentDispatcher") || 432 | (l.ReactCurrentDispatcher = { current: null }); 433 | l.hasOwnProperty("ReactCurrentBatchConfig") || 434 | (l.ReactCurrentBatchConfig = { suspense: null }); 435 | for (var ea = {}, q = new Uint16Array(16), K = 0; 15 > K; K++) q[K] = K + 1; 436 | q[15] = 0; 437 | var wa = 438 | /^[:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD][:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]*$/, 439 | ha = Object.prototype.hasOwnProperty, 440 | ja = {}, 441 | ia = {}, 442 | m = {}; 443 | "children dangerouslySetInnerHTML defaultValue defaultChecked innerHTML suppressContentEditableWarning suppressHydrationWarning style" 444 | .split(" ") 445 | .forEach(function (a) { 446 | m[a] = new n(a, 0, !1, a, null, !1); 447 | }); 448 | [ 449 | ["acceptCharset", "accept-charset"], 450 | ["className", "class"], 451 | ["htmlFor", "for"], 452 | ["httpEquiv", "http-equiv"], 453 | ].forEach(function (a) { 454 | var b = a[0]; 455 | m[b] = new n(b, 1, !1, a[1], null, !1); 456 | }); 457 | ["contentEditable", "draggable", "spellCheck", "value"].forEach(function ( 458 | a 459 | ) { 460 | m[a] = new n(a, 2, !1, a.toLowerCase(), null, !1); 461 | }); 462 | [ 463 | "autoReverse", 464 | "externalResourcesRequired", 465 | "focusable", 466 | "preserveAlpha", 467 | ].forEach(function (a) { 468 | m[a] = new n(a, 2, !1, a, null, !1); 469 | }); 470 | "allowFullScreen async autoFocus autoPlay controls default defer disabled disablePictureInPicture formNoValidate hidden loop noModule noValidate open playsInline readOnly required reversed scoped seamless itemScope" 471 | .split(" ") 472 | .forEach(function (a) { 473 | m[a] = new n(a, 3, !1, a.toLowerCase(), null, !1); 474 | }); 475 | ["checked", "multiple", "muted", "selected"].forEach(function (a) { 476 | m[a] = new n(a, 3, !0, a, null, !1); 477 | }); 478 | ["capture", "download"].forEach(function (a) { 479 | m[a] = new n(a, 4, !1, a, null, !1); 480 | }); 481 | ["cols", "rows", "size", "span"].forEach(function (a) { 482 | m[a] = new n(a, 6, !1, a, null, !1); 483 | }); 484 | ["rowSpan", "start"].forEach(function (a) { 485 | m[a] = new n(a, 5, !1, a.toLowerCase(), null, !1); 486 | }); 487 | var S = /[\-:]([a-z])/g, 488 | T = function (a) { 489 | return a[1].toUpperCase(); 490 | }; 491 | "accent-height alignment-baseline arabic-form baseline-shift cap-height clip-path clip-rule color-interpolation color-interpolation-filters color-profile color-rendering dominant-baseline enable-background fill-opacity fill-rule flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-name glyph-orientation-horizontal glyph-orientation-vertical horiz-adv-x horiz-origin-x image-rendering letter-spacing lighting-color marker-end marker-mid marker-start overline-position overline-thickness paint-order panose-1 pointer-events rendering-intent shape-rendering stop-color stop-opacity strikethrough-position strikethrough-thickness stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width text-anchor text-decoration text-rendering underline-position underline-thickness unicode-bidi unicode-range units-per-em v-alphabetic v-hanging v-ideographic v-mathematical vector-effect vert-adv-y vert-origin-x vert-origin-y word-spacing writing-mode xmlns:xlink x-height" 492 | .split(" ") 493 | .forEach(function (a) { 494 | var b = a.replace(S, T); 495 | m[b] = new n(b, 1, !1, a, null, !1); 496 | }); 497 | "xlink:actuate xlink:arcrole xlink:role xlink:show xlink:title xlink:type" 498 | .split(" ") 499 | .forEach(function (a) { 500 | var b = a.replace(S, T); 501 | m[b] = new n(b, 1, !1, a, "http://www.w3.org/1999/xlink", !1); 502 | }); 503 | ["xml:base", "xml:lang", "xml:space"].forEach(function (a) { 504 | var b = a.replace(S, T); 505 | m[b] = new n(b, 1, !1, a, "http://www.w3.org/XML/1998/namespace", !1); 506 | }); 507 | ["tabIndex", "crossOrigin"].forEach(function (a) { 508 | m[a] = new n(a, 1, !1, a.toLowerCase(), null, !1); 509 | }); 510 | m.xlinkHref = new n( 511 | "xlinkHref", 512 | 1, 513 | !1, 514 | "xlink:href", 515 | "http://www.w3.org/1999/xlink", 516 | !0 517 | ); 518 | ["src", "href", "action", "formAction"].forEach(function (a) { 519 | m[a] = new n(a, 1, !1, a.toLowerCase(), null, !0); 520 | }); 521 | var za = /["'&<>]/, 522 | Ia = "function" === typeof Object.is ? Object.is : Ba, 523 | u = null, 524 | J = null, 525 | e = null, 526 | F = !1, 527 | P = !1, 528 | t = null, 529 | I = 0, 530 | G = 0, 531 | Ja = { 532 | readContext: function (a, b) { 533 | b = G; 534 | E(a, b); 535 | return a[b]; 536 | }, 537 | useContext: function (a, b) { 538 | A(); 539 | b = G; 540 | E(a, b); 541 | return a[b]; 542 | }, 543 | useMemo: function (a, b) { 544 | u = A(); 545 | e = O(); 546 | b = void 0 === b ? null : b; 547 | if (null !== e) { 548 | var c = e.memoizedState; 549 | if (null !== c && null !== b) { 550 | a: { 551 | var d = c[1]; 552 | if (null === d) d = !1; 553 | else { 554 | for ( 555 | var g = 0; 556 | g < d.length && g < b.length; 557 | g++ 558 | ) 559 | if (!Ia(b[g], d[g])) { 560 | d = !1; 561 | break a; 562 | } 563 | d = !0; 564 | } 565 | } 566 | if (d) return c[0]; 567 | } 568 | } 569 | a = a(); 570 | e.memoizedState = [a, b]; 571 | return a; 572 | }, 573 | useReducer: na, 574 | useRef: function (a) { 575 | u = A(); 576 | e = O(); 577 | var b = e.memoizedState; 578 | return null === b 579 | ? ((a = { current: a }), (e.memoizedState = a)) 580 | : b; 581 | }, 582 | useState: function (a) { 583 | return na(ma, a); 584 | }, 585 | useLayoutEffect: function (a, b) {}, 586 | useCallback: function (a, b) { 587 | return a; 588 | }, 589 | useImperativeHandle: Q, 590 | useEffect: Q, 591 | useDebugValue: Q, 592 | useResponder: function (a, b) { 593 | return { props: b, responder: a }; 594 | }, 595 | useDeferredValue: function (a, b) { 596 | A(); 597 | return a; 598 | }, 599 | useTransition: function (a) { 600 | A(); 601 | return [ 602 | function (a) { 603 | a(); 604 | }, 605 | !1, 606 | ]; 607 | }, 608 | }, 609 | qa = { 610 | area: !0, 611 | base: !0, 612 | br: !0, 613 | col: !0, 614 | embed: !0, 615 | hr: !0, 616 | img: !0, 617 | input: !0, 618 | keygen: !0, 619 | link: !0, 620 | meta: !0, 621 | param: !0, 622 | source: !0, 623 | track: !0, 624 | wbr: !0, 625 | }, 626 | Ka = x({ menuitem: !0 }, qa), 627 | H = { 628 | animationIterationCount: !0, 629 | borderImageOutset: !0, 630 | borderImageSlice: !0, 631 | borderImageWidth: !0, 632 | boxFlex: !0, 633 | boxFlexGroup: !0, 634 | boxOrdinalGroup: !0, 635 | columnCount: !0, 636 | columns: !0, 637 | flex: !0, 638 | flexGrow: !0, 639 | flexPositive: !0, 640 | flexShrink: !0, 641 | flexNegative: !0, 642 | flexOrder: !0, 643 | gridArea: !0, 644 | gridRow: !0, 645 | gridRowEnd: !0, 646 | gridRowSpan: !0, 647 | gridRowStart: !0, 648 | gridColumn: !0, 649 | gridColumnEnd: !0, 650 | gridColumnSpan: !0, 651 | gridColumnStart: !0, 652 | fontWeight: !0, 653 | lineClamp: !0, 654 | lineHeight: !0, 655 | opacity: !0, 656 | order: !0, 657 | orphans: !0, 658 | tabSize: !0, 659 | widows: !0, 660 | zIndex: !0, 661 | zoom: !0, 662 | fillOpacity: !0, 663 | floodOpacity: !0, 664 | stopOpacity: !0, 665 | strokeDasharray: !0, 666 | strokeDashoffset: !0, 667 | strokeMiterlimit: !0, 668 | strokeOpacity: !0, 669 | strokeWidth: !0, 670 | }, 671 | La = ["Webkit", "ms", "Moz", "O"]; 672 | Object.keys(H).forEach(function (a) { 673 | La.forEach(function (b) { 674 | b = b + a.charAt(0).toUpperCase() + a.substring(1); 675 | H[b] = H[a]; 676 | }); 677 | }); 678 | var Ma = /([A-Z])/g, 679 | Na = /^ms-/, 680 | B = k.Children.toArray, 681 | U = l.ReactCurrentDispatcher, 682 | Oa = { listing: !0, pre: !0, textarea: !0 }, 683 | Pa = /^[a-zA-Z][a-zA-Z:_\.\-\d]*$/, 684 | ra = {}, 685 | V = {}, 686 | Qa = Object.prototype.hasOwnProperty, 687 | Ra = { 688 | children: null, 689 | dangerouslySetInnerHTML: null, 690 | suppressContentEditableWarning: null, 691 | suppressHydrationWarning: null, 692 | }, 693 | sa = (function () { 694 | function a(a, b) { 695 | k.isValidElement(a) 696 | ? a.type !== L 697 | ? (a = [a]) 698 | : ((a = a.props.children), 699 | (a = k.isValidElement(a) ? [a] : B(a))) 700 | : (a = B(a)); 701 | a = { 702 | type: null, 703 | domNamespace: "http://www.w3.org/1999/xhtml", 704 | children: a, 705 | childIndex: 0, 706 | context: ea, 707 | footer: "", 708 | }; 709 | var c = q[0]; 710 | if (0 === c) { 711 | var h = q; 712 | c = h.length; 713 | var d = 2 * c; 714 | if (!(65536 >= d)) throw Error(p(304)); 715 | var z = new Uint16Array(d); 716 | z.set(h); 717 | q = z; 718 | q[0] = c + 1; 719 | for (h = c; h < d - 1; h++) q[h] = h + 1; 720 | q[d - 1] = 0; 721 | } else q[0] = q[c]; 722 | this.threadID = c; 723 | this.stack = [a]; 724 | this.exhausted = !1; 725 | this.currentSelectValue = null; 726 | this.previousWasTextNode = !1; 727 | this.makeStaticMarkup = b; 728 | this.suspenseDepth = 0; 729 | this.contextIndex = -1; 730 | this.contextStack = []; 731 | this.contextValueStack = []; 732 | } 733 | var b = a.prototype; 734 | b.destroy = function () { 735 | if (!this.exhausted) { 736 | this.exhausted = !0; 737 | this.clearProviders(); 738 | var a = this.threadID; 739 | q[a] = q[0]; 740 | q[0] = a; 741 | } 742 | }; 743 | b.pushProvider = function (a) { 744 | var b = ++this.contextIndex, 745 | c = a.type._context, 746 | h = this.threadID; 747 | E(c, h); 748 | var v = c[h]; 749 | this.contextStack[b] = c; 750 | this.contextValueStack[b] = v; 751 | c[h] = a.props.value; 752 | }; 753 | b.popProvider = function (a) { 754 | a = this.contextIndex; 755 | var b = this.contextStack[a], 756 | c = this.contextValueStack[a]; 757 | this.contextStack[a] = null; 758 | this.contextValueStack[a] = null; 759 | this.contextIndex--; 760 | b[this.threadID] = c; 761 | }; 762 | b.clearProviders = function () { 763 | for (var a = this.contextIndex; 0 <= a; a--) 764 | this.contextStack[a][this.threadID] = 765 | this.contextValueStack[a]; 766 | }; 767 | b.read = function (a) { 768 | if (this.exhausted) return null; 769 | var b = G; 770 | G = this.threadID; 771 | var c = U.current; 772 | U.current = Ja; 773 | try { 774 | for (var h = [""], v = !1; h[0].length < a; ) { 775 | if (0 === this.stack.length) { 776 | this.exhausted = !0; 777 | var z = this.threadID; 778 | q[z] = q[0]; 779 | q[0] = z; 780 | break; 781 | } 782 | var f = this.stack[this.stack.length - 1]; 783 | if (v || f.childIndex >= f.children.length) { 784 | var e = f.footer; 785 | "" !== e && (this.previousWasTextNode = !1); 786 | this.stack.pop(); 787 | if ("select" === f.type) 788 | this.currentSelectValue = null; 789 | else if ( 790 | null != f.type && 791 | null != f.type.type && 792 | f.type.type.$$typeof === N 793 | ) 794 | this.popProvider(f.type); 795 | else if (f.type === M) { 796 | this.suspenseDepth--; 797 | var l = h.pop(); 798 | if (v) { 799 | v = !1; 800 | var m = f.fallbackFrame; 801 | if (!m) throw Error(p(303)); 802 | this.stack.push(m); 803 | h[this.suspenseDepth] += "\x3c!--$!--\x3e"; 804 | continue; 805 | } else h[this.suspenseDepth] += l; 806 | } 807 | h[this.suspenseDepth] += e; 808 | } else { 809 | var k = f.children[f.childIndex++], 810 | n = ""; 811 | try { 812 | n += this.render(k, f.context, f.domNamespace); 813 | } catch (R) { 814 | if (null != R && "function" === typeof R.then) 815 | throw Error(p(294)); 816 | throw R; 817 | } finally { 818 | } 819 | h.length <= this.suspenseDepth && h.push(""); 820 | h[this.suspenseDepth] += n; 821 | } 822 | } 823 | return h[0]; 824 | } finally { 825 | (U.current = c), (G = b); 826 | } 827 | }; 828 | b.render = function (a, b, g) { 829 | if ("string" === typeof a || "number" === typeof a) { 830 | g = "" + a; 831 | if ("" === g) return ""; 832 | if (this.makeStaticMarkup) return C(g); 833 | if (this.previousWasTextNode) 834 | return "\x3c!-- --\x3e" + C(g); 835 | this.previousWasTextNode = !0; 836 | return C(g); 837 | } 838 | b = Ea(a, b, this.threadID); 839 | a = b.child; 840 | b = b.context; 841 | if (null === a || !1 === a) return ""; 842 | if (!k.isValidElement(a)) { 843 | if (null != a && null != a.$$typeof) { 844 | g = a.$$typeof; 845 | if (g === W) throw Error(p(257)); 846 | throw Error(p(258, g.toString())); 847 | } 848 | a = B(a); 849 | this.stack.push({ 850 | type: null, 851 | domNamespace: g, 852 | children: a, 853 | childIndex: 0, 854 | context: b, 855 | footer: "", 856 | }); 857 | return ""; 858 | } 859 | var c = a.type; 860 | if ("string" === typeof c) return this.renderDOM(a, b, g); 861 | switch (c) { 862 | case Y: 863 | case Fa: 864 | case X: 865 | case Z: 866 | case L: 867 | return ( 868 | (a = B(a.props.children)), 869 | this.stack.push({ 870 | type: null, 871 | domNamespace: g, 872 | children: a, 873 | childIndex: 0, 874 | context: b, 875 | footer: "", 876 | }), 877 | "" 878 | ); 879 | case M: 880 | throw Error(p(294)); 881 | } 882 | if ("object" === typeof c && null !== c) 883 | switch (c.$$typeof) { 884 | case ba: 885 | u = {}; 886 | var d = c.render(a.props, a.ref); 887 | d = la(c.render, a.props, d, a.ref); 888 | d = B(d); 889 | this.stack.push({ 890 | type: null, 891 | domNamespace: g, 892 | children: d, 893 | childIndex: 0, 894 | context: b, 895 | footer: "", 896 | }); 897 | return ""; 898 | case ca: 899 | return ( 900 | (a = [ 901 | k.createElement( 902 | c.type, 903 | x({ ref: a.ref }, a.props) 904 | ), 905 | ]), 906 | this.stack.push({ 907 | type: null, 908 | domNamespace: g, 909 | children: a, 910 | childIndex: 0, 911 | context: b, 912 | footer: "", 913 | }), 914 | "" 915 | ); 916 | case N: 917 | return ( 918 | (c = B(a.props.children)), 919 | (g = { 920 | type: a, 921 | domNamespace: g, 922 | children: c, 923 | childIndex: 0, 924 | context: b, 925 | footer: "", 926 | }), 927 | this.pushProvider(a), 928 | this.stack.push(g), 929 | "" 930 | ); 931 | case aa: 932 | c = a.type; 933 | d = a.props; 934 | var e = this.threadID; 935 | E(c, e); 936 | c = B(d.children(c[e])); 937 | this.stack.push({ 938 | type: a, 939 | domNamespace: g, 940 | children: c, 941 | childIndex: 0, 942 | context: b, 943 | footer: "", 944 | }); 945 | return ""; 946 | case Ga: 947 | throw Error(p(338)); 948 | case da: 949 | switch (((c = a.type), ta(c), c._status)) { 950 | case 1: 951 | return ( 952 | (a = [ 953 | k.createElement( 954 | c._result, 955 | x({ ref: a.ref }, a.props) 956 | ), 957 | ]), 958 | this.stack.push({ 959 | type: null, 960 | domNamespace: g, 961 | children: a, 962 | childIndex: 0, 963 | context: b, 964 | footer: "", 965 | }), 966 | "" 967 | ); 968 | case 2: 969 | throw c._result; 970 | default: 971 | throw Error(p(295)); 972 | } 973 | case Ha: 974 | throw Error(p(343)); 975 | } 976 | throw Error(p(130, null == c ? c : typeof c, "")); 977 | }; 978 | b.renderDOM = function (a, b, g) { 979 | var c = a.type.toLowerCase(); 980 | "http://www.w3.org/1999/xhtml" === g && oa(c); 981 | if (!ra.hasOwnProperty(c)) { 982 | if (!Pa.test(c)) throw Error(p(65, c)); 983 | ra[c] = !0; 984 | } 985 | var d = a.props; 986 | if ("input" === c) 987 | d = x({ type: void 0 }, d, { 988 | defaultChecked: void 0, 989 | defaultValue: void 0, 990 | value: null != d.value ? d.value : d.defaultValue, 991 | checked: 992 | null != d.checked ? d.checked : d.defaultChecked, 993 | }); 994 | else if ("textarea" === c) { 995 | var e = d.value; 996 | if (null == e) { 997 | e = d.defaultValue; 998 | var f = d.children; 999 | if (null != f) { 1000 | if (null != e) throw Error(p(92)); 1001 | if (Array.isArray(f)) { 1002 | if (!(1 >= f.length)) throw Error(p(93)); 1003 | f = f[0]; 1004 | } 1005 | e = "" + f; 1006 | } 1007 | null == e && (e = ""); 1008 | } 1009 | d = x({}, d, { value: void 0, children: "" + e }); 1010 | } else if ("select" === c) 1011 | (this.currentSelectValue = 1012 | null != d.value ? d.value : d.defaultValue), 1013 | (d = x({}, d, { value: void 0 })); 1014 | else if ("option" === c) { 1015 | f = this.currentSelectValue; 1016 | var l = Da(d.children); 1017 | if (null != f) { 1018 | var m = null != d.value ? d.value + "" : l; 1019 | e = !1; 1020 | if (Array.isArray(f)) 1021 | for (var k = 0; k < f.length; k++) { 1022 | if ("" + f[k] === m) { 1023 | e = !0; 1024 | break; 1025 | } 1026 | } 1027 | else e = "" + f === m; 1028 | d = x({ selected: void 0, children: void 0 }, d, { 1029 | selected: e, 1030 | children: l, 1031 | }); 1032 | } 1033 | } 1034 | if ((e = d)) { 1035 | if ( 1036 | Ka[c] && 1037 | (null != e.children || 1038 | null != e.dangerouslySetInnerHTML) 1039 | ) 1040 | throw Error(p(137, c, "")); 1041 | if (null != e.dangerouslySetInnerHTML) { 1042 | if (null != e.children) throw Error(p(60)); 1043 | if ( 1044 | !( 1045 | "object" === typeof e.dangerouslySetInnerHTML && 1046 | "__html" in e.dangerouslySetInnerHTML 1047 | ) 1048 | ) 1049 | throw Error(p(61)); 1050 | } 1051 | if (null != e.style && "object" !== typeof e.style) 1052 | throw Error(p(62, "")); 1053 | } 1054 | e = d; 1055 | f = this.makeStaticMarkup; 1056 | l = 1 === this.stack.length; 1057 | m = "<" + a.type; 1058 | for (w in e) 1059 | if (Qa.call(e, w)) { 1060 | var n = e[w]; 1061 | if (null != n) { 1062 | if ("style" === w) { 1063 | k = void 0; 1064 | var q = "", 1065 | u = ""; 1066 | for (k in n) 1067 | if (n.hasOwnProperty(k)) { 1068 | var y = 0 === k.indexOf("--"), 1069 | r = n[k]; 1070 | if (null != r) { 1071 | if (y) var t = k; 1072 | else if ( 1073 | ((t = k), V.hasOwnProperty(t)) 1074 | ) 1075 | t = V[t]; 1076 | else { 1077 | var A = t 1078 | .replace(Ma, "-$1") 1079 | .toLowerCase() 1080 | .replace(Na, "-ms-"); 1081 | t = V[t] = A; 1082 | } 1083 | q += u + t + ":"; 1084 | u = k; 1085 | y = 1086 | null == r || 1087 | "boolean" === typeof r || 1088 | "" === r 1089 | ? "" 1090 | : y || 1091 | "number" !== typeof r || 1092 | 0 === r || 1093 | (H.hasOwnProperty(u) && 1094 | H[u]) 1095 | ? ("" + r).trim() 1096 | : r + "px"; 1097 | q += y; 1098 | u = ";"; 1099 | } 1100 | } 1101 | n = q || null; 1102 | } 1103 | k = null; 1104 | b: if (((y = c), (r = e), -1 === y.indexOf("-"))) 1105 | y = "string" === typeof r.is; 1106 | else 1107 | switch (y) { 1108 | case "annotation-xml": 1109 | case "color-profile": 1110 | case "font-face": 1111 | case "font-face-src": 1112 | case "font-face-uri": 1113 | case "font-face-format": 1114 | case "font-face-name": 1115 | case "missing-glyph": 1116 | y = !1; 1117 | break b; 1118 | default: 1119 | y = !0; 1120 | } 1121 | y 1122 | ? Ra.hasOwnProperty(w) || 1123 | ((k = w), 1124 | (k = 1125 | fa(k) && null != n 1126 | ? k + '="' + (C(n) + '"') 1127 | : "")) 1128 | : (k = Aa(w, n)); 1129 | k && (m += " " + k); 1130 | } 1131 | } 1132 | f || (l && (m += ' data-reactroot=""')); 1133 | var w = m; 1134 | e = ""; 1135 | qa.hasOwnProperty(c) 1136 | ? (w += "/>") 1137 | : ((w += ">"), (e = "")); 1138 | a: { 1139 | f = d.dangerouslySetInnerHTML; 1140 | if (null != f) { 1141 | if (null != f.__html) { 1142 | f = f.__html; 1143 | break a; 1144 | } 1145 | } else if ( 1146 | ((f = d.children), 1147 | "string" === typeof f || "number" === typeof f) 1148 | ) { 1149 | f = C(f); 1150 | break a; 1151 | } 1152 | f = null; 1153 | } 1154 | null != f 1155 | ? ((d = []), 1156 | Oa.hasOwnProperty(c) && 1157 | "\n" === f.charAt(0) && 1158 | (w += "\n"), 1159 | (w += f)) 1160 | : (d = B(d.children)); 1161 | a = a.type; 1162 | g = 1163 | null == g || "http://www.w3.org/1999/xhtml" === g 1164 | ? oa(a) 1165 | : "http://www.w3.org/2000/svg" === g && 1166 | "foreignObject" === a 1167 | ? "http://www.w3.org/1999/xhtml" 1168 | : g; 1169 | this.stack.push({ 1170 | domNamespace: g, 1171 | type: c, 1172 | children: d, 1173 | childIndex: 0, 1174 | context: b, 1175 | footer: e, 1176 | }); 1177 | this.previousWasTextNode = !1; 1178 | return w; 1179 | }; 1180 | return a; 1181 | })(); 1182 | l = { 1183 | renderToString: function (a) { 1184 | a = new sa(a, !1); 1185 | try { 1186 | return a.read(Infinity); 1187 | } finally { 1188 | a.destroy(); 1189 | } 1190 | }, 1191 | renderToStaticMarkup: function (a) { 1192 | a = new sa(a, !0); 1193 | try { 1194 | return a.read(Infinity); 1195 | } finally { 1196 | a.destroy(); 1197 | } 1198 | }, 1199 | renderToNodeStream: function () { 1200 | throw Error(p(207)); 1201 | }, 1202 | renderToStaticNodeStream: function () { 1203 | throw Error(p(208)); 1204 | }, 1205 | version: "16.13.1", 1206 | }; 1207 | return l.default || l; 1208 | }); 1209 | -------------------------------------------------------------------------------- /inc/ssr.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The Block Editor SSR JS library. 3 | * 4 | * This library handles isomorphic rendering and hydration on the server 5 | * site and client side. 6 | */ 7 | let ReactDOMServer = window.ReactDOMServer; 8 | let ReactDOMHydrate = window.ReactDOM && window.ReactDOM.hydrate; 9 | let ReactDOMRender = window.wp && window.wp.element.render; 10 | let useState = window.wp && window.wp.element.useState; 11 | let useEffect = window.wp && window.wp.element.useEffect; 12 | let apiFetch = window.wp && window.wp.apiFetch; 13 | 14 | const ENV_BROWSER = 'browser'; 15 | const ENV_SERVER = 'server'; 16 | 17 | function getEnvironment() { 18 | return typeof global === 'undefined' || typeof global.isSSR === 'undefined' ? ENV_BROWSER : ENV_SERVER; 19 | } 20 | 21 | const onFrontend = callback => getEnvironment() === ENV_BROWSER && callback(); 22 | const onBackend = callback => getEnvironment() === ENV_SERVER && callback(); 23 | 24 | function render( getComponent, containerId ) { 25 | const environment = getEnvironment(); 26 | const component = getComponent( { 27 | environment, 28 | ...getBlockSsrData(), 29 | } ); 30 | 31 | switch ( environment ) { 32 | case ENV_SERVER: 33 | global.print( ReactDOMServer.renderToString( component ) ); 34 | break; 35 | 36 | case ENV_BROWSER: { 37 | if ( ! containerId ) { 38 | return; 39 | } 40 | const container = document.getElementById( containerId ); 41 | if ( ! container ) { 42 | return; 43 | } 44 | const didRender = 'rendered' in container.dataset; 45 | if ( didRender ) { 46 | ReactDOMHydrate( 47 | component, 48 | container, 49 | undefined 50 | ); 51 | } else { 52 | ReactDOMRender( 53 | component, 54 | container 55 | ); 56 | } 57 | break; 58 | } 59 | 60 | default: 61 | throw new Error( `Unknown environment "${ environment }"` ); 62 | } 63 | } 64 | 65 | if ( apiFetch ) { 66 | apiFetch.use( ( options, next ) => { 67 | if ( window.SSRHydrationData && window.SSRHydrationData[ JSON.stringify( options ) ] ) { 68 | const [ status, data ] = window.SSRHydrationData[ JSON.stringify( options ) ]; 69 | return new Promise( ( resolve, reject ) => { 70 | if ( status < 400 ) { 71 | resolve( data ); 72 | } else { 73 | reject( data ); 74 | } 75 | } ); 76 | 77 | } 78 | return next( options ); 79 | } ); 80 | } 81 | 82 | function getBlockSsrData() { 83 | 84 | // In SSR, return the global from v8js. 85 | if ( 'blockSsrData' in window ) { 86 | return window.blockSsrData; 87 | } 88 | 89 | // When hydrating on the frontend, parse the data attributes from the current script. 90 | const scriptDataset = document.currentScript.dataset.blockSsrData; 91 | return ( typeof scriptDataset !== 'undefined' ) ? JSON.parse( scriptDataset ) : {}; 92 | } 93 | 94 | function useApiFetch( args ) { 95 | if ( getEnvironment() === 'browser' ) { 96 | let defaultIsLoading = true; 97 | let defaultData = null; 98 | let defaultError = null; 99 | if ( window.SSRHydrationData && window.SSRHydrationData[ JSON.stringify( args ) ] ) { 100 | const [ status, data ] = window.SSRHydrationData[ JSON.stringify( args ) ]; 101 | defaultIsLoading = false; 102 | if ( status < 400 ) { 103 | defaultData = data; 104 | } else { 105 | defaultError = data; 106 | } 107 | 108 | } 109 | const [ isLoading, setIsLoading ] = useState( defaultIsLoading ); 110 | const [ data, setData ] = useState( defaultData ); 111 | const [ error, setError ] = useState( defaultError ); 112 | 113 | useEffect( () => { 114 | setIsLoading( true ); 115 | apiFetch( args ) 116 | .then( res => { 117 | setData( res ); 118 | setIsLoading( false ); 119 | } ) 120 | .catch( err => { 121 | setError( err ); 122 | setIsLoading( false ); 123 | } ); 124 | }, [ JSON.stringify( args ) ] ); 125 | 126 | return [ isLoading, data, error ]; 127 | } else if ( getEnvironment() === 'server' ) { 128 | const [ status, data ] = global.PHP.apiFetch( args ); 129 | if ( status >= 400 ) { 130 | return [ false, null, data ]; 131 | } 132 | return [ false, data, null ]; 133 | } 134 | 135 | return [ false, [], null ]; 136 | } 137 | 138 | window.BlockEditorSSR = { 139 | render: render, 140 | onBackend: onBackend, 141 | onFrontend: onFrontend, 142 | getEnvironment: getEnvironment, 143 | getBlockSsrData: getBlockSsrData, 144 | useApiFetch: useApiFetch, 145 | }; 146 | -------------------------------------------------------------------------------- /inc/url-search-params.js: -------------------------------------------------------------------------------- 1 | /**! 2 | * url-search-params-polyfill 3 | * 4 | * @author Jerry Bendy (https://github.com/jerrybendy) 5 | * @licence MIT 6 | */ 7 | (function(self) { 8 | 'use strict'; 9 | 10 | var nativeURLSearchParams = (function() { 11 | // #41 Fix issue in RN 12 | try { 13 | if (self.URLSearchParams && (new self.URLSearchParams('foo=bar')).get('foo') === 'bar') { 14 | return self.URLSearchParams; 15 | } 16 | } catch (e) {} 17 | return null; 18 | })(), 19 | isSupportObjectConstructor = nativeURLSearchParams && (new nativeURLSearchParams({a: 1})).toString() === 'a=1', 20 | // There is a bug in safari 10.1 (and earlier) that incorrectly decodes `%2B` as an empty space and not a plus. 21 | decodesPlusesCorrectly = nativeURLSearchParams && (new nativeURLSearchParams('s=%2B').get('s') === '+'), 22 | __URLSearchParams__ = "__URLSearchParams__", 23 | // Fix bug in Edge which cannot encode ' &' correctly 24 | encodesAmpersandsCorrectly = nativeURLSearchParams ? (function() { 25 | var ampersandTest = new nativeURLSearchParams(); 26 | ampersandTest.append('s', ' &'); 27 | return ampersandTest.toString() === 's=+%26'; 28 | })() : true, 29 | prototype = URLSearchParamsPolyfill.prototype, 30 | iterable = !!(self.Symbol && self.Symbol.iterator); 31 | 32 | if (nativeURLSearchParams && isSupportObjectConstructor && decodesPlusesCorrectly && encodesAmpersandsCorrectly) { 33 | return; 34 | } 35 | 36 | 37 | /** 38 | * Make a URLSearchParams instance 39 | * 40 | * @param {object|string|URLSearchParams} search 41 | * @constructor 42 | */ 43 | function URLSearchParamsPolyfill(search) { 44 | search = search || ""; 45 | 46 | // support construct object with another URLSearchParams instance 47 | if (search instanceof URLSearchParams || search instanceof URLSearchParamsPolyfill) { 48 | search = search.toString(); 49 | } 50 | this [__URLSearchParams__] = parseToDict(search); 51 | } 52 | 53 | 54 | /** 55 | * Appends a specified key/value pair as a new search parameter. 56 | * 57 | * @param {string} name 58 | * @param {string} value 59 | */ 60 | prototype.append = function(name, value) { 61 | appendTo(this [__URLSearchParams__], name, value); 62 | }; 63 | 64 | /** 65 | * Deletes the given search parameter, and its associated value, 66 | * from the list of all search parameters. 67 | * 68 | * @param {string} name 69 | */ 70 | prototype['delete'] = function(name) { 71 | delete this [__URLSearchParams__] [name]; 72 | }; 73 | 74 | /** 75 | * Returns the first value associated to the given search parameter. 76 | * 77 | * @param {string} name 78 | * @returns {string|null} 79 | */ 80 | prototype.get = function(name) { 81 | var dict = this [__URLSearchParams__]; 82 | return this.has(name) ? dict[name][0] : null; 83 | }; 84 | 85 | /** 86 | * Returns all the values association with a given search parameter. 87 | * 88 | * @param {string} name 89 | * @returns {Array} 90 | */ 91 | prototype.getAll = function(name) { 92 | var dict = this [__URLSearchParams__]; 93 | return this.has(name) ? dict [name].slice(0) : []; 94 | }; 95 | 96 | /** 97 | * Returns a Boolean indicating if such a search parameter exists. 98 | * 99 | * @param {string} name 100 | * @returns {boolean} 101 | */ 102 | prototype.has = function(name) { 103 | return hasOwnProperty(this [__URLSearchParams__], name); 104 | }; 105 | 106 | /** 107 | * Sets the value associated to a given search parameter to 108 | * the given value. If there were several values, delete the 109 | * others. 110 | * 111 | * @param {string} name 112 | * @param {string} value 113 | */ 114 | prototype.set = function set(name, value) { 115 | this [__URLSearchParams__][name] = ['' + value]; 116 | }; 117 | 118 | /** 119 | * Returns a string containg a query string suitable for use in a URL. 120 | * 121 | * @returns {string} 122 | */ 123 | prototype.toString = function() { 124 | var dict = this[__URLSearchParams__], query = [], i, key, name, value; 125 | for (key in dict) { 126 | name = encode(key); 127 | for (i = 0, value = dict[key]; i < value.length; i++) { 128 | query.push(name + '=' + encode(value[i])); 129 | } 130 | } 131 | return query.join('&'); 132 | }; 133 | 134 | // There is a bug in Safari 10.1 and `Proxy`ing it is not enough. 135 | var forSureUsePolyfill = !decodesPlusesCorrectly; 136 | var useProxy = (!forSureUsePolyfill && nativeURLSearchParams && !isSupportObjectConstructor && self.Proxy); 137 | var propValue; 138 | if (useProxy) { 139 | // Safari 10.0 doesn't support Proxy, so it won't extend URLSearchParams on safari 10.0 140 | propValue = new Proxy(nativeURLSearchParams, { 141 | construct: function (target, args) { 142 | return new target((new URLSearchParamsPolyfill(args[0]).toString())); 143 | } 144 | }) 145 | // Chrome <=60 .toString() on a function proxy got error "Function.prototype.toString is not generic" 146 | propValue.toString = Function.prototype.toString.bind(URLSearchParamsPolyfill); 147 | } else { 148 | propValue = URLSearchParamsPolyfill; 149 | } 150 | /* 151 | * Apply polifill to global object and append other prototype into it 152 | */ 153 | Object.defineProperty(self, 'URLSearchParams', { 154 | value: propValue 155 | }); 156 | 157 | var USPProto = self.URLSearchParams.prototype; 158 | 159 | USPProto.polyfill = true; 160 | 161 | /** 162 | * 163 | * @param {function} callback 164 | * @param {object} thisArg 165 | */ 166 | USPProto.forEach = USPProto.forEach || function(callback, thisArg) { 167 | var dict = parseToDict(this.toString()); 168 | Object.getOwnPropertyNames(dict).forEach(function(name) { 169 | dict[name].forEach(function(value) { 170 | callback.call(thisArg, value, name, this); 171 | }, this); 172 | }, this); 173 | }; 174 | 175 | /** 176 | * Sort all name-value pairs 177 | */ 178 | USPProto.sort = USPProto.sort || function() { 179 | var dict = parseToDict(this.toString()), keys = [], k, i, j; 180 | for (k in dict) { 181 | keys.push(k); 182 | } 183 | keys.sort(); 184 | 185 | for (i = 0; i < keys.length; i++) { 186 | this['delete'](keys[i]); 187 | } 188 | for (i = 0; i < keys.length; i++) { 189 | var key = keys[i], values = dict[key]; 190 | for (j = 0; j < values.length; j++) { 191 | this.append(key, values[j]); 192 | } 193 | } 194 | }; 195 | 196 | /** 197 | * Returns an iterator allowing to go through all keys of 198 | * the key/value pairs contained in this object. 199 | * 200 | * @returns {function} 201 | */ 202 | USPProto.keys = USPProto.keys || function() { 203 | var items = []; 204 | this.forEach(function(item, name) { 205 | items.push(name); 206 | }); 207 | return makeIterator(items); 208 | }; 209 | 210 | /** 211 | * Returns an iterator allowing to go through all values of 212 | * the key/value pairs contained in this object. 213 | * 214 | * @returns {function} 215 | */ 216 | USPProto.values = USPProto.values || function() { 217 | var items = []; 218 | this.forEach(function(item) { 219 | items.push(item); 220 | }); 221 | return makeIterator(items); 222 | }; 223 | 224 | /** 225 | * Returns an iterator allowing to go through all key/value 226 | * pairs contained in this object. 227 | * 228 | * @returns {function} 229 | */ 230 | USPProto.entries = USPProto.entries || function() { 231 | var items = []; 232 | this.forEach(function(item, name) { 233 | items.push([name, item]); 234 | }); 235 | return makeIterator(items); 236 | }; 237 | 238 | 239 | if (iterable) { 240 | USPProto[self.Symbol.iterator] = USPProto[self.Symbol.iterator] || USPProto.entries; 241 | } 242 | 243 | 244 | function encode(str) { 245 | var replace = { 246 | '!': '%21', 247 | "'": '%27', 248 | '(': '%28', 249 | ')': '%29', 250 | '~': '%7E', 251 | '%20': '+', 252 | '%00': '\x00' 253 | }; 254 | return encodeURIComponent(str).replace(/[!'\(\)~]|%20|%00/g, function(match) { 255 | return replace[match]; 256 | }); 257 | } 258 | 259 | function decode(str) { 260 | return str 261 | .replace(/[ +]/g, '%20') 262 | .replace(/(%[a-f0-9]{2})+/ig, function(match) { 263 | return decodeURIComponent(match); 264 | }); 265 | } 266 | 267 | function makeIterator(arr) { 268 | var iterator = { 269 | next: function() { 270 | var value = arr.shift(); 271 | return {done: value === undefined, value: value}; 272 | } 273 | }; 274 | 275 | if (iterable) { 276 | iterator[self.Symbol.iterator] = function() { 277 | return iterator; 278 | }; 279 | } 280 | 281 | return iterator; 282 | } 283 | 284 | function parseToDict(search) { 285 | var dict = {}; 286 | 287 | if (typeof search === "object") { 288 | // if `search` is an array, treat it as a sequence 289 | if (isArray(search)) { 290 | for (var i = 0; i < search.length; i++) { 291 | var item = search[i]; 292 | if (isArray(item) && item.length === 2) { 293 | appendTo(dict, item[0], item[1]); 294 | } else { 295 | throw new TypeError("Failed to construct 'URLSearchParams': Sequence initializer must only contain pair elements"); 296 | } 297 | } 298 | 299 | } else { 300 | for (var key in search) { 301 | if (search.hasOwnProperty(key)) { 302 | appendTo(dict, key, search[key]); 303 | } 304 | } 305 | } 306 | 307 | } else { 308 | // remove first '?' 309 | if (search.indexOf("?") === 0) { 310 | search = search.slice(1); 311 | } 312 | 313 | var pairs = search.split("&"); 314 | for (var j = 0; j < pairs.length; j++) { 315 | var value = pairs [j], 316 | index = value.indexOf('='); 317 | 318 | if (-1 < index) { 319 | appendTo(dict, decode(value.slice(0, index)), decode(value.slice(index + 1))); 320 | 321 | } else { 322 | if (value) { 323 | appendTo(dict, decode(value), ''); 324 | } 325 | } 326 | } 327 | } 328 | 329 | return dict; 330 | } 331 | 332 | function appendTo(dict, name, value) { 333 | var val = typeof value === 'string' ? value : ( 334 | value !== null && value !== undefined && typeof value.toString === 'function' ? value.toString() : JSON.stringify(value) 335 | ); 336 | 337 | // #47 Prevent using `hasOwnProperty` as a property name 338 | if (hasOwnProperty(dict, name)) { 339 | dict[name].push(val); 340 | } else { 341 | dict[name] = [val]; 342 | } 343 | } 344 | 345 | function isArray(val) { 346 | return !!val && '[object Array]' === Object.prototype.toString.call(val); 347 | } 348 | 349 | function hasOwnProperty(obj, prop) { 350 | return Object.prototype.hasOwnProperty.call(obj, prop); 351 | } 352 | 353 | })(typeof global !== 'undefined' ? global : (typeof window !== 'undefined' ? window : this)); 354 | -------------------------------------------------------------------------------- /plugin.php: -------------------------------------------------------------------------------- 1 |