diff --git a/src/wp-admin/js/editor.js b/src/wp-admin/js/editor.js
index 0e47b3e022..961fc8b831 100644
|
a
|
b
|
window.wp = window.wp || {}; |
| 99 | 99 | |
| 100 | 100 | editorHeight = parseInt( textarea.style.height, 10 ) || 0; |
| 101 | 101 | |
| 102 | | // Save the selection |
| 103 | | addHTMLBookmarkInTextAreaContent( $textarea, $ ); |
| | 102 | var keepSelection = false; |
| | 103 | if (editor) { |
| | 104 | keepSelection = editor.getParam( 'wp_keep_editor_selection' ) |
| | 105 | } |
| | 106 | else { |
| | 107 | keepSelection = window.tinyMCEPreInit.mceInit[ id ] && |
| | 108 | window.tinyMCEPreInit.mceInit[ id ]['wp_keep_editor_selection'] |
| | 109 | } |
| | 110 | |
| | 111 | if ( keepSelection ) { |
| | 112 | // Save the selection |
| | 113 | addHTMLBookmarkInTextAreaContent( $textarea ); |
| | 114 | } |
| 104 | 115 | |
| 105 | 116 | if ( editor ) { |
| 106 | 117 | editor.show(); |
| … |
… |
window.wp = window.wp || {}; |
| 116 | 127 | } |
| 117 | 128 | } |
| 118 | 129 | |
| 119 | | // Restore the selection |
| 120 | | focusHTMLBookmarkInVisualEditor( editor ); |
| | 130 | if ( editor.getParam( 'wp_keep_editor_selection' ) ) { |
| | 131 | // Restore the selection |
| | 132 | focusHTMLBookmarkInVisualEditor( editor ); |
| | 133 | } |
| 121 | 134 | } else { |
| 122 | 135 | tinymce.init( window.tinyMCEPreInit.mceInit[ id ] ); |
| 123 | 136 | } |
| … |
… |
window.wp = window.wp || {}; |
| 132 | 145 | return false; |
| 133 | 146 | } |
| 134 | 147 | |
| 135 | | var selectionRange = null; |
| 136 | 148 | if ( editor ) { |
| 137 | 149 | // Don't resize the textarea in iOS. The iframe is forced to 100% height there, we shouldn't match it. |
| 138 | 150 | if ( ! tinymce.Env.iOS ) { |
| … |
… |
window.wp = window.wp || {}; |
| 150 | 162 | } |
| 151 | 163 | } |
| 152 | 164 | |
| 153 | | selectionRange = findBookmarkedPosition( editor ); |
| | 165 | var selectionRange = null; |
| | 166 | |
| | 167 | if ( editor.getParam( 'wp_keep_editor_selection' ) ) { |
| | 168 | selectionRange = findBookmarkedPosition( editor ); |
| | 169 | } |
| 154 | 170 | |
| 155 | 171 | editor.hide(); |
| 156 | 172 | |
| … |
… |
window.wp = window.wp || {}; |
| 234 | 250 | function getShortcodeWrapperInfo( content, cursorPosition ) { |
| 235 | 251 | var contentShortcodes = getShortCodePositionsInText( content ); |
| 236 | 252 | |
| 237 | | return _.find( contentShortcodes, function( element ) { |
| 238 | | return cursorPosition >= element.startIndex && cursorPosition <= element.endIndex; |
| 239 | | } ); |
| | 253 | for ( var i = 0; i < contentShortcodes.length; i++ ) { |
| | 254 | var element = contentShortcodes[ i ]; |
| | 255 | |
| | 256 | if ( cursorPosition >= element.startIndex && cursorPosition <= element.endIndex ) { |
| | 257 | return element; |
| | 258 | } |
| | 259 | } |
| 240 | 260 | } |
| 241 | 261 | |
| 242 | 262 | /** |
| … |
… |
window.wp = window.wp || {}; |
| 245 | 265 | * @param {string} content The content we want to scan for shortcodes. |
| 246 | 266 | */ |
| 247 | 267 | function getShortcodesInText( content ) { |
| 248 | | var shortcodes = content.match( /\[+([\w_-])+/g ); |
| | 268 | var shortcodes = content.match( /\[+([\w_-])+/g ), |
| | 269 | result = []; |
| 249 | 270 | |
| 250 | | return _.uniq( |
| 251 | | _.map( shortcodes, function( element ) { |
| 252 | | return element.replace( /^\[+/g, '' ); |
| 253 | | } ) |
| 254 | | ); |
| | 271 | for ( var i = 0; i < shortcodes.length; i++ ) { |
| | 272 | var shortcode = shortcodes[ i ].replace( /^\[+/g, '' ); |
| | 273 | |
| | 274 | if ( result.indexOf( shortcode ) === -1 ) { |
| | 275 | result.push( shortcode ); |
| | 276 | } |
| | 277 | } |
| | 278 | |
| | 279 | return result; |
| 255 | 280 | } |
| 256 | 281 | |
| 257 | 282 | /** |
| … |
… |
window.wp = window.wp || {}; |
| 335 | 360 | shortcodesDetails.push( shortcodeInfo ); |
| 336 | 361 | } |
| 337 | 362 | |
| | 363 | /** |
| | 364 | * Get all URL matches, and treat them as embeds. |
| | 365 | * |
| | 366 | * Since there isn't a good way to detect if a URL by itself on a line is a previewable |
| | 367 | * object, it's best to treat all of them as such. |
| | 368 | * |
| | 369 | * This means that the selection will capture the whole URL, in a similar way shrotcodes |
| | 370 | * are treated. |
| | 371 | */ |
| | 372 | var urlRegexp = new RegExp( |
| | 373 | '(^|[\\n\\r][\\n\\r]|<p>)(https?:\\/\\/[^\s"]+?)(<\\/p>\s*|[\\n\\r][\\n\\r]|$)', 'gi' |
| | 374 | ); |
| | 375 | |
| | 376 | while ( shortcodeMatch = urlRegexp.exec( content ) ) { |
| | 377 | shortcodeInfo = { |
| | 378 | shortcodeName: 'url', |
| | 379 | showAsPlainText: false, |
| | 380 | startIndex: shortcodeMatch.index, |
| | 381 | endIndex: shortcodeMatch.index + shortcodeMatch[ 0 ].length, |
| | 382 | length: shortcodeMatch[ 0 ].length, |
| | 383 | isPreviewable: true, |
| | 384 | urlAtStartOfContent: shortcodeMatch[ 1 ] === '', |
| | 385 | urlAtEndOfContent: shortcodeMatch[ 3 ] === '' |
| | 386 | }; |
| | 387 | |
| | 388 | shortcodesDetails.push( shortcodeInfo ); |
| | 389 | } |
| | 390 | |
| 338 | 391 | return shortcodesDetails; |
| 339 | 392 | } |
| 340 | 393 | |
| … |
… |
window.wp = window.wp || {}; |
| 412 | 465 | |
| 413 | 466 | var isCursorStartInShortcode = getShortcodeWrapperInfo( content, cursorStart ); |
| 414 | 467 | if ( isCursorStartInShortcode && isCursorStartInShortcode.isPreviewable ) { |
| 415 | | cursorStart = isCursorStartInShortcode.startIndex; |
| | 468 | /** |
| | 469 | * If a URL is at the start or the end of the content, |
| | 470 | * the selection doesn't work, because it inserts a marker in the text, |
| | 471 | * which breaks the embedURL detection. |
| | 472 | * |
| | 473 | * The best way to avoid that and not modify the user content is to |
| | 474 | * adjust the cursor to either after or before URL. |
| | 475 | */ |
| | 476 | if (isCursorStartInShortcode.urlAtStartOfContent) { |
| | 477 | cursorStart = isCursorStartInShortcode.endIndex; |
| | 478 | } |
| | 479 | else { |
| | 480 | cursorStart = isCursorStartInShortcode.startIndex; |
| | 481 | } |
| 416 | 482 | } |
| 417 | 483 | |
| 418 | 484 | var isCursorEndInShortcode = getShortcodeWrapperInfo( content, cursorEnd ); |
| 419 | 485 | if ( isCursorEndInShortcode && isCursorEndInShortcode.isPreviewable ) { |
| 420 | | cursorEnd = isCursorEndInShortcode.endIndex; |
| | 486 | if (isCursorEndInShortcode.urlAtEndOfContent) { |
| | 487 | cursorEnd = isCursorEndInShortcode.startIndex; |
| | 488 | } |
| | 489 | else { |
| | 490 | cursorEnd = isCursorEndInShortcode.endIndex; |
| | 491 | } |
| 421 | 492 | } |
| 422 | 493 | |
| 423 | 494 | return { |
| … |
… |
window.wp = window.wp || {}; |
| 455 | 526 | mode = htmlModeCursorStartPosition !== htmlModeCursorEndPosition ? 'range' : 'single', |
| 456 | 527 | |
| 457 | 528 | selectedText = null, |
| 458 | | cursorMarkerSkeleton = getCursorMarkerSpan( $$, '' ); |
| | 529 | cursorMarkerSkeleton = getCursorMarkerSpan( $$, '' ).attr( 'data-mce-type','bookmark' ); |
| 459 | 530 | |
| 460 | 531 | if ( mode === 'range' ) { |
| 461 | 532 | var markedText = textArea.value.slice( htmlModeCursorStartPosition, htmlModeCursorEndPosition ), |
| … |
… |
window.wp = window.wp || {}; |
| 470 | 541 | textArea.value = [ |
| 471 | 542 | textArea.value.slice( 0, htmlModeCursorStartPosition ), // text until the cursor/selection position |
| 472 | 543 | cursorMarkerSkeleton.clone() // cursor/selection start marker |
| 473 | | .addClass( 'mce_SELRES_start')[0].outerHTML, |
| | 544 | .addClass( 'mce_SELRES_start' )[0].outerHTML, |
| 474 | 545 | selectedText, // selected text with end cursor/position marker |
| 475 | 546 | textArea.value.slice( htmlModeCursorEndPosition ) // text from last cursor/selection position to end |
| 476 | 547 | ].join( '' ); |
| … |
… |
window.wp = window.wp || {}; |
| 487 | 558 | * @param {Object} editor TinyMCE editor instance. |
| 488 | 559 | */ |
| 489 | 560 | function focusHTMLBookmarkInVisualEditor( editor ) { |
| 490 | | var startNode = editor.$( '.mce_SELRES_start' ), |
| 491 | | endNode = editor.$( '.mce_SELRES_end' ); |
| | 561 | var startNode = editor.$( '.mce_SELRES_start' ).attr( 'data-mce-bogus', 1 ), |
| | 562 | endNode = editor.$( '.mce_SELRES_end' ).attr( 'data-mce-bogus', 1 ); |
| 492 | 563 | |
| 493 | 564 | if ( startNode.length ) { |
| 494 | 565 | editor.focus(); |
| … |
… |
window.wp = window.wp || {}; |
| 505 | 576 | } |
| 506 | 577 | } |
| 507 | 578 | |
| 508 | | scrollVisualModeToStartElement( editor, startNode ); |
| 509 | | |
| | 579 | if ( editor.getParam( 'wp_keep_scroll_position' ) ) { |
| | 580 | scrollVisualModeToStartElement( editor, startNode ); |
| | 581 | } |
| 510 | 582 | |
| 511 | 583 | removeSelectionMarker( startNode ); |
| 512 | 584 | removeSelectionMarker( endNode ); |
| … |
… |
window.wp = window.wp || {}; |
| 548 | 620 | var elementTop = editor.$( element ).offset().top, |
| 549 | 621 | TinyMCEContentAreaTop = editor.$( editor.getContentAreaContainer() ).offset().top, |
| 550 | 622 | |
| | 623 | toolbarHeight = getToolbarHeight( editor ), |
| | 624 | |
| 551 | 625 | edTools = $( '#wp-content-editor-tools' ), |
| 552 | | edToolsHeight = edTools.height(), |
| 553 | | edToolsOffsetTop = edTools.offset().top, |
| | 626 | edToolsHeight = 0, |
| | 627 | edToolsOffsetTop = 0; |
| 554 | 628 | |
| 555 | | toolbarHeight = getToolbarHeight( editor ), |
| | 629 | if ( edTools.length ) { |
| | 630 | edToolsHeight = edTools.height(); |
| | 631 | edToolsOffsetTop = edTools.offset().top; |
| | 632 | } |
| 556 | 633 | |
| 557 | | windowHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight, |
| | 634 | var windowHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight, |
| 558 | 635 | |
| 559 | 636 | selectionPosition = TinyMCEContentAreaTop + elementTop, |
| 560 | 637 | visibleAreaHeight = windowHeight - ( edToolsHeight + toolbarHeight ); |
| … |
… |
window.wp = window.wp || {}; |
| 675 | 752 | * This way we can adjust the selection to properly select only the content, ignoring |
| 676 | 753 | * whitespace inserted around the selected object by the Editor. |
| 677 | 754 | */ |
| 678 | | startElement.attr('data-mce-object-selection', 'true'); |
| 679 | | endElement.attr('data-mce-object-selection', 'true'); |
| | 755 | startElement.attr( 'data-mce-object-selection', 'true' ); |
| | 756 | endElement.attr( 'data-mce-object-selection', 'true' ); |
| 680 | 757 | |
| 681 | 758 | editor.$( startNode ).before( startElement[0] ); |
| 682 | 759 | editor.$( startNode ).after( endElement[0] ); |
| … |
… |
window.wp = window.wp || {}; |
| 776 | 853 | end = selection.end || selection.start; |
| 777 | 854 | |
| 778 | 855 | if ( textArea.focus ) { |
| 779 | | // focus and scroll to the position |
| 780 | | setTimeout( function() { |
| 781 | | if ( textArea.blur ) { |
| 782 | | // defocus before focusing |
| 783 | | textArea.blur(); |
| 784 | | } |
| 785 | | textArea.focus(); |
| 786 | | }, 100 ); |
| | 856 | if ( editor.getParam( 'wp_keep_scroll_position' ) ) { |
| | 857 | // focus and scroll to the position |
| | 858 | setTimeout( function() { |
| | 859 | if ( textArea.blur ) { |
| | 860 | // defocus before focusing |
| | 861 | textArea.blur(); |
| | 862 | } |
| | 863 | textArea.focus(); |
| | 864 | }, 100 ); |
| | 865 | } |
| 787 | 866 | |
| 788 | 867 | textArea.focus(); |
| 789 | 868 | } |
diff --git a/src/wp-includes/class-wp-editor.php b/src/wp-includes/class-wp-editor.php
index 1a402beeb7..0ff14c10aa 100644
|
a
|
b
|
private static function default_settings() { |
| 981 | 981 | 'end_container_on_empty_block' => true, |
| 982 | 982 | 'wpeditimage_html5_captions' => true, |
| 983 | 983 | 'wp_lang_attr' => get_bloginfo( 'language' ), |
| | 984 | 'wp_keep_editor_selection' => false, |
| | 985 | 'wp_keep_scroll_position' => false, |
| 984 | 986 | 'wp_shortcut_labels' => wp_json_encode( $shortcut_labels ), |
| 985 | 987 | ); |
| 986 | 988 | |