wp/wp-includes/js/wplink.js
changeset 7 cf61fcea0001
parent 5 5e2f62d02dcd
child 9 177826044cd9
--- a/wp/wp-includes/js/wplink.js	Tue Jun 09 11:14:17 2015 +0000
+++ b/wp/wp-includes/js/wplink.js	Mon Oct 14 17:39:30 2019 +0200
@@ -1,14 +1,15 @@
-/* global ajaxurl, tinymce, wpLinkL10n, setUserSetting, wpActiveEditor */
 var wpLink;
 
-( function( $ ) {
-	var editor, searchTimer, River, Query, correctedURL,
+( function( $, wpLinkL10n, wp ) {
+	var editor, searchTimer, River, Query, correctedURL, linkNode,
+		emailRegexp = /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,63}$/i,
+		urlRegexp = /^(https?|ftp):\/\/[A-Z0-9.-]+\.[A-Z]{2,63}[^ "]*$/i,
 		inputs = {},
 		rivers = {},
 		isTouch = ( 'ontouchend' in document );
 
 	function getLink() {
-		return editor.dom.getParent( editor.selection.getNode(), 'a' );
+		return linkNode || editor.dom.getParent( editor.selection.getNode(), 'a[href]' );
 	}
 
 	wpLink = {
@@ -18,6 +19,7 @@
 		keySensitivity: 100,
 		lastSearch: '',
 		textarea: '',
+		modalOpen: false,
 
 		init: function() {
 			inputs.wrap = $('#wp-link-wrap');
@@ -50,13 +52,12 @@
 				event.preventDefault();
 				wpLink.update();
 			});
-			inputs.close.add( inputs.backdrop ).add( '#wp-link-cancel a' ).click( function( event ) {
+
+			inputs.close.add( inputs.backdrop ).add( '#wp-link-cancel button' ).click( function( event ) {
 				event.preventDefault();
 				wpLink.close();
 			});
 
-			$( '#wp-link-search-toggle' ).on( 'click', wpLink.toggleInternalLinking );
-
 			rivers.elements.on( 'river-select', wpLink.updateFields );
 
 			// Display 'hint' message when search field or 'query-results' box are focused
@@ -68,35 +69,37 @@
 				inputs.queryNoticeTextHint.addClass( 'screen-reader-text' ).hide();
 			} );
 
-			inputs.search.keyup( function() {
-				var self = this;
-
+			inputs.search.on( 'keyup input', function() {
 				window.clearTimeout( searchTimer );
 				searchTimer = window.setTimeout( function() {
-					wpLink.searchInternalLinks.call( self );
+					wpLink.searchInternalLinks();
 				}, 500 );
 			});
 
-			function correctURL() {
-				var url = $.trim( inputs.url.val() );
-
-				if ( url && correctedURL !== url && ! /^(?:[a-z]+:|#|\?|\.|\/)/.test( url ) ) {
-					inputs.url.val( 'http://' + url );
-					correctedURL = url;
-				}
-			}
-
 			inputs.url.on( 'paste', function() {
-				setTimeout( correctURL, 0 );
+				setTimeout( wpLink.correctURL, 0 );
 			} );
 
-			inputs.url.on( 'blur', correctURL );
+			inputs.url.on( 'blur', wpLink.correctURL );
 		},
 
-		open: function( editorId ) {
-			var ed;
+		// If URL wasn't corrected last time and doesn't start with http:, https:, ? # or /, prepend http://
+		correctURL: function () {
+			var url = $.trim( inputs.url.val() );
 
-			$( document.body ).addClass( 'modal-open' );
+			if ( url && correctedURL !== url && ! /^(?:[a-z]+:|#|\?|\.|\/)/.test( url ) ) {
+				inputs.url.val( 'http://' + url );
+				correctedURL = url;
+			}
+		},
+
+		open: function( editorId, url, text, node ) {
+			var ed,
+				$body = $( document.body );
+
+			$body.addClass( 'modal-open' );
+			wpLink.modalOpen = true;
+			linkNode = node;
 
 			wpLink.range = null;
 
@@ -110,18 +113,18 @@
 
 			this.textarea = $( '#' + window.wpActiveEditor ).get( 0 );
 
-			if ( typeof tinymce !== 'undefined' ) {
-				ed = tinymce.get( wpActiveEditor );
+			if ( typeof window.tinymce !== 'undefined' ) {
+				// Make sure the link wrapper is the last element in the body,
+				// or the inline editor toolbar may show above the backdrop.
+				$body.append( inputs.backdrop, inputs.wrap );
+
+				ed = window.tinymce.get( window.wpActiveEditor );
 
 				if ( ed && ! ed.isHidden() ) {
 					editor = ed;
 				} else {
 					editor = null;
 				}
-
-				if ( editor && tinymce.isIE ) {
-					editor.windowManager.bookmark = editor.selection.getBookmark();
-				}
 			}
 
 			if ( ! wpLink.isMCE() && document.selection ) {
@@ -132,7 +135,7 @@
 			inputs.wrap.show();
 			inputs.backdrop.show();
 
-			wpLink.refresh();
+			wpLink.refresh( url, text );
 
 			$( document ).trigger( 'wplink-open', inputs.wrap );
 		},
@@ -141,15 +144,15 @@
 			return editor && ! editor.isHidden();
 		},
 
-		refresh: function() {
-			var text = '';
+		refresh: function( url, text ) {
+			var linkText = '';
 
 			// Refresh rivers (clear links, check visibility)
 			rivers.search.refresh();
 			rivers.recent.refresh();
 
 			if ( wpLink.isMCE() ) {
-				wpLink.mceRefresh();
+				wpLink.mceRefresh( url, text );
 			} else {
 				// For the Text editor the "Link text" field is always shown
 				if ( ! inputs.wrap.hasClass( 'has-text-field' ) ) {
@@ -158,11 +161,11 @@
 
 				if ( document.selection ) {
 					// Old IE
-					text = document.selection.createRange().text || '';
+					linkText = document.selection.createRange().text || text || '';
 				} else if ( typeof this.textarea.selectionStart !== 'undefined' &&
 					( this.textarea.selectionStart !== this.textarea.selectionEnd ) ) {
 					// W3C
-					text = this.textarea.value.substring( this.textarea.selectionStart, this.textarea.selectionEnd ) || '';
+					text = this.textarea.value.substring( this.textarea.selectionStart, this.textarea.selectionEnd ) || text || '';
 				}
 
 				inputs.text.val( text );
@@ -176,7 +179,10 @@
 				// Focus the URL field and highlight its contents.
 				// If this is moved above the selection changes,
 				// IE will show a flashing cursor over the dialog.
-				inputs.url.focus()[0].select();
+				window.setTimeout( function() {
+					inputs.url[0].select();
+					inputs.url.focus();
+				} );
 			}
 
 			// Load the most recent results if this is the first time opening the panel.
@@ -188,7 +194,7 @@
 		},
 
 		hasSelectedText: function( linkNode ) {
-			var html = editor.selection.getContent();
+			var node, nodes, i, html = editor.selection.getContent();
 
 			// Partial html and not a fully selected anchor element
 			if ( /</.test( html ) && ( ! /^<a [^>]+>[^<]+<\/a>$/.test( html ) || html.indexOf('href=') === -1 ) ) {
@@ -196,14 +202,16 @@
 			}
 
 			if ( linkNode ) {
-				var nodes = linkNode.childNodes, i;
+				nodes = linkNode.childNodes;
 
 				if ( nodes.length === 0 ) {
 					return false;
 				}
 
 				for ( i = nodes.length - 1; i >= 0; i-- ) {
-					if ( nodes[i].nodeType != 3 ) {
+					node = nodes[i];
+
+					if ( node.nodeType != 3 && ! window.tinymce.dom.BookmarkManager.isBookmarkNode( node ) ) {
 						return false;
 					}
 				}
@@ -212,24 +220,49 @@
 			return true;
 		},
 
-		mceRefresh: function() {
-			var text,
-				selectedNode = editor.selection.getNode(),
-				linkNode = editor.dom.getParent( selectedNode, 'a[href]' ),
+		mceRefresh: function( searchStr, text ) {
+			var linkText, href,
+				linkNode = getLink(),
 				onlyText = this.hasSelectedText( linkNode );
 
 			if ( linkNode ) {
-				text = linkNode.innerText || linkNode.textContent;
-				inputs.url.val( editor.dom.getAttrib( linkNode, 'href' ) );
-				inputs.openInNewTab.prop( 'checked', '_blank' === editor.dom.getAttrib( linkNode, 'target' ) );
-				inputs.submit.val( wpLinkL10n.update );
+				linkText = linkNode.textContent || linkNode.innerText;
+				href = editor.dom.getAttrib( linkNode, 'href' );
+
+				if ( ! $.trim( linkText ) ) {
+					linkText = text || '';
+				}
+
+				if ( searchStr && ( urlRegexp.test( searchStr ) || emailRegexp.test( searchStr ) ) ) {
+					href = searchStr;
+				}
+
+				if ( href !== '_wp_link_placeholder' ) {
+					inputs.url.val( href );
+					inputs.openInNewTab.prop( 'checked', '_blank' === editor.dom.getAttrib( linkNode, 'target' ) );
+					inputs.submit.val( wpLinkL10n.update );
+				} else {
+					this.setDefaultValues( linkText );
+				}
+
+				if ( searchStr && searchStr !== href ) {
+					// The user has typed something in the inline dialog. Trigger a search with it.
+					inputs.search.val( searchStr );
+				} else {
+					inputs.search.val( '' );
+				}
+
+				// Always reset the search
+				window.setTimeout( function() {
+					wpLink.searchInternalLinks();
+				} );
 			} else {
-				text = editor.selection.getContent({ format: 'text' });
-				this.setDefaultValues();
+				linkText = editor.selection.getContent({ format: 'text' }) || text || '';
+				this.setDefaultValues( linkText );
 			}
 
 			if ( onlyText ) {
-				inputs.text.val( text || '' );
+				inputs.text.val( linkText );
 				inputs.wrap.addClass( 'has-text-field' );
 			} else {
 				inputs.text.val( '' );
@@ -237,18 +270,25 @@
 			}
 		},
 
-		close: function() {
+		close: function( reset ) {
 			$( document.body ).removeClass( 'modal-open' );
+			wpLink.modalOpen = false;
 
-			if ( ! wpLink.isMCE() ) {
-				wpLink.textarea.focus();
+			if ( reset !== 'noReset' ) {
+				if ( ! wpLink.isMCE() ) {
+					wpLink.textarea.focus();
 
-				if ( wpLink.range ) {
-					wpLink.range.moveToBookmark( wpLink.range.getBookmark() );
-					wpLink.range.select();
+					if ( wpLink.range ) {
+						wpLink.range.moveToBookmark( wpLink.range.getBookmark() );
+						wpLink.range.select();
+					}
+				} else {
+					if ( editor.plugins.wplink ) {
+						editor.plugins.wplink.close();
+					}
+
+					editor.focus();
 				}
-			} else {
-				editor.focus();
 			}
 
 			inputs.backdrop.hide();
@@ -260,12 +300,24 @@
 		},
 
 		getAttrs: function() {
+			wpLink.correctURL();
+
 			return {
 				href: $.trim( inputs.url.val() ),
-				target: inputs.openInNewTab.prop( 'checked' ) ? '_blank' : ''
+				target: inputs.openInNewTab.prop( 'checked' ) ? '_blank' : null
 			};
 		},
 
+		buildHtml: function(attrs) {
+			var html = '<a href="' + attrs.href + '"';
+
+			if ( attrs.target ) {
+				html += ' rel="noopener" target="' + attrs.target + '"';
+			}
+
+			return html + '>';
+		},
+
 		update: function() {
 			if ( wpLink.isMCE() ) {
 				wpLink.mceUpdate();
@@ -285,19 +337,19 @@
 			attrs = wpLink.getAttrs();
 			text = inputs.text.val();
 
+			var parser = document.createElement( 'a' );
+			parser.href = attrs.href;
+
+			if ( 'javascript:' === parser.protocol || 'data:' === parser.protocol ) { // jshint ignore:line
+				attrs.href = '';
+			}
+
 			// If there's no href, return.
 			if ( ! attrs.href ) {
 				return;
 			}
 
-			// Build HTML
-			html = '<a href="' + attrs.href + '"';
-
-			if ( attrs.target ) {
-				html += ' target="' + attrs.target + '"';
-			}
-
-			html += '>';
+			html = wpLink.buildHtml(attrs);
 
 			// Insert HTML
 			if ( document.selection && wpLink.range ) {
@@ -335,81 +387,123 @@
 
 			wpLink.close();
 			textarea.focus();
+			$( textarea ).trigger( 'change' );
+
+			// Audible confirmation message when a link has been inserted in the Editor.
+			wp.a11y.speak( wpLinkL10n.linkInserted );
 		},
 
 		mceUpdate: function() {
 			var attrs = wpLink.getAttrs(),
-				link, text;
+				$link, text, hasText, $mceCaret;
 
-			wpLink.close();
-			editor.focus();
+			var parser = document.createElement( 'a' );
+			parser.href = attrs.href;
 
-			if ( tinymce.isIE ) {
-				editor.selection.moveToBookmark( editor.windowManager.bookmark );
+			if ( 'javascript:' === parser.protocol || 'data:' === parser.protocol ) { // jshint ignore:line
+				attrs.href = '';
 			}
 
 			if ( ! attrs.href ) {
 				editor.execCommand( 'unlink' );
+				wpLink.close();
 				return;
 			}
 
-			link = getLink();
-			text = inputs.text.val();
+			$link = editor.$( getLink() );
 
-			if ( link ) {
-				if ( text ) {
-					if ( 'innerText' in link ) {
-						link.innerText = text;
-					} else {
-						link.textContent = text;
-					}
+			editor.undoManager.transact( function() {
+				if ( ! $link.length ) {
+					editor.execCommand( 'mceInsertLink', false, { href: '_wp_link_placeholder', 'data-wp-temp-link': 1 } );
+					$link = editor.$( 'a[data-wp-temp-link="1"]' ).removeAttr( 'data-wp-temp-link' );
+					hasText = $.trim( $link.text() );
 				}
 
-				editor.dom.setAttribs( link, attrs );
-			} else {
-				if ( text ) {
-					editor.selection.setNode( editor.dom.create( 'a', attrs, text ) );
+				if ( ! $link.length ) {
+					editor.execCommand( 'unlink' );
 				} else {
-					editor.execCommand( 'mceInsertLink', false, attrs );
+					if ( inputs.wrap.hasClass( 'has-text-field' ) ) {
+						text = inputs.text.val();
+
+						if ( text ) {
+							$link.text( text );
+						} else if ( ! hasText ) {
+							$link.text( attrs.href );
+						}
+					}
+
+					attrs['data-wplink-edit'] = null;
+					attrs['data-mce-href'] = null; // attrs.href
+					$link.attr( attrs );
+				}
+			} );
+
+			wpLink.close( 'noReset' );
+			editor.focus();
+
+			if ( $link.length ) {
+				$mceCaret = $link.parent( '#_mce_caret' );
+
+				if ( $mceCaret.length ) {
+					$mceCaret.before( $link.removeAttr( 'data-mce-bogus' ) );
+				}
+
+				editor.selection.select( $link[0] );
+				editor.selection.collapse();
+
+				if ( editor.plugins.wplink ) {
+					editor.plugins.wplink.checkLink( $link[0] );
 				}
 			}
+
+			editor.nodeChanged();
+
+			// Audible confirmation message when a link has been inserted in the Editor.
+			wp.a11y.speak( wpLinkL10n.linkInserted );
 		},
 
 		updateFields: function( e, li ) {
 			inputs.url.val( li.children( '.item-permalink' ).val() );
 		},
 
-		setDefaultValues: function() {
-			var selection,
-				emailRegexp = /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i,
-				urlRegexp = /^(https?|ftp):\/\/[A-Z0-9.-]+\.[A-Z]{2,4}[^ "]*$/i;
+		getUrlFromSelection: function( selection ) {
+			if ( ! selection ) {
+				if ( this.isMCE() ) {
+					selection = editor.selection.getContent({ format: 'text' });
+				} else if ( document.selection && wpLink.range ) {
+					selection = wpLink.range.text;
+				} else if ( typeof this.textarea.selectionStart !== 'undefined' ) {
+					selection = this.textarea.value.substring( this.textarea.selectionStart, this.textarea.selectionEnd );
+				}
+			}
 
-			if ( this.isMCE() ) {
-				selection = editor.selection.getContent();
-			} else if ( document.selection && wpLink.range ) {
-				selection = wpLink.range.text;
-			} else if ( typeof this.textarea.selectionStart !== 'undefined' ) {
-				selection = this.textarea.value.substring( this.textarea.selectionStart, this.textarea.selectionEnd );
-			}
+			selection = $.trim( selection );
 
 			if ( selection && emailRegexp.test( selection ) ) {
 				// Selection is email address
-				inputs.url.val( 'mailto:' + selection );
+				return 'mailto:' + selection;
 			} else if ( selection && urlRegexp.test( selection ) ) {
 				// Selection is URL
-				inputs.url.val( selection.replace( /&amp;|&#0?38;/gi, '&' ) );
-			} else {
-				// Set URL to default.
-				inputs.url.val( '' );
+				return selection.replace( /&amp;|&#0?38;/gi, '&' );
 			}
 
+			return '';
+		},
+
+		setDefaultValues: function( selection ) {
+			inputs.url.val( this.getUrlFromSelection( selection ) );
+
+			// Empty the search field and swap the "rivers".
+			inputs.search.val('');
+			wpLink.searchInternalLinks();
+
 			// Update save prompt.
 			inputs.submit.val( wpLinkL10n.save );
 		},
 
 		searchInternalLinks: function() {
-			var t = $( this ), waiting,
-				search = t.val();
+			var waiting,
+				search = inputs.search.val() || '';
 
 			if ( search.length > 2 ) {
 				rivers.recent.hide();
@@ -420,7 +514,7 @@
 					return;
 
 				wpLink.lastSearch = search;
-				waiting = t.parent().find( '.spinner' ).addClass( 'is-active' );
+				waiting = inputs.search.parent().find( '.spinner' ).addClass( 'is-active' );
 
 				rivers.search.change( search );
 				rivers.search.ajax( function() {
@@ -443,13 +537,14 @@
 		},
 
 		keydown: function( event ) {
-			var fn, id,
-				key = $.ui.keyCode;
+			var fn, id;
 
-			if ( key.ESCAPE === event.keyCode ) {
+			// Escape key.
+			if ( 27 === event.keyCode ) {
 				wpLink.close();
 				event.stopImmediatePropagation();
-			} else if ( key.TAB === event.keyCode ) {
+			// Tab key.
+			} else if ( 9 === event.keyCode ) {
 				id = event.target.id;
 
 				// wp-link-submit must always be the last focusable element in the dialog.
@@ -463,7 +558,8 @@
 				}
 			}
 
-			if ( event.keyCode !== key.UP && event.keyCode !== key.DOWN ) {
+			// Up Arrow and Down Arrow keys.
+			if ( 38 !== event.keyCode && 40 !== event.keyCode ) {
 				return;
 			}
 
@@ -472,7 +568,8 @@
 				return;
 			}
 
-			fn = event.keyCode === key.UP ? 'prev' : 'next';
+			// Up Arrow key.
+			fn = 38 === event.keyCode ? 'prev' : 'next';
 			clearInterval( wpLink.keyInterval );
 			wpLink[ fn ]();
 			wpLink.keyInterval = setInterval( wpLink[ fn ], wpLink.keySensitivity );
@@ -480,9 +577,8 @@
 		},
 
 		keyup: function( event ) {
-			var key = $.ui.keyCode;
-
-			if ( event.which === key.UP || event.which === key.DOWN ) {
+			// Up Arrow and Down Arrow keys.
+			if ( 38 === event.keyCode || 40 === event.keyCode ) {
 				clearInterval( wpLink.keyInterval );
 				event.preventDefault();
 			}
@@ -509,15 +605,6 @@
 				funcContext = this;
 				funcTriggered = true;
 			};
-		},
-
-		toggleInternalLinking: function( event ) {
-			var visible = inputs.wrap.hasClass( 'search-panel-visible' );
-
-			inputs.wrap.toggleClass( 'search-panel-visible', ! visible );
-			setUserSetting( 'wplink', visible ? '0' : '1' );
-			inputs[ ! visible ? 'search' : 'url' ].focus();
-			event.preventDefault();
 		}
 	};
 
@@ -694,7 +781,7 @@
 
 			this.querying = true;
 
-			$.post( ajaxurl, query, function( r ) {
+			$.post( window.ajaxurl, query, function( r ) {
 				self.page++;
 				self.querying = false;
 				self.allLoaded = ! r;
@@ -704,4 +791,4 @@
 	});
 
 	$( document ).ready( wpLink.init );
-})( jQuery );
+})( jQuery, window.wpLinkL10n, window.wp );