diff --git a/wp-admin/js/editor.js b/wp-admin/js/editor.js
index 0e47b3e022..cabde4f6b8 100644
--- a/wp-admin/js/editor.js
+++ b/wp-admin/js/editor.js
@@ -99,8 +99,18 @@ window.wp = window.wp || {};
editorHeight = parseInt( textarea.style.height, 10 ) || 0;
- // Save the selection
- addHTMLBookmarkInTextAreaContent( $textarea, $ );
+ var keepSelection = false;
+ if ( editor ) {
+ keepSelection = editor.getParam( 'wp_keep_scroll_position' )
+ } else {
+ keepSelection = window.tinyMCEPreInit.mceInit[ id ] &&
+ window.tinyMCEPreInit.mceInit[ id ]['wp_keep_scroll_position']
+ }
+
+ if ( keepSelection ) {
+ // Save the selection
+ addHTMLBookmarkInTextAreaContent( $textarea );
+ }
if ( editor ) {
editor.show();
@@ -116,8 +126,10 @@ window.wp = window.wp || {};
}
}
- // Restore the selection
- focusHTMLBookmarkInVisualEditor( editor );
+ if ( editor.getParam( 'wp_keep_scroll_position' ) ) {
+ // Restore the selection
+ focusHTMLBookmarkInVisualEditor( editor );
+ }
} else {
tinymce.init( window.tinyMCEPreInit.mceInit[ id ] );
}
@@ -132,7 +144,6 @@ window.wp = window.wp || {};
return false;
}
- var selectionRange = null;
if ( editor ) {
// Don't resize the textarea in iOS. The iframe is forced to 100% height there, we shouldn't match it.
if ( ! tinymce.Env.iOS ) {
@@ -150,7 +161,11 @@ window.wp = window.wp || {};
}
}
- selectionRange = findBookmarkedPosition( editor );
+ var selectionRange = null;
+
+ if ( editor.getParam( 'wp_keep_scroll_position' ) ) {
+ selectionRange = findBookmarkedPosition( editor );
+ }
editor.hide();
@@ -234,9 +249,13 @@ window.wp = window.wp || {};
function getShortcodeWrapperInfo( content, cursorPosition ) {
var contentShortcodes = getShortCodePositionsInText( content );
- return _.find( contentShortcodes, function( element ) {
- return cursorPosition >= element.startIndex && cursorPosition <= element.endIndex;
- } );
+ for ( var i = 0; i < contentShortcodes.length; i++ ) {
+ var element = contentShortcodes[ i ];
+
+ if ( cursorPosition >= element.startIndex && cursorPosition <= element.endIndex ) {
+ return element;
+ }
+ }
}
/**
@@ -245,13 +264,20 @@ window.wp = window.wp || {};
* @param {string} content The content we want to scan for shortcodes.
*/
function getShortcodesInText( content ) {
- var shortcodes = content.match( /\[+([\w_-])+/g );
+ var shortcodes = content.match( /\[+([\w_-])+/g ),
+ result = [];
- return _.uniq(
- _.map( shortcodes, function( element ) {
- return element.replace( /^\[+/g, '' );
- } )
- );
+ if ( shortcodes ) {
+ for ( var i = 0; i < shortcodes.length; i++ ) {
+ var shortcode = shortcodes[ i ].replace( /^\[+/g, '' );
+
+ if ( result.indexOf( shortcode ) === -1 ) {
+ result.push( shortcode );
+ }
+ }
+ }
+
+ return result;
}
/**
@@ -335,6 +361,34 @@ window.wp = window.wp || {};
shortcodesDetails.push( shortcodeInfo );
}
+ /**
+ * Get all URL matches, and treat them as embeds.
+ *
+ * Since there isn't a good way to detect if a URL by itself on a line is a previewable
+ * object, it's best to treat all of them as such.
+ *
+ * This means that the selection will capture the whole URL, in a similar way shrotcodes
+ * are treated.
+ */
+ var urlRegexp = new RegExp(
+ '(^|[\\n\\r][\\n\\r]|)(https?:\\/\\/[^\s"]+?)(<\\/p>\s*|[\\n\\r][\\n\\r]|$)', 'gi'
+ );
+
+ while ( shortcodeMatch = urlRegexp.exec( content ) ) {
+ shortcodeInfo = {
+ shortcodeName: 'url',
+ showAsPlainText: false,
+ startIndex: shortcodeMatch.index,
+ endIndex: shortcodeMatch.index + shortcodeMatch[ 0 ].length,
+ length: shortcodeMatch[ 0 ].length,
+ isPreviewable: true,
+ urlAtStartOfContent: shortcodeMatch[ 1 ] === '',
+ urlAtEndOfContent: shortcodeMatch[ 3 ] === ''
+ };
+
+ shortcodesDetails.push( shortcodeInfo );
+ }
+
return shortcodesDetails;
}
@@ -399,8 +453,7 @@ window.wp = window.wp || {};
*/
if ( voidElements.indexOf( isCursorStartInTag.tagType ) !== -1 ) {
cursorStart = isCursorStartInTag.ltPos;
- }
- else {
+ } else {
cursorStart = isCursorStartInTag.gtPos;
}
}
@@ -412,12 +465,28 @@ window.wp = window.wp || {};
var isCursorStartInShortcode = getShortcodeWrapperInfo( content, cursorStart );
if ( isCursorStartInShortcode && isCursorStartInShortcode.isPreviewable ) {
- cursorStart = isCursorStartInShortcode.startIndex;
+ /**
+ * If a URL is at the start or the end of the content,
+ * the selection doesn't work, because it inserts a marker in the text,
+ * which breaks the embedURL detection.
+ *
+ * The best way to avoid that and not modify the user content is to
+ * adjust the cursor to either after or before URL.
+ */
+ if ( isCursorStartInShortcode.urlAtStartOfContent ) {
+ cursorStart = isCursorStartInShortcode.endIndex;
+ } else {
+ cursorStart = isCursorStartInShortcode.startIndex;
+ }
}
var isCursorEndInShortcode = getShortcodeWrapperInfo( content, cursorEnd );
if ( isCursorEndInShortcode && isCursorEndInShortcode.isPreviewable ) {
- cursorEnd = isCursorEndInShortcode.endIndex;
+ if ( isCursorEndInShortcode.urlAtEndOfContent ) {
+ cursorEnd = isCursorEndInShortcode.startIndex;
+ } else {
+ cursorEnd = isCursorEndInShortcode.endIndex;
+ }
}
return {
@@ -455,7 +524,7 @@ window.wp = window.wp || {};
mode = htmlModeCursorStartPosition !== htmlModeCursorEndPosition ? 'range' : 'single',
selectedText = null,
- cursorMarkerSkeleton = getCursorMarkerSpan( $$, '' );
+ cursorMarkerSkeleton = getCursorMarkerSpan( $$, '' ).attr( 'data-mce-type','bookmark' );
if ( mode === 'range' ) {
var markedText = textArea.value.slice( htmlModeCursorStartPosition, htmlModeCursorEndPosition ),
@@ -470,7 +539,7 @@ window.wp = window.wp || {};
textArea.value = [
textArea.value.slice( 0, htmlModeCursorStartPosition ), // text until the cursor/selection position
cursorMarkerSkeleton.clone() // cursor/selection start marker
- .addClass( 'mce_SELRES_start')[0].outerHTML,
+ .addClass( 'mce_SELRES_start' )[0].outerHTML,
selectedText, // selected text with end cursor/position marker
textArea.value.slice( htmlModeCursorEndPosition ) // text from last cursor/selection position to end
].join( '' );
@@ -487,8 +556,8 @@ window.wp = window.wp || {};
* @param {Object} editor TinyMCE editor instance.
*/
function focusHTMLBookmarkInVisualEditor( editor ) {
- var startNode = editor.$( '.mce_SELRES_start' ),
- endNode = editor.$( '.mce_SELRES_end' );
+ var startNode = editor.$( '.mce_SELRES_start' ).attr( 'data-mce-bogus', 1 ),
+ endNode = editor.$( '.mce_SELRES_end' ).attr( 'data-mce-bogus', 1 );
if ( startNode.length ) {
editor.focus();
@@ -505,8 +574,9 @@ window.wp = window.wp || {};
}
}
- scrollVisualModeToStartElement( editor, startNode );
-
+ if ( editor.getParam( 'wp_keep_scroll_position' ) ) {
+ scrollVisualModeToStartElement( editor, startNode );
+ }
removeSelectionMarker( startNode );
removeSelectionMarker( endNode );
@@ -548,13 +618,18 @@ window.wp = window.wp || {};
var elementTop = editor.$( element ).offset().top,
TinyMCEContentAreaTop = editor.$( editor.getContentAreaContainer() ).offset().top,
- edTools = $( '#wp-content-editor-tools' ),
- edToolsHeight = edTools.height(),
- edToolsOffsetTop = edTools.offset().top,
-
toolbarHeight = getToolbarHeight( editor ),
- windowHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight,
+ edTools = $( '#wp-content-editor-tools' ),
+ edToolsHeight = 0,
+ edToolsOffsetTop = 0;
+
+ if ( edTools.length ) {
+ edToolsHeight = edTools.height();
+ edToolsOffsetTop = edTools.offset().top;
+ }
+
+ var windowHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight,
selectionPosition = TinyMCEContentAreaTop + elementTop,
visibleAreaHeight = windowHeight - ( edToolsHeight + toolbarHeight );
@@ -675,8 +750,8 @@ window.wp = window.wp || {};
* This way we can adjust the selection to properly select only the content, ignoring
* whitespace inserted around the selected object by the Editor.
*/
- startElement.attr('data-mce-object-selection', 'true');
- endElement.attr('data-mce-object-selection', 'true');
+ startElement.attr( 'data-mce-object-selection', 'true' );
+ endElement.attr( 'data-mce-object-selection', 'true' );
editor.$( startNode ).before( startElement[0] );
editor.$( startNode ).after( endElement[0] );
diff --git a/wp-admin/js/editor.min.js b/wp-admin/js/editor.min.js
index 2f38355d82..b420cad561 100644
--- a/wp-admin/js/editor.min.js
+++ b/wp-admin/js/editor.min.js
@@ -1 +1 @@
-window.wp=window.wp||{},function(a,b){function c(){function c(){!x&&window.tinymce&&(x=window.tinymce,y=x.$,y(document).on("click",function(a){var b,c,d=y(a.target);d.hasClass("wp-switch-editor")&&(b=d.attr("data-wp-editor-id"),c=d.hasClass("switch-tmce")?"tmce":"html",e(b,c))}))}function d(a){var b=y(".mce-toolbar-grp",a.getContainer())[0],c=b&&b.clientHeight;return c&&c>10&&c<200?parseInt(c,10):30}function e(b,c){b=b||"content",c=c||"toggle";var e,f,g,h=x.get(b),i=y("#wp-"+b+"-wrap"),j=y("#"+b),k=j[0];if("toggle"===c&&(c=h&&!h.isHidden()?"html":"tmce"),"tmce"===c||"tinymce"===c){if(h&&!h.isHidden())return!1;"undefined"!=typeof window.QTags&&window.QTags.closeAllTags(b),e=parseInt(k.style.height,10)||0,m(j,a),h?(h.show(),!x.Env.iOS&&e&&(f=d(h),e=e-f+14,e>50&&e<5e3&&h.theme.resizeTo(null,e)),n(h)):x.init(window.tinyMCEPreInit.mceInit[b]),i.removeClass("html-active").addClass("tmce-active"),j.attr("aria-hidden",!0),window.setUserSetting("editor","tinymce")}else if("html"===c){if(h&&h.isHidden())return!1;var l=null;h?(x.Env.iOS||(g=h.iframeElement,e=g?parseInt(g.style.height,10):0,e&&(f=d(h),e=e+f-14,e>50&&e<5e3&&(k.style.height=e+"px"))),l=r(h),h.hide(),l&&s(h,l)):j.css({display:"",visibility:""}),i.removeClass("tmce-active").addClass("html-active"),j.attr("aria-hidden",!1),window.setUserSetting("editor","html")}}function f(a,b){var c=a.lastIndexOf("<",b-1),d=a.lastIndexOf(">",b);if(c>d||">"===a.substr(b,1)){var e=a.substr(c),f=e.match(/<\s*(\/)?(\w+)/);if(!f)return null;var g=f[2],h=e.indexOf(">");return{ltPos:c,gtPos:c+h+1,tagType:g,isClosingTag:!!f[1]}}return null}function g(a,b){var c=j(a);return _.find(c,function(a){return b>=a.startIndex&&b<=a.endIndex})}function h(a){var b=a.match(/\[+([\w_-])+/g);return _.uniq(_.map(b,function(a){return a.replace(/^\[+/g,"")}))}function i(a){var c=["caption"];return c.indexOf(a)!==-1||void 0!==b.mce.views.get(a)}function j(a){var c=h(a);if(0===c.length)return[];for(var d,e=b.shortcode.regexp(c.join("|")),f=[];d=e.exec(a);){var g="["===d[1],j=!g&&i(d[2]),k={shortcodeName:d[2],showAsPlainText:g,startIndex:d.index,endIndex:d.index+d[0].length,length:d[0].length,isPreviewable:j};f.push(k)}return f}function k(a,b){return a("").css({display:"inline-block",width:0,overflow:"hidden","line-height":0}).html(b?b:"")}function l(a,b){var c=["area","base","br","col","embed","hr","img","input","keygen","link","meta","param","source","track","wbr"],d=b.cursorStart,e=b.cursorEnd,h=f(a,d);h&&(d=c.indexOf(h.tagType)!==-1?h.ltPos:h.gtPos);var i=f(a,e);i&&(e=i.gtPos);var j=g(a,d);j&&j.isPreviewable&&(d=j.startIndex);var k=g(a,e);return k&&k.isPreviewable&&(e=k.endIndex),{cursorStart:d,cursorEnd:e}}function m(a){if(a&&a.length){var b=a[0],c=b.value,d=l(c,{cursorStart:b.selectionStart,cursorEnd:b.selectionEnd}),e=d.cursorStart,f=d.cursorEnd,g=e!==f?"range":"single",h=null,i=k(y,"");if("range"===g){var j=b.value.slice(e,f),m=i.clone().addClass("mce_SELRES_end");h=[j,m[0].outerHTML].join("")}b.value=[b.value.slice(0,e),i.clone().addClass("mce_SELRES_start")[0].outerHTML,h,b.value.slice(f)].join("")}}function n(a){var b=a.$(".mce_SELRES_start"),c=a.$(".mce_SELRES_end");if(b.length)if(a.focus(),c.length){var d=a.getDoc().createRange();d.setStartAfter(b[0]),d.setEndBefore(c[0]),a.selection.setRng(d)}else a.selection.select(b[0]);p(a,b),o(b),o(c)}function o(a){var b=a.parent();a.remove(),!b.is("p")||b.children().length||b.text()||b.remove()}function p(b,c){var e=b.$(c).offset().top,f=b.$(b.getContentAreaContainer()).offset().top,g=a("#wp-content-editor-tools"),h=g.height(),i=g.offset().top,j=d(b),k=window.innerHeight||document.documentElement.clientHeight||document.body.clientHeight,l=f+e,m=k-(h+j),n=Math.max(l-m/2,i-h);a("html,body").animate({scrollTop:parseInt(n,10)},100)}function q(a){a.content=a.content.replace(/(?:
|\u00a0|\uFEFF| )*<\/p>/g,"
")}function r(a){var b=a.getWin(),c=b.getSelection();if(!(c.rangeCount<=0)){var d="SELRES_"+Math.random(),e=k(a.$,d),f=e.clone().addClass("mce_SELRES_start"),g=e.clone().addClass("mce_SELRES_end"),h=c.getRangeAt(0),i=h.startContainer,j=h.startOffset,l=h.cloneRange();a.$(i).parents(".mce-offscreen-selection").length>0?(i=a.$("[data-mce-selected]")[0],f.attr("data-mce-object-selection","true"),g.attr("data-mce-object-selection","true"),a.$(i).before(f[0]),a.$(i).after(g[0])):(l.collapse(!1),l.insertNode(g[0]),l.setStart(i,j),l.collapse(!0),l.insertNode(f[0]),h.setStartAfter(f[0]),h.setEndBefore(g[0]),c.removeAllRanges(),c.addRange(h)),a.on("GetContent",q);var m=t(a.getContent());a.off("GetContent",q),f.remove(),g.remove();var n=new RegExp(']*\\s*class="mce_SELRES_start"[^>]+>\\s*'+d+"[^<]*<\\/span>(\\s*)"),o=new RegExp('(\\s*)]*\\s*class="mce_SELRES_end"[^>]+>\\s*'+d+"[^<]*<\\/span>"),p=m.match(n),r=m.match(o);if(!p)return null;var s=p.index,u=p[0].length,v=null;if(r){p[0].indexOf("data-mce-object-selection")!==-1&&(u-=p[1].length);var w=r.index;r[0].indexOf("data-mce-object-selection")!==-1&&(w-=r[1].length),v=w-u}return{start:s,end:v}}}function s(a,b){if(b){var c=a.getElement(),d=b.start,e=b.end||b.start;c.focus&&(setTimeout(function(){c.blur&&c.blur(),c.focus()},100),c.focus()),c.setSelectionRange(d,e)}}function t(a){var b="blockquote|ul|ol|li|dl|dt|dd|table|thead|tbody|tfoot|tr|th|td|h[1-6]|fieldset|figure",c=b+"|div|p",d=b+"|pre",e=!1,f=!1,g=[];return a?(a.indexOf("