]> scripts.mit.edu Git - autoinstalls/wordpress.git/blob - wp-includes/js/tinymce/plugins/wpview/plugin.js
WordPress 4.1-scripts
[autoinstalls/wordpress.git] / wp-includes / js / tinymce / plugins / wpview / plugin.js
1 /* global tinymce */
2
3 /**
4  * WordPress View plugin.
5  */
6 tinymce.PluginManager.add( 'wpview', function( editor ) {
7         var selected,
8                 Env = tinymce.Env,
9                 VK = tinymce.util.VK,
10                 TreeWalker = tinymce.dom.TreeWalker,
11                 toRemove = false,
12                 firstFocus = true,
13                 _noop = function() { return false; },
14                 isios = /iPad|iPod|iPhone/.test( navigator.userAgent ),
15                 cursorInterval, lastKeyDownNode, setViewCursorTries, focus, execCommandView, execCommandBefore;
16
17         function getView( node ) {
18                 return getParent( node, 'wpview-wrap' );
19         }
20
21         /**
22          * Returns the node or a parent of the node that has the passed className.
23          * Doing this directly is about 40% faster
24          */
25         function getParent( node, className ) {
26                 while ( node && node.parentNode ) {
27                         if ( node.className && ( ' ' + node.className + ' ' ).indexOf( ' ' + className + ' ' ) !== -1 ) {
28                                 return node;
29                         }
30
31                         node = node.parentNode;
32                 }
33
34                 return false;
35         }
36
37         /**
38          * Get the text/shortcode string for a view.
39          *
40          * @param view The view wrapper's node
41          * @returns string The text/shoercode string of the view
42          */
43         function getViewText( view ) {
44                 if ( view = getView( view ) ) {
45                         return window.decodeURIComponent( editor.dom.getAttrib( view, 'data-wpview-text' ) || '' );
46                 }
47
48                 return '';
49         }
50
51         /**
52          * Set the view's original text/shortcode string
53          *
54          * @param view The view wrapper's HTML id or node
55          * @param text The text string to be set
56          */
57         function setViewText( view, text ) {
58                 view = getView( view );
59
60                 if ( view ) {
61                         editor.dom.setAttrib( view, 'data-wpview-text', window.encodeURIComponent( text || '' ) );
62                         return true;
63                 }
64
65                 return false;
66         }
67
68         function _stop( event ) {
69                 event.stopPropagation();
70         }
71
72         function setViewCursor( before, view ) {
73                 var location = before ? 'before' : 'after',
74                         offset = before ? 0 : 1;
75                 deselect();
76                 editor.selection.setCursorLocation( editor.dom.select( '.wpview-selection-' + location, view )[0], offset );
77                 editor.nodeChanged();
78         }
79
80         function handleEnter( view, before, key ) {
81                 var dom = editor.dom,
82                         padNode = dom.create( 'p' );
83
84                 if ( ! ( Env.ie && Env.ie < 11 ) ) {
85                         padNode.innerHTML = '<br data-mce-bogus="1">';
86                 }
87
88                 if ( before ) {
89                         view.parentNode.insertBefore( padNode, view );
90                 } else {
91                         dom.insertAfter( padNode, view );
92                 }
93
94                 deselect();
95
96                 if ( before && key === VK.ENTER ) {
97                         setViewCursor( before, view );
98                 } else {
99                         editor.selection.setCursorLocation( padNode, 0 );
100                 }
101
102                 editor.nodeChanged();
103         }
104
105         function removeView( view ) {
106                 // TODO: trigger an event to run a clean up function.
107                 // Maybe `jQuery( view ).trigger( 'remove' );`?
108                 editor.undoManager.transact( function() {
109                         handleEnter( view );
110                         editor.dom.remove( view );
111                 });
112         }
113
114         function select( viewNode ) {
115                 var clipboard,
116                         dom = editor.dom;
117
118                 if ( ! viewNode ) {
119                         return;
120                 }
121
122                 // Adjust the toolbar position and bail if node is already selected.
123                 if ( viewNode === selected ) {
124                         adjustToolbarPosition( viewNode );
125                         return;
126                 }
127
128                 // Make sure that the editor is focused.
129                 // It is possible that the editor is not focused when the mouse event fires
130                 // without focus, the selection will not work properly.
131                 editor.getBody().focus();
132
133                 deselect();
134                 selected = viewNode;
135                 dom.setAttrib( viewNode, 'data-mce-selected', 1 );
136                 adjustToolbarPosition( viewNode );
137
138                 clipboard = dom.create( 'div', {
139                         'class': 'wpview-clipboard',
140                         'contenteditable': 'true'
141                 }, getViewText( viewNode ) );
142
143                 editor.dom.select( '.wpview-body', viewNode )[0].appendChild( clipboard );
144
145                 // Both of the following are necessary to prevent manipulating the selection/focus
146                 dom.bind( clipboard, 'beforedeactivate focusin focusout', _stop );
147                 dom.bind( selected, 'beforedeactivate focusin focusout', _stop );
148
149                 // select the hidden div
150                 if ( isios ) {
151                         editor.selection.select( clipboard );
152                 } else {
153                         editor.selection.select( clipboard, true );
154                 }
155
156                 editor.nodeChanged();
157                 editor.fire( 'wpview-selected', viewNode );
158         }
159
160         function adjustToolbarPosition( viewNode ) {
161                 var delta = 0,
162                         toolbar = editor.$( viewNode ).find( '.toolbar' ),
163                         editorToolbar = tinymce.$( editor.editorContainer ).find( '.mce-toolbar-grp' )[0],
164                         editorToolbarBottom = ( editorToolbar && editorToolbar.getBoundingClientRect().bottom ) || 0;
165                 
166                 if ( toolbar.length && editor.iframeElement ) {
167                         // 48 = 43 for the toolbar + 5 buffer
168                         delta = viewNode.getBoundingClientRect().top + editor.iframeElement.getBoundingClientRect().top - editorToolbarBottom - 48;
169                 }
170
171                 if ( delta < 0 ) {
172                         toolbar.removeClass( 'mce-arrow-down' ).css({ top: ( -43 + delta * -1 ) });
173                 } else if ( delta > 0 && ! toolbar.hasClass( 'mce-arrow-down' ) ) {
174                         toolbar.addClass( 'mce-arrow-down' ).css({ top: '' });
175                 }
176         }
177
178         /**
179          * Deselect a selected view and remove clipboard
180          */
181         function deselect() {
182                 var clipboard,
183                         dom = editor.dom;
184
185                 if ( selected ) {
186                         clipboard = editor.dom.select( '.wpview-clipboard', selected )[0];
187                         dom.unbind( clipboard );
188                         dom.remove( clipboard );
189
190                         dom.unbind( selected, 'beforedeactivate focusin focusout click mouseup', _stop );
191                         dom.setAttrib( selected, 'data-mce-selected', null );
192                 }
193
194                 selected = null;
195         }
196
197         // Check if the `wp.mce` API exists.
198         if ( typeof wp === 'undefined' || ! wp.mce ) {
199                 return {
200                         getViewText: _noop,
201                         setViewText: _noop,
202                         getView: _noop
203                 };
204         }
205
206         // Remove the content of view wrappers from HTML string
207         function emptyViews( content ) {
208                 return content.replace(/<div[^>]+data-wpview-text=\"([^"]+)"[^>]*>[\s\S]+?wpview-selection-after[^>]+>(?:&nbsp;|\u00a0)*<\/p><\/div>/g, '$1' );
209         }
210
211         // Prevent adding undo levels on changes inside a view wrapper
212         editor.on( 'BeforeAddUndo', function( event ) {
213                 if ( event.lastLevel && emptyViews( event.level.content ) === emptyViews( event.lastLevel.content ) ) {
214                         event.preventDefault();
215                 }
216         });
217
218         // When the editor's content changes, scan the new content for
219         // matching view patterns, and transform the matches into
220         // view wrappers.
221         editor.on( 'BeforeSetContent', function( event ) {
222                 var node;
223
224                 if ( ! event.content ) {
225                         return;
226                 }
227
228                 if ( selected ) {
229                         removeView( selected );
230                 }
231
232                 node = editor.selection.getNode();
233
234                 // When a url is pasted, only try to embed it when pasted in an empty paragrapgh.
235                 if ( event.content.match( /^\s*(https?:\/\/[^\s"]+)\s*$/i ) &&
236                         ( node.nodeName !== 'P' || node.parentNode !== editor.getBody() || ! editor.dom.isEmpty( node ) ) ) {
237                         return;
238                 }
239
240                 event.content = wp.mce.views.toViews( event.content );
241         });
242
243         // When the editor's content has been updated and the DOM has been
244         // processed, render the views in the document.
245         editor.on( 'SetContent', function() {
246                 wp.mce.views.render();
247         });
248
249         // Set the cursor before or after a view when clicking next to it.
250         editor.on( 'click', function( event ) {
251                 var x = event.clientX,
252                         y = event.clientY,
253                         body = editor.getBody(),
254                         bodyRect = body.getBoundingClientRect(),
255                         first = body.firstChild,
256                         firstRect = first.getBoundingClientRect(),
257                         last = body.lastChild,
258                         lastRect = last.getBoundingClientRect(),
259                         view;
260
261                 if ( y < firstRect.top && ( view = getView( first ) ) ) {
262                         setViewCursor( true, view );
263                         event.preventDefault();
264                 } else if ( y > lastRect.bottom && ( view = getView( last ) ) ) {
265                         setViewCursor( false, view );
266                         event.preventDefault();
267                 } else {
268                         tinymce.each( editor.dom.select( '.wpview-wrap' ), function( view ) {
269                                 var rect = view.getBoundingClientRect();
270
271                                 if ( y >= rect.top && y <= rect.bottom ) {
272                                         if ( x < bodyRect.left ) {
273                                                 setViewCursor( true, view );
274                                                 event.preventDefault();
275                                         } else if ( x > bodyRect.right ) {
276                                                 setViewCursor( false, view );
277                                                 event.preventDefault();
278                                         }
279                                         return;
280                                 }
281                         });
282                 }
283         });
284
285         editor.on( 'init', function() {
286                 var scrolled = false,
287                         selection = editor.selection,
288                         MutationObserver = window.MutationObserver || window.WebKitMutationObserver;
289
290                 // When a view is selected, ensure content that is being pasted
291                 // or inserted is added to a text node (instead of the view).
292                 editor.on( 'BeforeSetContent', function() {
293                         var walker, target,
294                                 view = getView( selection.getNode() );
295
296                         // If the selection is not within a view, bail.
297                         if ( ! view ) {
298                                 return;
299                         }
300
301                         if ( ! view.nextSibling || getView( view.nextSibling ) ) {
302                                 // If there are no additional nodes or the next node is a
303                                 // view, create a text node after the current view.
304                                 target = editor.getDoc().createTextNode('');
305                                 editor.dom.insertAfter( target, view );
306                         } else {
307                                 // Otherwise, find the next text node.
308                                 walker = new TreeWalker( view.nextSibling, view.nextSibling );
309                                 target = walker.next();
310                         }
311
312                         // Select the `target` text node.
313                         selection.select( target );
314                         selection.collapse( true );
315                 });
316
317                 editor.dom.bind( editor.getDoc(), 'touchmove', function() {
318                         scrolled = true;
319                 });
320
321                 editor.on( 'mousedown mouseup click touchend', function( event ) {
322                         var view = getView( event.target );
323
324                         firstFocus = false;
325
326                         // Contain clicks inside the view wrapper
327                         if ( view ) {
328                                 event.stopImmediatePropagation();
329                                 event.preventDefault();
330
331                                 if ( ( event.type === 'touchend' || event.type === 'mousedown' ) && ! event.metaKey && ! event.ctrlKey ) {
332                                         if ( editor.dom.hasClass( event.target, 'edit' ) ) {
333                                                 wp.mce.views.edit( view );
334                                                 editor.focus();
335                                                 return false;
336                                         } else if ( editor.dom.hasClass( event.target, 'remove' ) ) {
337                                                 removeView( view );
338                                                 return false;
339                                         }
340                                 }
341
342                                 if ( event.type === 'touchend' && scrolled ) {
343                                         scrolled = false;
344                                 } else {
345                                         select( view );
346                                 }
347
348                                 // Returning false stops the ugly bars from appearing in IE11 and stops the view being selected as a range in FF.
349                                 // Unfortunately, it also inhibits the dragging of views to a new location.
350                                 return false;
351                         } else {
352                                 if ( event.type === 'touchend' || event.type === 'mousedown' ) {
353                                         deselect();
354                                 }
355                         }
356
357                         if ( event.type === 'touchend' && scrolled ) {
358                                 scrolled = false;
359                         }
360                 }, true );
361
362                 if ( MutationObserver ) {
363                         new MutationObserver( function() {
364                                 editor.fire( 'wp-body-class-change' );
365                         } )
366                         .observe( editor.getBody(), {
367                                 attributes: true,
368                                 attributeFilter: ['class']
369                         } );
370                 }
371         });
372
373         editor.on( 'PreProcess', function( event ) {
374                 // Empty the wpview wrap nodes
375                 tinymce.each( editor.dom.select( 'div[data-wpview-text]', event.node ), function( node ) {
376                         node.textContent = node.innerText = '\u00a0';
377                 });
378     });
379
380     editor.on( 'PostProcess', function( event ) {
381                 if ( event.content ) {
382                         event.content = event.content.replace( /<div [^>]*?data-wpview-text="([^"]*)"[^>]*>[\s\S]*?<\/div>/g, function( match, shortcode ) {
383                                 if ( shortcode ) {
384                                         return '<p>' + window.decodeURIComponent( shortcode ) + '</p>';
385                                 }
386                                 return ''; // If error, remove the view wrapper
387                         });
388                 }
389         });
390
391         // Excludes arrow keys, delete, backspace, enter, space bar.
392         // Ref: https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent.keyCode
393         function isSpecialKey( key ) {
394                 return ( ( key <= 47 && key !== VK.SPACEBAR && key !== VK.ENTER && key !== VK.DELETE && key !== VK.BACKSPACE && ( key < 37 || key > 40 ) ) ||
395                         key >= 224 || // OEM or non-printable
396                         ( key >= 144 && key <= 150 ) || // Num Lock, Scroll Lock, OEM
397                         ( key >= 91 && key <= 93 ) || // Windows keys
398                         ( key >= 112 && key <= 135 ) ); // F keys
399         }
400
401         // (De)select views when arrow keys are used to navigate the content of the editor.
402         editor.on( 'keydown', function( event ) {
403                 var key = event.keyCode,
404                         dom = editor.dom,
405                         selection = editor.selection,
406                         node, view, cursorBefore, cursorAfter,
407                         range, clonedRange, tempRange;
408
409                 if ( selected ) {
410                         // Ignore key presses that involve the command or control key, but continue when in combination with backspace or v.
411                         // Also ignore the F# keys.
412                         if ( ( ( event.metaKey || event.ctrlKey ) && key !== VK.BACKSPACE && key !== 86 ) || ( key >= 112 && key <= 123 ) ) {
413                                 // Remove the view when pressing cmd/ctrl+x on keyup, otherwise the browser can't copy the content.
414                                 if ( ( event.metaKey || event.ctrlKey ) && key === 88 ) {
415                                         toRemove = selected;
416                                 }
417                                 return;
418                         }
419
420                         view = getView( selection.getNode() );
421
422                         // If the caret is not within the selected view, deselect the view and bail.
423                         if ( view !== selected ) {
424                                 deselect();
425                                 return;
426                         }
427
428                         if ( key === VK.LEFT ) {
429                                 setViewCursor( true, view );
430                                 event.preventDefault();
431                         } else if ( key === VK.UP ) {
432                                 if ( view.previousSibling ) {
433                                         if ( getView( view.previousSibling ) ) {
434                                                 setViewCursor( true, view.previousSibling );
435                                         } else {
436                                                 deselect();
437                                                 selection.select( view.previousSibling, true );
438                                                 selection.collapse();
439                                         }
440                                 } else {
441                                         setViewCursor( true, view );
442                                 }
443                                 event.preventDefault();
444                         } else if ( key === VK.RIGHT ) {
445                                 setViewCursor( false, view );
446                                 event.preventDefault();
447                         } else if ( key === VK.DOWN ) {
448                                 if ( view.nextSibling ) {
449                                         if ( getView( view.nextSibling ) ) {
450                                                 setViewCursor( false, view.nextSibling );
451                                         } else {
452                                                 deselect();
453                                                 selection.setCursorLocation( view.nextSibling, 0 );
454                                         }
455                                 } else {
456                                         setViewCursor( false, view );
457                                 }
458
459                                 event.preventDefault();
460                         // Ignore keys that don't insert anything.
461                         } else if ( ! isSpecialKey( key ) ) {
462                                 removeView( selected );
463
464                                 if ( key === VK.ENTER || key === VK.DELETE || key === VK.BACKSPACE ) {
465                                         event.preventDefault();
466                                 }
467                         }
468                 } else {
469                         if ( event.metaKey || event.ctrlKey || ( key >= 112 && key <= 123 ) ) {
470                                 return;
471                         }
472
473                         node = selection.getNode();
474                         lastKeyDownNode = node;
475                         view = getView( node );
476
477                         // Make sure we don't delete part of a view.
478                         // If the range ends or starts with the view, we'll need to trim it.
479                         if ( ! selection.isCollapsed() ) {
480                                 range = selection.getRng();
481
482                                 if ( view = getView( range.endContainer ) ) {
483                                         clonedRange = range.cloneRange();
484                                         selection.select( view.previousSibling, true );
485                                         selection.collapse();
486                                         tempRange = selection.getRng();
487                                         clonedRange.setEnd( tempRange.endContainer, tempRange.endOffset );
488                                         selection.setRng( clonedRange );
489                                 } else if ( view = getView( range.startContainer ) ) {
490                                         clonedRange = range.cloneRange();
491                                         clonedRange.setStart( view.nextSibling, 0 );
492                                         selection.setRng( clonedRange );
493                                 }
494                         }
495
496                         if ( ! view ) {
497                                 // Make sure we don't eat any content.
498                                 if ( event.keyCode === VK.BACKSPACE ) {
499                                         if ( editor.dom.isEmpty( node ) ) {
500                                                 if ( view = getView( node.previousSibling ) ) {
501                                                         setViewCursor( false, view );
502                                                         editor.dom.remove( node );
503                                                         event.preventDefault();
504                                                 }
505                                         } else if ( ( range = selection.getRng() ) &&
506                                                         range.startOffset === 0 &&
507                                                         range.endOffset === 0 &&
508                                                         ( view = getView( node.previousSibling ) ) ) {
509                                                 setViewCursor( false, view );
510                                                 event.preventDefault();
511                                         }
512                                 }
513                                 return;
514                         }
515
516                         if ( ! ( ( cursorBefore = dom.hasClass( view, 'wpview-selection-before' ) ) ||
517                                         ( cursorAfter = dom.hasClass( view, 'wpview-selection-after' ) ) ) ) {
518                                 return;
519                         }
520
521                         if ( isSpecialKey( key ) ) {
522                                 // ignore
523                                 return;
524                         }
525
526                         if ( ( cursorAfter && key === VK.UP ) || ( cursorBefore && key === VK.BACKSPACE ) ) {
527                                 if ( view.previousSibling ) {
528                                         if ( getView( view.previousSibling ) ) {
529                                                 setViewCursor( false, view.previousSibling );
530                                         } else {
531                                                 if ( dom.isEmpty( view.previousSibling ) && key === VK.BACKSPACE ) {
532                                                         dom.remove( view.previousSibling );
533                                                 } else {
534                                                         selection.select( view.previousSibling, true );
535                                                         selection.collapse();
536                                                 }
537                                         }
538                                 } else {
539                                         setViewCursor( true, view );
540                                 }
541                                 event.preventDefault();
542                         } else if ( cursorAfter && ( key === VK.DOWN || key === VK.RIGHT ) ) {
543                                 if ( view.nextSibling ) {
544                                         if ( getView( view.nextSibling ) ) {
545                                                 setViewCursor( key === VK.RIGHT, view.nextSibling );
546                                         } else {
547                                                 selection.setCursorLocation( view.nextSibling, 0 );
548                                         }
549                                 }
550                                 event.preventDefault();
551                         } else if ( cursorBefore && ( key === VK.UP || key ===  VK.LEFT ) ) {
552                                 if ( view.previousSibling ) {
553                                         if ( getView( view.previousSibling ) ) {
554                                                 setViewCursor( key === VK.UP, view.previousSibling );
555                                         } else {
556                                                 selection.select( view.previousSibling, true );
557                                                 selection.collapse();
558                                         }
559                                 }
560                                 event.preventDefault();
561                         } else if ( cursorBefore && key === VK.DOWN ) {
562                                 if ( view.nextSibling ) {
563                                         if ( getView( view.nextSibling ) ) {
564                                                 setViewCursor( true, view.nextSibling );
565                                         } else {
566                                                 selection.setCursorLocation( view.nextSibling, 0 );
567                                         }
568                                 } else {
569                                         setViewCursor( false, view );
570                                 }
571                                 event.preventDefault();
572                         } else if ( ( cursorAfter && key === VK.LEFT ) || ( cursorBefore && key === VK.RIGHT ) ) {
573                                 select( view );
574                                 event.preventDefault();
575                         } else if ( cursorAfter && key === VK.BACKSPACE ) {
576                                 removeView( view );
577                                 event.preventDefault();
578                         } else if ( cursorAfter ) {
579                                 handleEnter( view );
580                         } else if ( cursorBefore ) {
581                                 handleEnter( view , true, key );
582                         }
583
584                         if ( key === VK.ENTER ) {
585                                 event.preventDefault();
586                         }
587                 }
588         });
589
590         editor.on( 'keyup', function() {
591                 if ( toRemove ) {
592                         removeView( toRemove );
593                         toRemove = false;
594                 }
595         });
596
597         editor.on( 'focus', function() {
598                 var view;
599
600                 focus = true;
601                 editor.dom.addClass( editor.getBody(), 'has-focus' );
602
603                 // Edge case: show the fake caret when the editor is focused for the first time
604                 // and the first element is a view.
605                 if ( firstFocus && ( view = getView( editor.getBody().firstChild ) ) ) {
606                         setViewCursor( true, view );
607                 }
608
609                 firstFocus = false;
610         } );
611
612         editor.on( 'blur', function() {
613                 focus = false;
614                 editor.dom.removeClass( editor.getBody(), 'has-focus' );
615         } );
616
617         editor.on( 'NodeChange', function( event ) {
618                 var dom = editor.dom,
619                         views = editor.dom.select( '.wpview-wrap' ),
620                         className = event.element.className,
621                         view = getView( event.element ),
622                         lKDN = lastKeyDownNode;
623
624                 lastKeyDownNode = false;
625
626                 clearInterval( cursorInterval );
627
628                 // This runs a lot and is faster than replacing each class separately
629                 tinymce.each( views, function ( view ) {
630                         if ( view.className ) {
631                                 view.className = view.className.replace( / ?\bwpview-(?:selection-before|selection-after|cursor-hide)\b/g, '' );
632                         }
633                 });
634
635                 if ( focus && view ) {
636                         if ( ( className === 'wpview-selection-before' || className === 'wpview-selection-after' ) &&
637                                 editor.selection.isCollapsed() ) {
638
639                                 setViewCursorTries = 0;
640
641                                 deselect();
642
643                                 // Make sure the cursor arrived in the right node.
644                                 // This is necessary for Firefox.
645                                 if ( lKDN === view.previousSibling ) {
646                                         setViewCursor( true, view );
647                                         return;
648                                 } else if ( lKDN === view.nextSibling ) {
649                                         setViewCursor( false, view );
650                                         return;
651                                 }
652
653                                 dom.addClass( view, className );
654
655                                 cursorInterval = setInterval( function() {
656                                         if ( dom.hasClass( view, 'wpview-cursor-hide' ) ) {
657                                                 dom.removeClass( view, 'wpview-cursor-hide' );
658                                         } else {
659                                                 dom.addClass( view, 'wpview-cursor-hide' );
660                                         }
661                                 }, 500 );
662                         // If the cursor lands anywhere else in the view, set the cursor before it.
663                         // Only try this once to prevent a loop. (You never know.)
664                         } else if ( ! getParent( event.element, 'wpview-clipboard' ) && ! setViewCursorTries ) {
665                                 deselect();
666                                 setViewCursorTries++;
667                                 setViewCursor( true, view );
668                         }
669                 }
670         });
671
672         editor.on( 'BeforeExecCommand', function() {
673                 var node = editor.selection.getNode(),
674                         view;
675
676                 if ( node && ( ( execCommandBefore = node.className === 'wpview-selection-before' ) || node.className === 'wpview-selection-after' ) && ( view = getView( node ) ) ) {
677                         handleEnter( view, execCommandBefore );
678                         execCommandView = view;
679                 }
680         });
681
682         editor.on( 'ExecCommand', function() {
683                 var toSelect, node;
684
685                 if ( selected ) {
686                         toSelect = selected;
687                         deselect();
688                         select( toSelect );
689                 }
690
691                 if ( execCommandView ) {
692                         node = execCommandView[ execCommandBefore ? 'previousSibling' : 'nextSibling' ];
693
694                         if ( node && node.nodeName === 'P' && editor.dom.isEmpty( node ) ) {
695                                 editor.dom.remove( node );
696                                 setViewCursor( execCommandBefore, execCommandView );
697                         }
698
699                         execCommandView = false;
700                 }
701         });
702
703         editor.on( 'ResolveName', function( event ) {
704                 if ( editor.dom.hasClass( event.target, 'wpview-wrap' ) ) {
705                         event.name = editor.dom.getAttrib( event.target, 'data-wpview-type' ) || 'wpview';
706                         event.stopPropagation();
707                 } else if ( getView( event.target ) ) {
708                         event.preventDefault();
709                         event.stopPropagation();
710                 }
711         });
712
713         return {
714                 getViewText: getViewText,
715                 setViewText: setViewText,
716                 getView: getView
717         };
718 });