Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove TinyMCE-based parser #1150

Merged
merged 4 commits into from
Jun 14, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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