Make WordPress Core

Opened 12 years ago

Closed 12 years ago

Last modified 12 years ago

#21812 closed task (blessed) (fixed)

Make custom TinyMCE views easier to implement

Reported by: koopersmith's profile koopersmith Owned by: azaozz's profile azaozz
Milestone: 3.5 Priority: high
Severity: normal Version: 3.5
Component: TinyMCE Keywords: has-patch
Focuses: Cc:


Currently, adding a custom view to TinyMCE is quite difficult. Given MCE's capability to mark html as non-editable, we should make improving the editor along these lines as easy as possible.

Potential applications include views and interactions for shortcodes, oEmbed, media objects, and more.

See #21390.

Attachments (5)

21812.patch (9.3 KB) - added by azaozz 12 years ago.
21812-2.patch (10.1 KB) - added by azaozz 12 years ago.
21812-3.patch (10.1 KB) - added by azaozz 12 years ago.
21812-4.patch (10.6 KB) - added by azaozz 12 years ago.
21812.diff (9.5 KB) - added by koopersmith 12 years ago.

Download all attachments as: .zip

Change History (37)

#1 @bpetty
12 years ago

  • Keywords needs-patch added

#2 @nacin
12 years ago

  • Owner set to azaozz
  • Status changed from new to assigned

#3 @nacin
12 years ago

  • Type changed from enhancement to task (blessed)

#4 @koopersmith
12 years ago

  • Priority changed from normal to high

#5 @koopersmith
12 years ago

Based on a conversation with azaozz in IRC, this is how we're going to proceed:

On the TinyMCE side, you can create a preview type. Each preview can have multiple instances within MCE. TinyMCE requires that some sort of preview html be provided on render, so we will handle the creation of placeholder divs. The external JS will either immediately replace them, or replace them once their content has loaded (the latter will be especially useful when using AJAX requests).

  1. A preview object provides a regex, which is used to scan the content periodically.
  1. When content is replaced, it triggers a preview-provided callback that will either replace the div on the spot, or leave it as-is and replace it at a later point.
  1. When MCE needs to generate the text representation, it will either use a data attribute on the preview or trigger another callback (if provided). It will default to using the original text.

azaozz is working on this, and barring any blockers, will have a patch later today.

12 years ago

#6 @azaozz
12 years ago

A very crude first run patch. Still missing some things but the main functionality is there.

12 years ago

#7 @azaozz
12 years ago

In 21812-2.patch:

  • Added cleanup_callback called when the preview is being removed.
  • Fixed creating previews on paste and on inserting content in the editor.
  • Fixed lots of bugs.


  • Better CSS.
  • More testing with very big posts and many previews.


  • Abstract the calculation of the visible part of a preview and pass the rectangle in the events callbacks (the code currently showing popup buttons on the images).

12 years ago

#8 @azaozz
12 years ago

In 21812-3.patch:

  • Added scanning/detecting of previews when the user types the "trigger" string (on enter).
  • Improved timing/handling for firing the node_callback.
  • Moved couple of functions to match the standard TinyMCE plugin structure.

12 years ago

#9 @azaozz
12 years ago

In 21812-4.patch:

  • Added option for previews that are shown inline controlled by the wrapper tags (either 'div' or 'span').

12 years ago

#10 @koopersmith
12 years ago

  • Keywords has-patch added; needs-patch removed

21812.diff takes the techniques Andrew has been working on and transforms them into an API on the wp object, namely wp.mce.view, which acts as a bridge between TinyMCE and Backbone Views.

The patch differs from 21812-4.patch in several ways:

  • Blocker divs are not included — we're frequently going to add UI inside the views, so blocking interactions to the view will frequently degrade the experience.
  • Fixes several encoding and decoding bugs that caused errors when switching between editors.
  • Views are rendered instantly; there is no longer a need to call setTimeout.
  • There is no scanning on keydown. This should be added in again. I experimented with a few other methods, like scoping to the active node, so this will be worth experimenting with.
  • There is currently no way to add a preview for only certain editor instances. This should be implemented as a function on the object passed to wp.mce.view.add().

It's also worth noting that none of the provided patches isolates the replacement text when scanning it with multiple patterns. This could lead to collisions and encoding errors. When scanning, we should progressively split the string to ensure patterns won't collide or overlap.

