Ticket #26959: 26959-03.patch
File 26959-03.patch, 28.3 KB (added by , 11 years ago) |
---|
-
src/wp-includes/class-wp-editor.php
diff --git src/wp-includes/class-wp-editor.php src/wp-includes/class-wp-editor.php index a829945..16a4bc5 100644
final class _WP_Editors { 235 235 'fullscreen', 236 236 'wordpress', 237 237 'wpeditimage', 238 'wpgallery',239 238 'wplink', 240 239 'wpdialogs', 240 'wpview' 241 241 ) ) ); 242 242 243 243 if ( ( $key = array_search( 'spellchecker', $plugins ) ) !== false ) { … … final class _WP_Editors { 497 497 if ( self::$has_medialib ) { 498 498 add_thickbox(); 499 499 wp_enqueue_script('media-upload'); 500 501 if ( self::$has_tinymce ) 502 wp_enqueue_script('mce-view'); 500 503 } 504 501 505 } 502 506 503 507 public static function wp_mce_translation() { -
src/wp-includes/css/editor.css
diff --git src/wp-includes/css/editor.css src/wp-includes/css/editor.css index aa21bc6..b9abb77 100644
i.mce-i-wp_page:before { 1132 1132 color: #2ea2cc; 1133 1133 } 1134 1134 */ 1135 1136 1137 1135 1138 /* Distraction Free Writing mode 1136 1139 * =Overlay Styles 1137 1140 -------------------------------------------------------------- */ -
src/wp-includes/js/mce-view.js
diff --git src/wp-includes/js/mce-view.js src/wp-includes/js/mce-view.js index 912c4c7..4309a00 100644
window.wp = window.wp || {}; 3 3 4 4 (function($){ 5 5 var views = {}, 6 instances = {}; 6 instances = {}, 7 media = wp.media; 7 8 8 9 // Create the `wp.mce` object if necessary. 9 10 wp.mce = wp.mce || {}; … … window.wp = window.wp || {}; 19 20 // The default properties used for objects with the `pattern` key in 20 21 // `wp.mce.view.add()`. 21 22 pattern: { 22 view: Backbone.View,23 view: wp.Backbone.View, 23 24 text: function( instance ) { 24 25 return instance.options.original; 25 26 }, 26 27 27 28 toView: function( content ) { 28 if ( ! this.pattern ) 29 if ( ! this.pattern ) { 29 30 return; 31 } 30 32 31 33 this.pattern.lastIndex = 0; 32 34 var match = this.pattern.exec( content ); 33 35 34 if ( ! match ) 36 if ( ! match ) { 35 37 return; 38 } 36 39 37 40 return { 38 41 index: match.index, … … window.wp = window.wp || {}; 48 51 // The default properties used for objects with the `shortcode` key in 49 52 // `wp.mce.view.add()`. 50 53 shortcode: { 51 view: Backbone.View,54 view: wp.Backbone.View, 52 55 text: function( instance ) { 53 56 return instance.options.shortcode.string(); 54 57 }, … … window.wp = window.wp || {}; 56 59 toView: function( content ) { 57 60 var match = wp.shortcode.next( this.shortcode, content ); 58 61 59 if ( ! match ) 62 if ( ! match ) { 60 63 return; 64 } 61 65 62 66 return { 63 67 index: match.index, … … window.wp = window.wp || {}; 98 102 var parent, remove, base, properties; 99 103 100 104 // Fetch the parent view or the default options. 101 if ( options.extend ) 105 if ( options.extend ) { 102 106 parent = wp.mce.view.get( options.extend ); 103 else if ( options.shortcode )107 } else if ( options.shortcode ) { 104 108 parent = wp.mce.view.defaults.shortcode; 105 else109 } else { 106 110 parent = wp.mce.view.defaults.pattern; 111 } 107 112 108 113 // Extend the `options` object with the parent's properties. 109 114 _.defaults( options, parent ); … … window.wp = window.wp || {}; 118 123 this.$el.parent().remove(); 119 124 120 125 // Trigger the inherited `remove` method. 121 if ( remove ) 126 if ( remove ) { 122 127 remove.apply( this, arguments ); 128 } 123 129 124 130 return this; 125 131 } … … window.wp = window.wp || {}; 138 144 139 145 // If there's a `remove` method on the `base` view that wasn't 140 146 // created by this method, inherit it. 141 if ( ! remove && ! base._mceview ) 147 if ( ! remove && ! base._mceview ) { 142 148 remove = base.prototype.remove; 149 } 143 150 144 151 // Automatically create the new `Backbone.View` constructor. 145 152 options.view = base.extend( properties, { … … window.wp = window.wp || {}; 168 175 // every match. 169 176 // 170 177 // To render the views, call `wp.mce.view.render( scope )`. 178 // TODO: needs unit tests! 171 179 toViews: function( content ) { 172 180 var pieces = [ { content: content } ], 173 181 current; … … window.wp = window.wp || {}; 190 198 // and slicing the string as we go. 191 199 while ( remaining && (result = view.toView( remaining )) ) { 192 200 // Any text before the match becomes an unprocessed piece. 193 if ( result.index ) 201 if ( result.index ) { 194 202 pieces.push({ content: remaining.substring( 0, result.index ) }); 203 } 195 204 196 205 // Add the processed piece for the match. 197 206 pieces.push({ … … window.wp = window.wp || {}; 205 214 206 215 // There are no additional matches. If any content remains, 207 216 // add it as an unprocessed piece. 208 if ( remaining ) 217 if ( remaining ) { 209 218 pieces.push({ content: remaining }); 219 } 210 220 }); 211 221 }); 212 222 … … window.wp = window.wp || {}; 217 227 var view = wp.mce.view.get( viewType ), 218 228 instance, id; 219 229 220 if ( ! view ) 230 if ( ! view ) { 221 231 return ''; 222 232 } 223 233 // Create a new view instance. 224 234 instance = new view.view( _.extend( options || {}, { 225 235 viewType: viewType … … window.wp = window.wp || {}; 239 249 tag: 'span' === instance.tagName ? 'span' : 'div', 240 250 241 251 attrs: { 242 'class': 252 'class': 'wp-view-wrap wp-view-type-' + viewType, 243 253 'data-wp-view': id, 244 254 'contenteditable': false 245 } 255 }, 256 257 content: '\u00a0' 246 258 }); 247 259 }, 248 260 … … window.wp = window.wp || {}; 255 267 render: function( scope ) { 256 268 $( '.wp-view-wrap', scope ).each( function() { 257 269 var wrapper = $(this), 258 view = wp.mce.view.instance( this ); 270 view = wp.mce.view.instance( this ), 271 $shortcode; 259 272 260 if ( ! view ) 273 if ( ! view ) { 261 274 return; 275 } 262 276 263 277 // Link the real wrapper to the view. 264 278 view.$wrapper = wrapper; … … window.wp = window.wp || {}; 267 281 // Detach the view element to ensure events are not unbound. 268 282 view.$el.detach(); 269 283 284 $shortcode = $( '<div />' ) 285 .addClass( 'wp-view-shortcode' ) 286 .prop( 'contenteditable', 'true' ) 287 .data( 'mce-bogus', '1' ) 288 .text( view.options.shortcode.string() ); 289 270 290 // Empty the wrapper, attach the view element to the wrapper, 291 // add a hidden element with the shortcode, 271 292 // and add an ending marker to the wrapper to help regexes 272 293 // scan the HTML string. 273 wrapper.empty().append( view.el ).append('<span data-wp-view-end class="wp-view-end"></span>'); 294 wrapper.empty().append( view.el ) 295 .prepend( $shortcode ) 296 .append('<span data-wp-view-end class="wp-view-end"></span>'); 274 297 }); 275 298 }, 276 299 … … window.wp = window.wp || {}; 278 301 // Scans an HTML `content` string and replaces any view instances with 279 302 // their respective text representations. 280 303 toText: function( content ) { 281 return content.replace( /<(?:div|span)[^>]+data-wp-view="([^"]+)"[^>]*>.*?<span[^>]+data-wp-view-end[^>]*><\/span><\/(?:div|span)>/g, function( match, id ) { 304 305 return content.replace( /<(?:div|span)[^>]+data-wp-view="([^"]+)"[^>]*>.*?<span[^>]+data-wp-view-end[^>]*><\/span><\/(?:div|span)>/mg, function( match, id ) { 282 306 var instance = instances[ id ], 283 307 view; 284 308 285 if ( instance ) 309 if ( instance ) { 286 310 view = wp.mce.view.get( instance.options.viewType ); 287 311 } 288 312 return instance && view ? view.text( instance ) : ''; 289 313 }); 290 314 }, … … window.wp = window.wp || {}; 293 317 removeInternalAttrs: function( attrs ) { 294 318 var result = {}; 295 319 _.each( attrs, function( value, attr ) { 296 if ( -1 === attr.indexOf('data-mce') ) 320 if ( -1 === attr.indexOf('data-mce') ) { 297 321 result[ attr ] = value; 322 } 298 323 }); 299 324 return result; 300 325 }, … … window.wp = window.wp || {}; 311 336 instance: function( node ) { 312 337 var id = $( node ).data('wp-view'); 313 338 314 if ( id ) 339 if ( id ) { 315 340 return instances[ id ]; 341 } 316 342 }, 317 343 318 344 // ### Select a view. … … window.wp = window.wp || {}; 323 349 var $node = $(node); 324 350 325 351 // Bail if node is already selected. 326 if ( $node.hasClass('selected') ) 352 if ( $node.hasClass('selected') ) { 327 353 return; 354 } 328 355 329 356 $node.addClass('selected'); 330 357 $( node.firstChild ).trigger('select'); … … window.wp = window.wp || {}; 338 365 var $node = $(node); 339 366 340 367 // Bail if node is already selected. 341 if ( ! $node.hasClass('selected') ) 368 if ( ! $node.hasClass('selected') ) { 342 369 return; 370 } 343 371 344 372 $node.removeClass('selected'); 345 373 $( node.firstChild ).trigger('deselect'); 346 374 } 347 375 }; 348 376 349 }(jQuery)); 350 No newline at end of file 377 wp.mce.view.add( 'gallery', { 378 shortcode: 'gallery', 379 380 gallery: (function() { 381 var galleries = {}; 382 383 return { 384 attachments: function( shortcode, parent ) { 385 var shortcodeString = shortcode.string(), 386 result = galleries[ shortcodeString ], 387 attrs, args, query, others; 388 389 delete galleries[ shortcodeString ]; 390 391 if ( result ) { 392 return result; 393 } 394 395 attrs = shortcode.attrs.named; 396 args = _.pick( attrs, 'orderby', 'order' ); 397 398 args.type = 'image'; 399 args.perPage = -1; 400 401 // Map the `ids` param to the correct query args. 402 if ( attrs.ids ) { 403 args.post__in = attrs.ids.split(','); 404 args.orderby = 'post__in'; 405 } else if ( attrs.include ) { 406 args.post__in = attrs.include.split(','); 407 } 408 409 if ( attrs.exclude ) { 410 args.post__not_in = attrs.exclude.split(','); 411 } 412 413 if ( ! args.post__in ) { 414 args.parent = attrs.id || parent; 415 } 416 417 // Collect the attributes that were not included in `args`. 418 others = {}; 419 _.filter( attrs, function( value, key ) { 420 if ( _.isUndefined( args[ key ] ) ) { 421 others[ key ] = value; 422 } 423 }); 424 425 query = media.query( args ); 426 query.gallery = new Backbone.Model( others ); 427 return query; 428 }, 429 430 shortcode: function( attachments ) { 431 var props = attachments.props.toJSON(), 432 attrs = _.pick( props, 'include', 'exclude', 'orderby', 'order' ), 433 shortcode, clone; 434 435 if ( attachments.gallery ) { 436 _.extend( attrs, attachments.gallery.toJSON() ); 437 } 438 439 attrs.ids = attachments.pluck('id'); 440 441 // If the `ids` attribute is set and `orderby` attribute 442 // is the default value, clear it for cleaner output. 443 if ( attrs.ids && 'post__in' === attrs.orderby ) { 444 delete attrs.orderby; 445 } 446 447 shortcode = new wp.shortcode({ 448 tag: 'gallery', 449 attrs: attrs, 450 type: 'single' 451 }); 452 453 // Use a cloned version of the gallery. 454 clone = new wp.media.model.Attachments( attachments.models, { 455 props: props 456 }); 457 clone.gallery = attachments.gallery; 458 galleries[ shortcode.string() ] = clone; 459 460 return shortcode; 461 } 462 }; 463 }()), 464 465 view: { 466 className: 'editor-gallery', 467 template: media.template('editor-gallery'), 468 469 // The fallback post ID to use as a parent for galleries that don't 470 // specify the `ids` or `include` parameters. 471 // 472 // Uses the hidden input on the edit posts page by default. 473 parent: $('#post_ID').val(), 474 475 events: { 476 'click .remove': 'remove', 477 'click .edit': 'edit' 478 }, 479 480 initialize: function() { 481 this.update(); 482 }, 483 484 update: function() { 485 var view = wp.mce.view.get('gallery'); 486 487 this.attachments = view.gallery.attachments( this.options.shortcode, this.parent ); 488 this.attachments.more().done( _.bind( this.render, this ) ); 489 }, 490 491 492 render: function() { 493 var attrs = this.options.shortcode.attrs.named, 494 options; 495 496 if ( ! this.attachments.length ) { 497 return; 498 } 499 500 options = { 501 attachments: this.attachments.toJSON(), 502 columns: attrs.columns ? parseInt( attrs.columns, 10 ) : 3 503 }; 504 505 this.$el.html( this.template( options ) ); 506 }, 507 508 edit: function() { 509 var selection; 510 511 if ( ! wp.media.view || this.frame ) { 512 return; 513 } 514 515 selection = new wp.media.model.Selection( this.attachments.models, { 516 props: this.attachments.props.toJSON(), 517 multiple: true 518 }); 519 selection.gallery = this.attachments.gallery; 520 521 this.frame = wp.media({ 522 frame: 'post', 523 state: 'gallery-edit', 524 editing: true, 525 multiple: true, 526 selection: selection 527 }); 528 529 // Create a single-use frame. If the frame is closed, 530 // then detach it from the DOM and remove the reference. 531 this.frame.on( 'close', function() { 532 if ( this.frame ) { 533 this.frame.detach(); 534 } 535 delete this.frame; 536 }, this ); 537 538 // Update the `shortcode` and `attachments`. 539 this.frame.state('gallery-edit').on( 'update', function( selection ) { 540 var view = wp.mce.view.get('gallery'); 541 542 this.options.shortcode = view.gallery.shortcode( selection ); 543 this.update(); 544 }, this ); 545 546 this.frame.open(); 547 } 548 } 549 }); 550 }(jQuery)); -
src/wp-includes/js/tinymce/plugins/wpeditimage/plugin.js
diff --git src/wp-includes/js/tinymce/plugins/wpeditimage/plugin.js src/wp-includes/js/tinymce/plugins/wpeditimage/plugin.js index f625097..797b7f3 100644
tinymce.PluginManager.add( 'wpeditimage', function( editor ) { 379 379 return true; 380 380 } 381 381 382 if ( dom.getParent( node, '.wp-view-wrap' ) ) { 383 return true; 384 } 385 382 386 return false; 383 387 } 384 388 -
src/wp-includes/js/tinymce/plugins/wpview/plugin.js
diff --git src/wp-includes/js/tinymce/plugins/wpview/plugin.js src/wp-includes/js/tinymce/plugins/wpview/plugin.js index 0c56ecb..e3509a2 100644
2 2 /** 3 3 * WordPress View plugin. 4 4 */ 5 6 (function() { 7 var VK = tinymce.VK, 5 tinymce.PluginManager.add( 'wpview', function( editor ) { 6 var VK = tinymce.util.VK, 8 7 TreeWalker = tinymce.dom.TreeWalker, 8 removeSelected = false, 9 9 selected; 10 10 11 tinymce.create('tinymce.plugins.wpView', { 12 init : function( editor ) { 13 var wpView = this; 11 function getParentView( node ) { 12 while ( node ) { 13 if ( isView( node ) ) { 14 return node; 15 } 16 17 node = node.parentNode; 18 } 19 } 20 21 function isView( node ) { 22 return (/(?:^|\s)wp-view-wrap(?:\s|$)/).test( node.className ); 23 } 24 25 function select( view ) { 26 var elem; 27 if ( view === selected ) { 28 return; 29 } 30 31 deselect(); 32 selected = view; 33 34 elem = editor.dom.select( '.wp-view-shortcode', view )[0]; 35 36 // the following are both necessary to avoid tinymce from manipulating the selection/focus 37 editor.dom.bind(elem, 'beforedeactivate focusin focusout', function(e) { 38 e.stopPropagation(); 39 }); 40 editor.dom.bind(selected, 'beforedeactivate focusin focusout click mouseup', function(e) { 41 e.stopPropagation(); 42 }); 43 44 // select a the hidden div 45 editor.selection.select( elem, true ); 46 elem.focus(); 47 wp.mce.view.select( selected ); 48 } 49 50 function deselect() { 51 var elem; 52 53 if ( selected ) { 54 elem = editor.dom.select( '.wp-view-shortcode', selected )[0]; 55 editor.dom.unbind(elem, 'beforedeactivate focusin focusout'); 56 editor.dom.unbind(selected, 'beforedeactivate focusin focusout click mouseup'); 57 58 editor.selection.select( selected.nextSibling ); 59 editor.selection.collapse(); 60 wp.mce.view.deselect( selected ); 61 } 62 63 selected = null; 64 } 65 66 // Check if the `wp.mce` API exists. 67 if ( typeof wp === 'undefined' || ! wp.mce ) { 68 return; 69 } 70 71 editor.on( 'PreInit', function() { 72 // Add elements so we can set `contenteditable` to false. 73 // TODO: since we are serializing, is this needed? 74 editor.schema.addValidElements('div[*],span[*]'); 75 }); 76 77 // When the editor's content changes, scan the new content for 78 // matching view patterns, and transform the matches into 79 // view wrappers. Since the editor's DOM is outdated at this point, 80 // we'll wait to render the views. 81 editor.on( 'BeforeSetContent', function( e ) { 82 if ( ! e.content ) { 83 return; 84 } 85 86 e.content = wp.mce.view.toViews( e.content ); 87 }); 88 89 // When the editor's content has been updated and the DOM has been 90 // processed, render the views in the document. 91 editor.on( 'SetContent', function() { 92 wp.mce.view.render( editor.getDoc() ); 93 }); 94 95 // Provide our own handler for selecting a view that is picked up before TinyMCE 96 // Ideally, TinyMCE would provide a way to relinquish control over a block that is marked contenteditable=false perhaps through some sort of data attribute 97 editor.on( 'mousedown', function( event ) { 98 var view = getParentView( event.target ); 14 99 15 // Check if the `wp.mce` API exists. 16 if ( typeof wp === 'undefined' || ! wp.mce ) { 100 if ( event.metaKey || event.ctrlKey ) { 101 return; 102 } 103 104 // Update the selected view. 105 if ( view ) { 106 select( view ); 107 108 // maybe we can trigger the mousedown so that a view can listen to it. 109 // Prevent the selection from propagating to other plugins. 110 return false; 111 112 } else { 113 deselect(); 114 } 115 } ); 116 117 editor.on( 'init', function() { 118 var selection = editor.selection; 119 // When a view is selected, ensure content that is being pasted 120 // or inserted is added to a text node (instead of the view). 121 editor.on( 'BeforeSetContent', function() { 122 var walker, target, 123 view = getParentView( selection.getNode() ); 124 125 // If the selection is not within a view, bail. 126 if ( ! view ) { 17 127 return; 18 128 } 19 129 20 editor.on( 'PreInit', function() { 21 // Add elements so we can set `contenteditable` to false. 22 editor.schema.addValidElements('div[*],span[*]'); 23 }); 24 25 // When the editor's content changes, scan the new content for 26 // matching view patterns, and transform the matches into 27 // view wrappers. Since the editor's DOM is outdated at this point, 28 // we'll wait to render the views. 29 editor.on( 'BeforeSetContent', function( e ) { 30 if ( ! e.content ) { 31 return; 32 } 33 34 e.content = wp.mce.view.toViews( e.content ); 35 }); 36 37 // When the editor's content has been updated and the DOM has been 38 // processed, render the views in the document. 39 editor.on( 'SetContent', function() { 40 wp.mce.view.render( editor.getDoc() ); 41 }); 42 43 editor.on( 'init', function() { 44 var selection = editor.selection; 45 // When a view is selected, ensure content that is being pasted 46 // or inserted is added to a text node (instead of the view). 47 editor.on( 'BeforeSetContent', function() { 48 var walker, target, 49 view = wpView.getParentView( selection.getNode() ); 50 51 // If the selection is not within a view, bail. 52 if ( ! view ) { 53 return; 54 } 55 56 // If there are no additional nodes or the next node is a 57 // view, create a text node after the current view. 58 if ( ! view.nextSibling || wpView.isView( view.nextSibling ) ) { 59 target = editor.getDoc().createTextNode(''); 60 editor.dom.insertAfter( target, view ); 61 62 // Otherwise, find the next text node. 63 } else { 64 walker = new TreeWalker( view.nextSibling, view.nextSibling ); 65 target = walker.next(); 66 } 67 68 // Select the `target` text node. 69 selection.select( target ); 70 selection.collapse( true ); 71 }); 72 73 // When the selection's content changes, scan any new content 74 // for matching views and immediately render them. 75 // 76 // Runs on paste and on inserting nodes/html. 77 editor.on( 'SetContent', function( e ) { 78 if ( ! e.context ) { 79 return; 80 } 81 82 var node = selection.getNode(); 83 84 if ( ! node.innerHTML ) { 85 return; 86 } 87 88 node.innerHTML = wp.mce.view.toViews( node.innerHTML ); 89 wp.mce.view.render( node ); 90 }); 91 }); 92 93 // When the editor's contents are being accessed as a string, 94 // transform any views back to their text representations. 95 editor.on( 'PostProcess', function( e ) { 96 if ( ( ! e.get && ! e.save ) || ! e.content ) { 97 return; 98 } 99 100 e.content = wp.mce.view.toText( e.content ); 101 }); 102 103 // Triggers when the selection is changed. 104 // Add the event handler to the top of the stack. 105 editor.on( 'NodeChange', function( e ) { 106 var view = wpView.getParentView( e.element ); 107 108 // Update the selected view. 109 if ( view ) { 110 wpView.select( view ); 111 112 // Prevent the selection from propagating to other plugins. 113 return false; 114 115 // If we've clicked off of the selected view, deselect it. 116 } else { 117 wpView.deselect(); 118 } 119 }); 120 121 editor.on( 'keydown', function( event ) { 122 var keyCode = event.keyCode, 123 view, instance; 124 125 // If a view isn't selected, let the event go on its merry way. 126 if ( ! selected ) { 127 return; 128 } 129 130 // If the caret is not within the selected view, deselect the 131 // view and bail. 132 view = wpView.getParentView( editor.selection.getNode() ); 133 if ( view !== selected ) { 134 wpView.deselect(); 135 return; 136 } 137 138 // If delete or backspace is pressed, delete the view. 139 if ( keyCode === VK.DELETE || keyCode === VK.BACKSPACE ) { 140 if ( (instance = wp.mce.view.instance( selected )) ) { 141 instance.remove(); 142 wpView.deselect(); 143 } 144 } 145 146 // Let keypresses that involve the command or control keys through. 147 // Also, let any of the F# keys through. 148 if ( event.metaKey || event.ctrlKey || ( keyCode >= 112 && keyCode <= 123 ) ) { 149 return; 150 } 151 152 event.preventDefault(); 153 }); 154 }, 155 156 getParentView : function( node ) { 157 while ( node ) { 158 if ( this.isView( node ) ) { 159 return node; 160 } 161 162 node = node.parentNode; 130 // If there are no additional nodes or the next node is a 131 // view, create a text node after the current view. 132 if ( ! view.nextSibling || isView( view.nextSibling ) ) { 133 target = editor.getDoc().createTextNode(''); 134 editor.dom.insertAfter( target, view ); 135 136 // Otherwise, find the next text node. 137 } else { 138 walker = new TreeWalker( view.nextSibling, view.nextSibling ); 139 target = walker.next(); 163 140 } 164 },165 141 166 isView : function( node ) { 167 return (/(?:^|\s)wp-view-wrap(?:\s|$)/).test( node.className ); 168 }, 142 // Select the `target` text node. 143 selection.select( target ); 144 selection.collapse( true ); 145 }); 146 147 // When the selection's content changes, scan any new content 148 // for matching views and immediately render them. 149 // 150 // Runs on paste and on inserting nodes/html. 151 editor.on( 'SetContent', function( e ) { 152 if ( ! e.context ) { 153 return; 154 } 155 156 var node = selection.getNode(); 169 157 170 select : function( view ) { 171 if ( view === selected ) { 158 if ( ! node.innerHTML ) { 172 159 return; 173 160 } 174 161 175 this.deselect(); 176 selected = view; 177 wp.mce.view.select( selected ); 178 }, 162 node.innerHTML = wp.mce.view.toViews( node.innerHTML ); 163 wp.mce.view.render( node ); 164 }); 165 }); 166 167 // When the editor's contents are being accessed as a string, 168 // transform any views back to their text representations. 169 editor.on( 'PostProcess', function( e ) { 170 if ( ( ! e.get && ! e.save ) || ! e.content ) { 171 return; 172 } 173 174 e.content = wp.mce.view.toText( e.content ); 175 }); 176 177 editor.on( 'keydown', function( event ) { 178 var keyCode = event.keyCode, 179 view, instance; 180 181 // If a view isn't selected, let the event go on its merry way. 182 if ( ! selected ) { 183 return; 184 } 179 185 180 deselect : function() { 181 if ( selected ) { 182 wp.mce.view.deselect( selected ); 186 // Let keypresses that involve the command or control keys through. 187 // Also, let any of the F# keys through. 188 if ( event.metaKey || event.ctrlKey || ( keyCode >= 112 && keyCode <= 123 ) ) { 189 if ( ( event.metaKey || event.ctrlKey ) && keyCode === 88 ) { 190 removeSelected = true; 183 191 } 192 return; 193 } 184 194 185 selected = null; 195 // If the caret is not within the selected view, deselect the 196 // view and bail. 197 view = getParentView( editor.selection.getNode() ); 198 if ( view !== selected ) { 199 deselect(); 200 return; 186 201 } 202 203 // If delete or backspace is pressed, delete the view. 204 if ( keyCode === VK.DELETE || keyCode === VK.BACKSPACE ) { 205 if ( (instance = wp.mce.view.instance( selected )) ) { 206 instance.remove(); 207 deselect(); 208 } 209 } 210 211 event.preventDefault(); 187 212 }); 188 213 189 // Register plugin 190 tinymce.PluginManager.add( 'wpview', tinymce.plugins.wpView ); 191 })(); 214 editor.on( 'keyup', function() { 215 var instance; 216 217 if ( selected && removeSelected ) { 218 instance = wp.mce.view.instance( selected ); 219 removeSelected = false; 220 instance.remove(); 221 deselect(); 222 } 223 224 }); 225 }); -
src/wp-includes/js/tinymce/skins/wordpress/wp-content.css
diff --git src/wp-includes/js/tinymce/skins/wordpress/wp-content.css src/wp-includes/js/tinymce/skins/wordpress/wp-content.css index f0b9a73..3d5b71d 100644
img::selection { 183 183 outline: 1px solid #777; 184 184 } 185 185 186 187 /** 188 * WP Views 189 */ 190 191 /* delegate the handling of the selection to the wpview tinymce plugin */ 192 193 .wp-view-wrap, 194 .wp-view-wrap * { 195 -moz-user-select: none; 196 -webkit-user-select: none; 197 -ms-user-select: none; 198 user-select: none; 199 } 200 201 /* hide the shortcode content, but allow the content to still be selected */ 202 .wp-view-wrap .wp-view-shortcode { 203 position: absolute; 204 top: 20px; 205 width: 10px; 206 z-index: 100; 207 overflow: hidden; 208 opacity: 1; 209 left: -9999px; 210 display: block; 211 -moz-user-select: element; 212 -webkit-user-select: element; 213 -ms-user-select: element; 214 user-select: element; 215 216 } 217 218 /** 219 * Gallery preview 220 */ 221 .wp-view-type-gallery { 222 position: relative; 223 padding: 16px 0; 224 margin-bottom: 16px; 225 cursor: pointer; 226 } 227 228 .wp-view-type-gallery:after { 229 content: ''; 230 display: block; 231 height: 0; 232 clear: both; 233 visibility: hidden; 234 } 235 236 .wp-view-type-gallery.selected { 237 background-color: #efefef; 238 } 239 240 .wp-view-type-gallery .toolbar { 241 position: absolute; 242 top: 0; 243 left: 0; 244 background-color: #333; 245 color: white; 246 padding: 4px; 247 display: none; 248 } 249 250 .wp-view-type-gallery.selected .toolbar { 251 display: block; 252 } 253 254 .wp-view-type-gallery .toolbar span { 255 cursor: pointer; 256 } 257 258 .gallery img[data-mce-selected]:focus { 259 outline: none; 260 } 261 262 .gallery a { 263 cursor: default; 264 } 265 266 .gallery { 267 margin: auto; 268 line-height: 1; 269 } 270 271 .gallery .gallery-item { 272 float: left; 273 margin: 10px 0 0 0; 274 text-align: center; 275 } 276 277 .gallery .gallery-caption, 278 .gallery .gallery-icon { 279 margin: 0; 280 } 281 282 .gallery-columns-1 .gallery-item { 283 width: 100%; 284 } 285 286 .gallery-columns-2 .gallery-item { 287 width: 50%; 288 } 289 290 .gallery-columns-3 .gallery-item { 291 width: 33.333%; 292 } 293 294 .gallery-columns-4 .gallery-item { 295 width: 25%; 296 } 297 298 .gallery-columns-5 .gallery-item { 299 width: 20%; 300 } 301 302 .gallery-columns-6 .gallery-item { 303 width: 16%; 304 } 305 306 .gallery-columns-7 .gallery-item { 307 width: 14%; 308 } 309 310 .gallery-columns-8 .gallery-item { 311 width: 12%; 312 } 313 314 .gallery-columns-9 .gallery-item { 315 width: 11%; 316 } 317 318 .gallery img { 319 border: 2px solid #cfcfcf; 320 } 321 186 322 img.wp-oembed { 187 323 border: 1px dashed #888; 188 324 background: #f7f5f2 url("images/embedded.png") no-repeat scroll center center; -
src/wp-includes/media-template.php
diff --git src/wp-includes/media-template.php src/wp-includes/media-template.php index 28f4a1a..679a831 100644
function wp_print_media_templates() { 599 599 </script> 600 600 <?php 601 601 602 //TODO: do we want to deal with the fact that the elements used for gallery items are filterable and can be overriden via shortcode attributes 603 // do we want to deal with the difference between display and edit context at all? (e.g. wptexturize() being applied to the caption. 604 ?> 605 606 <script type="text/html" id="tmpl-editor-gallery"> 607 <div class="toolbar"> 608 <div class="dashicons dashicons-format-gallery edit"></div> 609 <div class="dashicons dashicons-no-alt remove"></div> 610 </div> 611 <div class="gallery gallery-columns-{{{ data.columns }}}"> 612 <# _.each( data.attachments, function( attachment, index ) { #> 613 <dl class="gallery-item"> 614 <dt class="gallery-icon"> 615 <?php // TODO: need to figure out the best way to make sure that we have thumbnails ?> 616 <img src="{{{ attachment.sizes.thumbnail.url }}}" /> 617 </dt> 618 <dd class="wp-caption-text gallery-caption"> 619 {{ attachment.caption }} 620 </dd> 621 </dl> 622 <?php // this is kind silly, but copied from the gallery shortcode. Maybe it should be removed ?> 623 <# if ( index % data.columns === data.columns - 1 ) { #> 624 <br style="clear: both;"> 625 <# } #> 626 627 <# } ); #> 628 </div> 629 </script> 630 <?php 631 602 632 /** 603 633 * Prints the media manager custom media templates. 604 634 *