6 | | tinymce.PluginManager.add( 'wpview', function( editor ) { |
7 | | var $ = editor.$, |
8 | | selected, |
9 | | Env = tinymce.Env, |
10 | | VK = tinymce.util.VK, |
11 | | TreeWalker = tinymce.dom.TreeWalker, |
12 | | toRemove = false, |
13 | | firstFocus = true, |
14 | | _noop = function() { return false; }, |
15 | | isios = /iPad|iPod|iPhone/.test( navigator.userAgent ), |
16 | | cursorInterval, |
17 | | lastKeyDownNode, |
18 | | setViewCursorTries, |
19 | | focus, |
20 | | execCommandView, |
21 | | execCommandBefore, |
22 | | toolbar; |
23 | | |
24 | | function getView( node ) { |
25 | | return getParent( node, 'wpview-wrap' ); |
26 | | } |
27 | | |
28 | | /** |
29 | | * Returns the node or a parent of the node that has the passed className. |
30 | | * Doing this directly is about 40% faster |
31 | | */ |
32 | | function getParent( node, className ) { |
33 | | while ( node && node.parentNode ) { |
34 | | if ( node.className && ( ' ' + node.className + ' ' ).indexOf( ' ' + className + ' ' ) !== -1 ) { |
35 | | return node; |
36 | | } |
37 | | |
38 | | node = node.parentNode; |
39 | | } |
40 | | |
41 | | return false; |
42 | | } |
43 | | |
44 | | function _stop( event ) { |
45 | | event.stopPropagation(); |
46 | | } |
47 | | |
48 | | function setViewCursor( before, view ) { |
49 | | var location = before ? 'before' : 'after', |
50 | | offset = before ? 0 : 1; |
51 | | deselect(); |
52 | | editor.selection.setCursorLocation( editor.dom.select( '.wpview-selection-' + location, view )[0], offset ); |
53 | | editor.nodeChanged(); |
54 | | } |
55 | | |
56 | | function handleEnter( view, before, key ) { |
57 | | var dom = editor.dom, |
58 | | padNode = dom.create( 'p' ); |
59 | | |
60 | | if ( ! ( Env.ie && Env.ie < 11 ) ) { |
61 | | padNode.innerHTML = '<br data-mce-bogus="1">'; |
62 | | } |
| 4 | ( function( tinymce, wp ) { |
| 5 | tinymce.PluginManager.add( 'wpview', function( editor ) { |
| 6 | function noop () {} |
70 | | deselect(); |
71 | | |
72 | | if ( before && key === VK.ENTER ) { |
73 | | setViewCursor( before, view ); |
74 | | } else { |
75 | | editor.selection.setCursorLocation( padNode, 0 ); |
76 | | } |
77 | | |
78 | | editor.nodeChanged(); |
79 | | } |
80 | | |
81 | | function removeView( view ) { |
82 | | editor.undoManager.transact( function() { |
83 | | handleEnter( view ); |
84 | | wp.mce.views.remove( editor, view ); |
85 | | }); |
86 | | } |
87 | | |
88 | | function select( viewNode ) { |
89 | | var clipboard, |
90 | | dom = editor.dom; |
91 | | |
92 | | if ( ! viewNode ) { |
93 | | return; |
| 14 | // Check if a node is a view or not. |
| 15 | function isView( node ) { |
| 16 | return editor.dom.hasClass( node, 'wpview' ); |
96 | | if ( viewNode !== selected ) { |
97 | | // Make sure that the editor is focused. |
98 | | // It is possible that the editor is not focused when the mouse event fires |
99 | | // without focus, the selection will not work properly. |
100 | | editor.getBody().focus(); |
101 | | |
102 | | deselect(); |
103 | | selected = viewNode; |
104 | | dom.setAttrib( viewNode, 'data-mce-selected', 1 ); |
105 | | |
106 | | clipboard = dom.create( 'div', { |
107 | | 'class': 'wpview-clipboard', |
108 | | 'contenteditable': 'true' |
109 | | }, wp.mce.views.getText( viewNode ) ); |
110 | | |
111 | | editor.dom.select( '.wpview-body', viewNode )[0].appendChild( clipboard ); |
112 | | |
113 | | // Both of the following are necessary to prevent manipulating the selection/focus |
114 | | dom.bind( clipboard, 'beforedeactivate focusin focusout', _stop ); |
115 | | dom.bind( selected, 'beforedeactivate focusin focusout', _stop ); |
116 | | |
117 | | // select the hidden div |
118 | | if ( isios ) { |
119 | | editor.selection.select( clipboard ); |
120 | | } else { |
121 | | editor.selection.select( clipboard, true ); |
| 19 | // Replace view tags with their text. |
| 20 | function resetViews( content ) { |
| 21 | function callback( match, $1 ) { |
| 22 | return '<p>' + window.decodeURIComponent( $1 ) + '</p>'; |
155 | | function resetViewsCallback( match, viewText ) { |
156 | | return '<p>' + window.decodeURIComponent( viewText ) + '</p>'; |
157 | | } |
158 | | |
159 | | // Replace the view tags with the view string |
160 | | function resetViews( content ) { |
161 | | return content.replace( /<div[^>]+data-wpview-text="([^"]+)"[^>]*>(?:[\s\S]+?wpview-selection-after[^>]+>[^<>]*<\/p>\s*|\.)<\/div>/g, resetViewsCallback ) |
162 | | .replace( /<p [^>]*?data-wpview-marker="([^"]+)"[^>]*>[\s\S]*?<\/p>/g, resetViewsCallback ); |
163 | | } |
164 | | |
165 | | // Prevent adding undo levels on changes inside a view wrapper |
166 | | editor.on( 'BeforeAddUndo', function( event ) { |
167 | | if ( event.level.content ) { |
168 | | event.level.content = resetViews( event.level.content ); |
| 29 | return content |
| 30 | .replace( /<div[^>]+data-wpview-text="([^"]+)"[^>]*>(?:\.|[\s\S]+?wpview-end[^>]+>\s*<\/span>\s*)<\/div>/g, callback ) |
| 31 | .replace( /<p[^>]+data-wpview-marker="([^"]+)"[^>]*>[\s\S]*?<\/p>/g, callback ); |
172 | | // When the editor's content changes, scan the new content for |
173 | | // matching view patterns, and transform the matches into |
174 | | // view wrappers. |
175 | | editor.on( 'BeforeSetContent', function( event ) { |
176 | | var node; |
177 | | |
178 | | if ( ! event.selection ) { |
179 | | wp.mce.views.unbind(); |
| 34 | // Empty view and marker nodes. |
| 35 | function emptyViewNodes( node ) { |
| 36 | editor.$( 'div[data-wpview-text], p[data-wpview-marker]', node ).each( function( i, node ) { |
| 37 | node.innerHTML = '.'; |
| 38 | } ); |
186 | | if ( ! event.load ) { |
187 | | if ( selected ) { |
188 | | removeView( selected ); |
189 | | } |
190 | | |
191 | | node = editor.selection.getNode(); |
192 | | |
193 | | if ( node && node !== editor.getBody() && /^\s*https?:\/\/\S+\s*$/i.test( event.content ) ) { |
194 | | // When a url is pasted or inserted, only try to embed it when it is in an empty paragrapgh. |
195 | | node = editor.dom.getParent( node, 'p' ); |
196 | | |
197 | | if ( node && /^[\s\uFEFF\u00A0]*$/.test( $( node ).text() || '' ) ) { |
198 | | // Make sure there are no empty inline elements in the <p> |
199 | | node.innerHTML = ''; |
200 | | } else { |
201 | | return; |
202 | | } |
| 45 | if ( ! event.selection ) { |
| 46 | wp.mce.views.unbind(); |
223 | | // When the editor's content has been updated and the DOM has been |
224 | | // processed, render the views in the document. |
225 | | editor.on( 'SetContent', function() { |
226 | | wp.mce.views.render(); |
227 | | }); |
228 | | |
229 | | // Set the cursor before or after a view when clicking next to it. |
230 | | editor.on( 'click', function( event ) { |
231 | | var x = event.clientX, |
232 | | y = event.clientY, |
233 | | body = editor.getBody(), |
234 | | bodyRect = body.getBoundingClientRect(), |
235 | | first = body.firstChild, |
236 | | last = body.lastChild, |
237 | | firstRect, lastRect, view; |
| 53 | if ( ! event.load ) { |
| 54 | node = editor.selection.getNode(); |
239 | | if ( ! first || ! last ) { |
240 | | return; |
241 | | } |
242 | | |
243 | | firstRect = first.getBoundingClientRect(); |
244 | | lastRect = last.getBoundingClientRect(); |
245 | | |
246 | | if ( y < firstRect.top && ( view = getView( first ) ) ) { |
247 | | setViewCursor( true, view ); |
248 | | event.preventDefault(); |
249 | | } else if ( y > lastRect.bottom && ( view = getView( last ) ) ) { |
250 | | setViewCursor( false, view ); |
251 | | event.preventDefault(); |
252 | | } else if ( x < bodyRect.left || x > bodyRect.right ) { |
253 | | tinymce.each( editor.dom.select( '.wpview-wrap' ), function( view ) { |
254 | | var rect = view.getBoundingClientRect(); |
255 | | |
256 | | if ( y < rect.top ) { |
257 | | return false; |
258 | | } |
| 56 | if ( node && node !== editor.getBody() && /^\s*https?:\/\/\S+\s*$/i.test( event.content ) ) { |
| 57 | // When a url is pasted or inserted, only try to embed it when it is in an empty paragrapgh. |
| 58 | node = editor.dom.getParent( node, 'p' ); |
271 | | }); |
272 | | } |
273 | | }); |
274 | | |
275 | | editor.on( 'init', function() { |
276 | | var scrolled = false, |
277 | | selection = editor.selection, |
278 | | MutationObserver = window.MutationObserver || window.WebKitMutationObserver; |
279 | | |
280 | | // When a view is selected, ensure content that is being pasted |
281 | | // or inserted is added to a text node (instead of the view). |
282 | | editor.on( 'BeforeSetContent', function() { |
283 | | var walker, target, |
284 | | view = getView( selection.getNode() ); |
285 | | |
286 | | // If the selection is not within a view, bail. |
287 | | if ( ! view ) { |
288 | | return; |
291 | | if ( ! view.nextSibling || getView( view.nextSibling ) ) { |
292 | | // If there are no additional nodes or the next node is a |
293 | | // view, create a text node after the current view. |
294 | | target = editor.getDoc().createTextNode(''); |
295 | | editor.dom.insertAfter( target, view ); |
296 | | } else { |
297 | | // Otherwise, find the next text node. |
298 | | walker = new TreeWalker( view.nextSibling, view.nextSibling ); |
299 | | target = walker.next(); |
300 | | } |
301 | | |
302 | | // Select the `target` text node. |
303 | | selection.select( target ); |
304 | | selection.collapse( true ); |
305 | | }); |
306 | | |
307 | | editor.dom.bind( editor.getDoc(), 'touchmove', function() { |
308 | | scrolled = true; |
309 | | }); |
310 | | |
311 | | editor.on( 'mousedown mouseup click touchend', function( event ) { |
312 | | var view = getView( event.target ); |
313 | | |
314 | | firstFocus = false; |
315 | | |
316 | | // Contain clicks inside the view wrapper |
317 | | if ( view ) { |
318 | | event.stopImmediatePropagation(); |
319 | | event.preventDefault(); |
320 | | |
321 | | if ( event.type === 'touchend' && scrolled ) { |
322 | | scrolled = false; |
323 | | } else { |
324 | | select( view ); |
325 | | } |
| 69 | event.content = wp.mce.views.setMarkers( event.content ); |
| 70 | } ); |
341 | | if ( MutationObserver ) { |
342 | | new MutationObserver( function() { |
343 | | editor.fire( 'wp-body-class-change' ); |
344 | | } ) |
345 | | .observe( editor.getBody(), { |
346 | | attributes: true, |
347 | | attributeFilter: ['class'] |
348 | | } ); |
349 | | } |
350 | | |
351 | | if ( tinymce.Env.ie ) { |
352 | | // Prevent resize handles in newer IE |
353 | | editor.dom.bind( editor.getBody(), 'controlselect mscontrolselect', function( event ) { |
354 | | if ( getView( event.target ) ) { |
355 | | event.preventDefault(); |
356 | | } |
357 | | }); |
358 | | } |
359 | | }); |
360 | | |
361 | | // Empty the wpview wrap and marker nodes |
362 | | function emptyViewNodes( rootNode ) { |
363 | | $( 'div[data-wpview-text], p[data-wpview-marker]', rootNode ).each( function( i, node ) { |
364 | | node.innerHTML = '.'; |
365 | | }); |
366 | | } |
367 | | |
368 | | // Run that before the DOM cleanup |
369 | | editor.on( 'PreProcess', function( event ) { |
370 | | emptyViewNodes( event.node ); |
371 | | }, true ); |
372 | | |
373 | | editor.on( 'hide', function() { |
374 | | wp.mce.views.unbind(); |
375 | | deselect(); |
376 | | emptyViewNodes(); |
377 | | }); |
378 | | |
379 | | editor.on( 'PostProcess', function( event ) { |
380 | | if ( event.content ) { |
381 | | event.content = event.content.replace( /<div [^>]*?data-wpview-text="([^"]+)"[^>]*>[\s\S]*?<\/div>/g, resetViewsCallback ) |
382 | | .replace( /<p [^>]*?data-wpview-marker="([^"]+)"[^>]*>[\s\S]*?<\/p>/g, resetViewsCallback ); |
383 | | } |
384 | | }); |
| 82 | // Replace views with their text. |
| 83 | editor.on( 'postprocess', function( event ) { |
| 84 | event.content = resetViews( event.content ); |
| 85 | } ); |
| 86 | |
| 87 | // Replace views with their text inside undo levels. |
| 88 | // This also prevents that new levels are added when there are changes inside the views. |
| 89 | editor.on( 'beforeaddundo', function( event ) { |
| 90 | event.level.content = resetViews( event.level.content ); |
| 91 | } ); |
386 | | // Excludes arrow keys, delete, backspace, enter, space bar. |
387 | | // Ref: https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent.keyCode |
388 | | function isSpecialKey( key ) { |
389 | | return ( ( key <= 47 && key !== VK.SPACEBAR && key !== VK.ENTER && key !== VK.DELETE && key !== VK.BACKSPACE && ( key < 37 || key > 40 ) ) || |
390 | | key >= 224 || // OEM or non-printable |
391 | | ( key >= 144 && key <= 150 ) || // Num Lock, Scroll Lock, OEM |
392 | | ( key >= 91 && key <= 93 ) || // Windows keys |
393 | | ( key >= 112 && key <= 135 ) ); // F keys |
394 | | } |
395 | | |
396 | | // (De)select views when arrow keys are used to navigate the content of the editor. |
397 | | editor.on( 'keydown', function( event ) { |
398 | | var key = event.keyCode, |
399 | | dom = editor.dom, |
400 | | selection = editor.selection, |
401 | | node, view, cursorBefore, cursorAfter, |
402 | | range, clonedRange, tempRange; |
403 | | |
404 | | if ( selected ) { |
405 | | // Ignore key presses that involve the command or control key, but continue when in combination with backspace or v. |
406 | | // Also ignore the F# keys. |
407 | | if ( ( ( event.metaKey || event.ctrlKey ) && key !== VK.BACKSPACE && key !== 86 ) || ( key >= 112 && key <= 123 ) ) { |
408 | | // Remove the view when pressing cmd/ctrl+x on keyup, otherwise the browser can't copy the content. |
409 | | if ( ( event.metaKey || event.ctrlKey ) && key === 88 ) { |
410 | | toRemove = selected; |
411 | | } |
412 | | return; |
| 93 | // Make sure views are copied as their text. |
| 94 | editor.on( 'drop objectselected', function( event ) { |
| 95 | if ( isView( event.targetClone ) ) { |
| 96 | event.targetClone = editor.getDoc().createTextNode( |
| 97 | window.decodeURIComponent( editor.dom.getAttrib( event.targetClone, 'data-wpview-text' ) ) |
| 98 | ); |
423 | | if ( key === VK.LEFT ) { |
424 | | setViewCursor( true, view ); |
425 | | event.preventDefault(); |
426 | | } else if ( key === VK.UP ) { |
427 | | if ( view.previousSibling ) { |
428 | | if ( getView( view.previousSibling ) ) { |
429 | | setViewCursor( true, view.previousSibling ); |
430 | | } else { |
431 | | deselect(); |
432 | | selection.select( view.previousSibling, true ); |
433 | | selection.collapse(); |
434 | | } |
435 | | } else { |
436 | | setViewCursor( true, view ); |
437 | | } |
438 | | event.preventDefault(); |
439 | | } else if ( key === VK.RIGHT ) { |
440 | | setViewCursor( false, view ); |
441 | | event.preventDefault(); |
442 | | } else if ( key === VK.DOWN ) { |
443 | | if ( view.nextSibling ) { |
444 | | if ( getView( view.nextSibling ) ) { |
445 | | setViewCursor( false, view.nextSibling ); |
446 | | } else { |
447 | | deselect(); |
448 | | selection.setCursorLocation( view.nextSibling, 0 ); |
449 | | } |
450 | | } else { |
451 | | setViewCursor( false, view ); |
| 109 | if ( /^https?:\/\/\S+$/i.test( pastedStr ) ) { |
| 110 | event.content = pastedStr; |
468 | | node = selection.getNode(); |
469 | | lastKeyDownNode = node; |
470 | | view = getView( node ); |
471 | | |
472 | | // Make sure we don't delete part of a view. |
473 | | // If the range ends or starts with the view, we'll need to trim it. |
474 | | if ( ! selection.isCollapsed() ) { |
475 | | range = selection.getRng(); |
476 | | |
477 | | if ( view = getView( range.endContainer ) ) { |
478 | | clonedRange = range.cloneRange(); |
479 | | selection.select( view.previousSibling, true ); |
480 | | selection.collapse(); |
481 | | tempRange = selection.getRng(); |
482 | | clonedRange.setEnd( tempRange.endContainer, tempRange.endOffset ); |
483 | | selection.setRng( clonedRange ); |
484 | | } else if ( view = getView( range.startContainer ) ) { |
485 | | clonedRange = range.cloneRange(); |
486 | | clonedRange.setStart( view.nextSibling, 0 ); |
487 | | selection.setRng( clonedRange ); |
488 | | } |
| 115 | // Show the view type in the element path. |
| 116 | editor.on( 'resolvename', function( event ) { |
| 117 | if ( isView( event.target ) ) { |
| 118 | event.name = editor.dom.getAttrib( event.target, 'data-wpview-type' ) || 'object'; |
491 | | if ( ! view ) { |
492 | | // Make sure we don't eat any content. |
493 | | if ( event.keyCode === VK.BACKSPACE ) { |
494 | | if ( editor.dom.isEmpty( node ) ) { |
495 | | if ( view = getView( node.previousSibling ) ) { |
496 | | setViewCursor( false, view ); |
497 | | editor.dom.remove( node ); |
498 | | event.preventDefault(); |
499 | | } |
500 | | } else if ( ( range = selection.getRng() ) && |
501 | | range.startOffset === 0 && |
502 | | range.endOffset === 0 && |
503 | | ( view = getView( node.previousSibling ) ) ) { |
504 | | setViewCursor( false, view ); |
505 | | event.preventDefault(); |
506 | | } |
507 | | } |
508 | | return; |
509 | | } |
| 122 | // See `media` plugin. |
| 123 | editor.on( 'click keyup', function() { |
| 124 | var node = editor.selection.getNode(); |
521 | | if ( ( cursorAfter && key === VK.UP ) || ( cursorBefore && key === VK.BACKSPACE ) ) { |
522 | | if ( view.previousSibling ) { |
523 | | if ( getView( view.previousSibling ) ) { |
524 | | setViewCursor( false, view.previousSibling ); |
525 | | } else { |
526 | | if ( dom.isEmpty( view.previousSibling ) && key === VK.BACKSPACE ) { |
527 | | dom.remove( view.previousSibling ); |
528 | | } else { |
529 | | selection.select( view.previousSibling, true ); |
530 | | selection.collapse(); |
531 | | } |
532 | | } |
533 | | } else { |
534 | | setViewCursor( true, view ); |
| 139 | if ( isView( node ) ) { |
| 140 | wp.mce.views.edit( editor, node ); |
536 | | event.preventDefault(); |
537 | | } else if ( cursorAfter && ( key === VK.DOWN || key === VK.RIGHT ) ) { |
538 | | if ( view.nextSibling ) { |
539 | | if ( getView( view.nextSibling ) ) { |
540 | | setViewCursor( key === VK.RIGHT, view.nextSibling ); |
541 | | } else { |
542 | | selection.setCursorLocation( view.nextSibling, 0 ); |
543 | | } |
544 | | } |
545 | | event.preventDefault(); |
546 | | } else if ( cursorBefore && ( key === VK.UP || key === VK.LEFT ) ) { |
547 | | if ( view.previousSibling ) { |
548 | | if ( getView( view.previousSibling ) ) { |
549 | | setViewCursor( key === VK.UP, view.previousSibling ); |
550 | | } else { |
551 | | selection.select( view.previousSibling, true ); |
552 | | selection.collapse(); |
553 | | } |
554 | | } |
555 | | event.preventDefault(); |
556 | | } else if ( cursorBefore && key === VK.DOWN ) { |
557 | | if ( view.nextSibling ) { |
558 | | if ( getView( view.nextSibling ) ) { |
559 | | setViewCursor( true, view.nextSibling ); |
560 | | } else { |
561 | | selection.setCursorLocation( view.nextSibling, 0 ); |
562 | | } |
563 | | } else { |
564 | | setViewCursor( false, view ); |
565 | | } |
566 | | event.preventDefault(); |
567 | | } else if ( ( cursorAfter && key === VK.LEFT ) || ( cursorBefore && key === VK.RIGHT ) ) { |
568 | | select( view ); |
569 | | event.preventDefault(); |
570 | | } else if ( cursorAfter && key === VK.BACKSPACE ) { |
571 | | removeView( view ); |
572 | | event.preventDefault(); |
573 | | } else if ( cursorAfter ) { |
574 | | handleEnter( view ); |
575 | | } else if ( cursorBefore ) { |
576 | | handleEnter( view , true, key ); |
607 | | editor.on( 'blur', function() { |
608 | | focus = false; |
609 | | editor.dom.removeClass( editor.getBody(), 'has-focus' ); |
610 | | } ); |
611 | | |
612 | | editor.on( 'NodeChange', function( event ) { |
613 | | var dom = editor.dom, |
614 | | views = editor.dom.select( '.wpview-wrap' ), |
615 | | className = event.element.className, |
616 | | view = getView( event.element ), |
617 | | lKDN = lastKeyDownNode; |
618 | | |
619 | | lastKeyDownNode = false; |
620 | | |
621 | | clearInterval( cursorInterval ); |
622 | | |
623 | | // This runs a lot and is faster than replacing each class separately |
624 | | tinymce.each( views, function ( view ) { |
625 | | if ( view.className ) { |
626 | | view.className = view.className.replace( / ?\bwpview-(?:selection-before|selection-after|cursor-hide)\b/g, '' ); |
627 | | } |
628 | | }); |
629 | | |
630 | | if ( focus && view ) { |
631 | | if ( ( className === 'wpview-selection-before' || className === 'wpview-selection-after' ) && |
632 | | editor.selection.isCollapsed() ) { |
633 | | |
634 | | setViewCursorTries = 0; |
635 | | |
636 | | deselect(); |
637 | | |
638 | | // Make sure the cursor arrived in the right node. |
639 | | // This is necessary for Firefox. |
640 | | if ( lKDN === view.previousSibling ) { |
641 | | setViewCursor( true, view ); |
642 | | return; |
643 | | } else if ( lKDN === view.nextSibling ) { |
644 | | setViewCursor( false, view ); |
645 | | return; |
646 | | } |
647 | | |
648 | | dom.addClass( view, className ); |
649 | | |
650 | | cursorInterval = setInterval( function() { |
651 | | if ( dom.hasClass( view, 'wpview-cursor-hide' ) ) { |
652 | | dom.removeClass( view, 'wpview-cursor-hide' ); |
653 | | } else { |
654 | | dom.addClass( view, 'wpview-cursor-hide' ); |
| 162 | editor.on( 'wptoolbar', function( event ) { |
| 163 | if ( isView( event.element ) ) { |
| 164 | event.toolbar = toolbar; |
656 | | }, 500 ); |
657 | | // If the cursor lands anywhere else in the view, set the cursor before it. |
658 | | // Only try this once to prevent a loop. (You never know.) |
659 | | } else if ( ! getParent( event.element, 'wpview-clipboard' ) && ! setViewCursorTries ) { |
660 | | deselect(); |
661 | | setViewCursorTries++; |
662 | | setViewCursor( true, view ); |
663 | | } |
664 | | } |
665 | | }); |
666 | | |
667 | | editor.on( 'BeforeExecCommand', function() { |
668 | | var node = editor.selection.getNode(), |
669 | | view; |
670 | | |
671 | | if ( node && ( ( execCommandBefore = node.className === 'wpview-selection-before' ) || node.className === 'wpview-selection-after' ) && ( view = getView( node ) ) ) { |
672 | | handleEnter( view, execCommandBefore ); |
673 | | execCommandView = view; |
674 | | } |
675 | | }); |
676 | | |
677 | | editor.on( 'ExecCommand', function() { |
678 | | var toSelect, node; |
679 | | |
680 | | if ( selected ) { |
681 | | toSelect = selected; |
682 | | deselect(); |
683 | | select( toSelect ); |
684 | | } |
685 | | |
686 | | if ( execCommandView ) { |
687 | | node = execCommandView[ execCommandBefore ? 'previousSibling' : 'nextSibling' ]; |
688 | | |
689 | | if ( node && node.nodeName === 'P' && editor.dom.isEmpty( node ) ) { |
690 | | editor.dom.remove( node ); |
691 | | setViewCursor( execCommandBefore, execCommandView ); |
| 166 | } ); |
698 | | editor.on( 'ResolveName', function( event ) { |
699 | | if ( editor.dom.hasClass( event.target, 'wpview-wrap' ) ) { |
700 | | event.name = editor.dom.getAttrib( event.target, 'data-wpview-type' ) || 'wpview'; |
701 | | event.stopPropagation(); |
702 | | } else if ( getView( event.target ) ) { |
703 | | event.preventDefault(); |
704 | | event.stopPropagation(); |
705 | | } |
706 | | }); |
707 | | |
708 | | editor.addButton( 'wp_view_edit', { |
709 | | tooltip: 'Edit ', // trailing space is needed, used for context |
710 | | icon: 'dashicon dashicons-edit', |
711 | | onclick: function() { |
712 | | selected && wp.mce.views.edit( editor, selected ); |
713 | | } |
714 | | } ); |
715 | | |
716 | | editor.addButton( 'wp_view_remove', { |
717 | | tooltip: 'Remove', |
718 | | icon: 'dashicon dashicons-no', |
719 | | onclick: function() { |
720 | | selected && removeView( selected ); |
721 | | } |
722 | | } ); |
723 | | |
724 | | editor.once( 'preinit', function() { |
725 | | if ( editor.wp && editor.wp._createToolbar ) { |
726 | | toolbar = editor.wp._createToolbar( [ |
727 | | 'wp_view_edit', |
728 | | 'wp_view_remove' |
729 | | ] ); |
730 | | } |
731 | | } ); |
732 | | |
733 | | editor.on( 'wptoolbar', function( event ) { |
734 | | if ( selected ) { |
735 | | event.element = selected; |
736 | | event.toolbar = toolbar; |
737 | | } |
| 174 | return { |
| 175 | getView: noop |
| 176 | }; |