For an example, this code will identify and replace the string [text] with the string My test view., and record all clicks to the console.

	wp.mce.view.add( 'test', {
		pattern: /\[test\]/g,
		view: {
			events: {
				'click': 'clicked'

			render: function() {
				this.$el.text('My test view.');

			clicked: function() {
				console.log('I was clicked!');

#11 @koopersmith
12 years ago

  • Resolution set to fixed
  • Status changed from assigned to closed

In [21961]:

Makes custom TinyMCE views easier to implement.

For details and examples, see the ticket.

props azaozz, fixes #21812.

#12 @koopersmith
12 years ago

In [21962]:

Improve documentation for wp.mce.view.add(). Store view id in view options. see #21812.

#13 @koopersmith
12 years ago

In [22004]:

Add JavaScript methods for handling shortcodes.

Adds wp.shortcode, a set of methods used for parsing shortcodes out of content. Also adds a default set of shortcode properties to wp.mce.view.

fixes #21996, see #21812, #21813, #21815.

#14 follow-up: @scribu
12 years ago

Not sure if this is the correct ticket, but I'm getting the following error on CPT edit screens that don't have 'editor' in the 'supports' array:

ReferenceError: tinymce is not defined
tinymce.onAddEditor.add(function(mce, ed){

post.js (line 703)

#15 in reply to: ↑ 14 @koopersmith
12 years ago

Replying to scribu:

Not sure if this is the correct ticket, but I'm getting the following error on CPT edit screens that don't have 'editor' in the 'supports' array:

ReferenceError: tinymce is not defined
tinymce.onAddEditor.add(function(mce, ed){

post.js (line 703)

From the looks of it, it doesn't seem related. Can you post a stack trace?

#16 follow-up: @scribu
12 years ago

Uncaught ReferenceError: tinymce is not defined post.js:703
(anonymous function) post.js:703
p.Callbacks.k jquery.js:2
p.Callbacks.l.fireWith jquery.js:2
p.extend.ready jquery.js:2

#17 @koopersmith
12 years ago

In [22012]:

First pass on TinyMCE attachment in-editor UI.

  • Adds in-editor UI for image attachments. Most of this UI should be able to migrate to all images in a future commit.
  • Removes the wpeditimage TinyMCE plugin from the default plugins array.
  • Add, a helper method to constrain dimensions based upon a maximum width and/or height.
  • Add html attribute parsing and string generation utilities (currently stored in mce-views).
  • Calling remove on a TinyMCE views now ensures that the the parent and references are removed as well.
  • Fixes a bug where we weren't sending the full array of results to matches in wp.mce.view.

see #21390, #21812, #21813.

#18 in reply to: ↑ 16 @azaozz
12 years ago

Replying to scribu:

This was caused by #21718, fixed in [22017].

#19 @koopersmith
12 years ago

In [22023]:

Remove internal TinyMCE attributes when generating the HTML for attachment views.

Moves the HTML utility functions to the top of mce-views.js. Also adds wp.mce.view.removeInternalAttrs( attrs ) and wp.mce.view.attrs( content ).

see #21390, #21812, #21813.

#20 @koopersmith
12 years ago

In [22102]:

MCE Views: Use default shortcode properties when the shortcode parameter is set.

Prevents devs from having to manually extend the default shortcode property object.

see #21390, #21812.

#21 @scribu
12 years ago

Whether I use the new Beta Media button or the old link, after I insert an image into a post, in Visual mode, I can't switch to Text mode:

Uncaught TypeError: Cannot read property 'width' of undefined mce-view.js:394 mce-view.js:394
(anonymous function) media-upload.js:112 underscore.min.js:5
r.(anonymous function) backbone.min.js:25
(anonymous function) media-upload.js:111
g.Events.trigger backbone.min.js:9
media.controller.Workflow.Backbone.Model.extend.update media-views.js:122 media-views.js:477
p.event.dispatch jquery.js:2

#22 @scribu
12 years ago

  • Resolution fixed deleted
  • Status changed from closed to reopened

#23 @scribu
12 years ago

  • Resolution set to fixed
  • Status changed from reopened to closed

I was playing with add_image_size() and was missing the default image sizes.

False alarm.

#24 @koopersmith
12 years ago

In [22120]:

Use the new media modal to insert galleries into TinyMCE and the text editor.


  • Gallery insertion from the new media modal (into TinyMCE, the text editor, etc).
  • Gallery previews in TinyMCE now use the wp.mce.views API.
  • Disables the TinyMCE wpgallery plugin.
  • Gallery previews consist of the first image of the gallery and the appearance of a stack. This will later be fleshed out to include more images/functionality (including editing the gallery, gallery properties, and showing the number of images in the gallery).
  • Multiple galleries can be added to a single post.
  • The gallery MCE view provides a bridge between the wp.shortcode and Attachments representation of a gallery, which allows the existing collection to persist when a gallery is initially created (preventing a request to the server for the query).


  • Renames wp.shortcode.Match to wp.shortcode to better expose the shortcode constructor.
  • The wp.shortcode constructor now accepts an object of options instead of a wp.shortcode.regexp() match.
  • A wp.shortcode instance can be created from a wp.shortcode.regexp() match by calling wp.shortcode.fromMatch( match ).
  • Adds wp.shortcode.string(), which takes a set of shortcode parameters and converts them into a string.* Renames wp.shortcode.prototype.text() to wp.shortcode.prototype.string().
  • Adds an additional capture group to wp.shortcode.regexp() that records whether or not the shortcode has a closing tag. This allows us to improve the accuracy of the syntax used when transforming a shortcode object back into a string.

Media Models

  • Prevents media Query models from observing the central Attachments.all object when query args without corresponding filters are set (otherwise, queries quickly amass false positives).
  • Adds post__in, post__not_in, and post_parent as acceptable JS attachment Query args.
  • Attachments.more() always returns a $.promise object.

see #21390, #21809, #21812, #21815, #21817.

#25 @koopersmith
12 years ago

In [22162]:

Add a class to the TinyMCE view end marker.

Certain TinyMCE plugins scan for all spans in the document and remove any that do not have classes (or a handful of other attributes). This is quite aggressive, and breaks the regex used to scan for MCE views when transforming them back to plain text. Adding a class ensures that any plugins that do so will not break MCE views.

see #21812, #21390.

#26 @koopersmith
12 years ago

In [22208]:

MCE Views: Add selection/deselection when a view is clicked.

These methods should be expanded to prevent content from being inserted into the view by blocking and rerouting keystrokes as appropriate as well as repositioning the caret before content is inserted.

see #21390, #21812, #21813, #21815.

#25 @koopersmith
12 years ago

In [22210]:

MCE Views: First pass at keystroke and insertion handling.

  • When a MCE view is selected, normal keystrokes (those without command/control) are blocked.
  • Backspace/delete will successfully remove the view.
  • Arrow handling will be added at a later point.
  • When a MCE view is selected and content is pasted or injected (e.g. when an attachment is added), the content is added to the first text node after the view. If the view is at the end of the document or the next element is another view, a text node is injected into the DOM immediately after the selected view.

see #21390, #21812, #21813, #21815.

#26 @koopersmith
12 years ago

In [22219]:

MCE Views: Add alignment styles for generic views.

Adds alignnone, aligncenter, alignleft, and alignright by default.

see #21390, #21812, #21813.

#27 @koopersmith
12 years ago

In [22220]:

Properly align MCE attachment views.

  • Adds a $wrapper property to MCE views to allow them to manipulate the wrapper on render. This should be used sparingly — only for layout changes that cannot be accomplished through altering the wrapper's children — and likely only for adding/removing classes.
  • Uses wp.html.string() in wp.mce.view.toView().

see #21390, #21812, #21813.

#28 @azaozz
12 years ago

Small improvement for [22210], we don't need to do this "by hand":

isView : function( node ) {
	return (/(?:^|\s)wp-view-wrap(?:\s|$)/).test( node.className );

Can use ed.dom.hasClass(node, 'wp-view-wrap') or if it needs regex perhaps this:

isView : function( node ) {
	return node.className ? /\bwp-view-wrap\b/.test( node.className ) : false;
Last edited 12 years ago by azaozz (previous) (diff)

#29 follow-up: @lippe
12 years ago

[Edit] Sorry, I was a bit trigger happy. Read the whole changeset comment and realised views aren't going into 3.5. Sorry about that.

First time commenter here - I'm not sure if I'm supposed to comment on a fixed ticket, but here goes.

I found that in [22567] the wpviews TinyMCE plugin was deactivated. Wouldn't that make this a non-fixed issue? The plugin is still left in core so I'm assuming there's a plan to reactivate it some time.

Last edited 12 years ago by lippe (previous) (diff)

#30 in reply to: ↑ 29 @SergeyBiryukov
12 years ago

Replying to lippe:

I found that in [22567] the wpviews TinyMCE plugin was deactivated. Wouldn't that make this a non-fixed issue?

It's still considered as fixed for 3.5.

The plugin is still left in core so I'm assuming there's a plan to reactivate it some time.

Correct. That should happen in a new ticket.

Note: See TracTickets for help on using tickets.