Skip to content

Commit

Permalink
Remove TinyMCE-based parser (#1150)
Browse files Browse the repository at this point in the history
  • Loading branch information
nylen authored Jun 14, 2017
1 parent 4c4f96d commit 8194eca
Show file tree
Hide file tree
Showing 4 changed files with 122 additions and 256 deletions.
127 changes: 1 addition & 126 deletions blocks/api/parser.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
/**
* External dependencies
*/
import tinymce from 'tinymce';
import { parse as hpqParse } from 'hpq';
import { escape, unescape, pickBy } from 'lodash';
import { pickBy } from 'lodash';

/**
* Internal dependencies
Expand Down Expand Up @@ -95,130 +94,6 @@ export function createBlockWithFallback( name, rawContent, attributes ) {
}
}

/**
* Parses the post content with TinyMCE and returns a list of blocks.
*
* @param {String} content The post content
* @return {Array} Block list
*/
export function parseWithTinyMCE( content ) {
// First, convert comment delimiters into temporary <wp-block> "tags" so
// that TinyMCE can parse them. Examples:
// In : <!-- wp:core/text -->
// Out : <wp-block slug="core/text">
// In : <!-- /wp:core/text -->
// Out : </wp-block>
// In : <!-- wp:core/embed url:youtube.com/xxx& -->
// Out : <wp-block slug="core/embed" attributes="url:youtube.com/xxx&amp;">
content = content.replace(
/<!--\s*(\/?)wp:([a-z0-9/-]+)((?:\s+[a-z0-9_-]+(="[^"]*")?)*)\s*-->/g,
function( match, closingSlash, slug, attributes ) {
if ( closingSlash ) {
return '</wp-block>';
}

if ( attributes ) {
attributes = ' attributes="' + escape( attributes.trim() ) + '"';
}
return '<wp-block slug="' + slug + '"' + attributes + '>';
}
);

// Create a custom HTML schema.
const schema = new tinymce.html.Schema();

// Add <wp-block> "tags" to our schema.
schema.addCustomElements( 'wp-block' );

// Add valid <wp-block> "attributes" also.
schema.addValidElements( 'wp-block[slug|attributes]' );

// Initialize the parser with our custom schema.
const parser = new tinymce.html.DomParser( { validate: true }, schema );

// Parse the content into an object tree.
const tree = parser.parse( content );

// Create a serializer that we will use to pass strings to blocks.
// TODO: pass parse trees instead, and verify them against the markup
// shapes that each block can accept.
const serializer = new tinymce.html.Serializer( { validate: true }, schema );

// Walk the tree and initialize blocks.
const blocks = [];

// Store markup we found in between blocks.
let contentBetweenBlocks = null;
function flushContentBetweenBlocks() {
if ( contentBetweenBlocks && contentBetweenBlocks.firstChild ) {
const block = createBlockWithFallback(
null, // default: unknown type handler
serializer.serialize( contentBetweenBlocks ),
null // no known attributes
);
if ( block ) {
blocks.push( block );
}
}
contentBetweenBlocks = new tinymce.html.Node( 'body', 11 );
}
flushContentBetweenBlocks();

let currentNode = tree.firstChild;
while ( currentNode ) {
if ( currentNode.name === 'wp-block' ) {
// Set node type to document fragment so that the TinyMCE
// serializer doesn't output its markup.
currentNode.type = 11;

// Serialize the content
const rawContent = serializer.serialize( currentNode );

// Retrieve the attributes from the <wp-block> tag.
const nodeAttributes = currentNode.attributes.reduce( ( memo, attr ) => {
memo[ attr.name ] = attr.value;
return memo;
}, {} );

// Retrieve the block attributes from the original delimiters.
const attributesMatcher = /([a-z0-9_-]+)(="([^"]*)")?/g;
const blockAttributes = {};
let match;
do {
match = attributesMatcher.exec( unescape( nodeAttributes.attributes || '' ) );
if ( match ) {
blockAttributes[ match[ 1 ] ] = !! match[ 2 ] ? match[ 3 ] : true;
}
} while ( !! match );

// Try to create the block.
const block = createBlockWithFallback(
nodeAttributes.slug,
rawContent,
blockAttributes
);
if ( block ) {
flushContentBetweenBlocks();
blocks.push( block );
}

currentNode = currentNode.next;
} else {
// We have some HTML content outside of block delimiters. Save it
// so that we can initialize it using `getUnknownTypeHandler`.
const toAppend = currentNode;
// Advance the DOM tree pointer before calling `append` because
// this is a destructive operation.
currentNode = currentNode.next;
contentBetweenBlocks.append( toAppend );
}
}

flushContentBetweenBlocks();

return blocks;
}

/**
* Parses the post content with a PegJS grammar and returns a list of blocks.
*
Expand Down
Loading

0 comments on commit 8194eca

Please sign in to comment.