wp/wp-admin/js/editor.js
changeset 16 a86126ab1dd4
parent 9 177826044cd9
child 18 be944660c56a
equal deleted inserted replaced
15:3d4e9c994f10 16:a86126ab1dd4
    24 				/**
    24 				/**
    25 				 * Handles onclick events for the Visual/Text tabs.
    25 				 * Handles onclick events for the Visual/Text tabs.
    26 				 *
    26 				 *
    27 				 * @since 4.3.0
    27 				 * @since 4.3.0
    28 				 *
    28 				 *
    29 				 * @returns {void}
    29 				 * @return {void}
    30 				 */
    30 				 */
    31 				$$( document ).on( 'click', function( event ) {
    31 				$$( document ).on( 'click', function( event ) {
    32 					var id, mode,
    32 					var id, mode,
    33 						target = $$( event.target );
    33 						target = $$( event.target );
    34 
    34 
    45 		 * Returns the height of the editor toolbar(s) in px.
    45 		 * Returns the height of the editor toolbar(s) in px.
    46 		 *
    46 		 *
    47 		 * @since 3.9.0
    47 		 * @since 3.9.0
    48 		 *
    48 		 *
    49 		 * @param {Object} editor The TinyMCE editor.
    49 		 * @param {Object} editor The TinyMCE editor.
    50 		 * @returns {number} If the height is between 10 and 200 return the height,
    50 		 * @return {number} If the height is between 10 and 200 return the height,
    51 		 * else return 30.
    51 		 * else return 30.
    52 		 */
    52 		 */
    53 		function getToolbarHeight( editor ) {
    53 		function getToolbarHeight( editor ) {
    54 			var node = $$( '.mce-toolbar-grp', editor.getContainer() )[0],
    54 			var node = $$( '.mce-toolbar-grp', editor.getContainer() )[0],
    55 				height = node && node.clientHeight;
    55 				height = node && node.clientHeight;
    68 		 *
    68 		 *
    69 		 * @memberof switchEditors
    69 		 * @memberof switchEditors
    70 		 *
    70 		 *
    71 		 * @param {string} id The id of the editor you want to change the editor mode for. Default: `content`.
    71 		 * @param {string} id The id of the editor you want to change the editor mode for. Default: `content`.
    72 		 * @param {string} mode The mode you want to switch to. Default: `toggle`.
    72 		 * @param {string} mode The mode you want to switch to. Default: `toggle`.
    73 		 * @returns {void}
    73 		 * @return {void}
    74 		 */
    74 		 */
    75 		function switchEditor( id, mode ) {
    75 		function switchEditor( id, mode ) {
    76 			id = id || 'content';
    76 			id = id || 'content';
    77 			mode = mode || 'toggle';
    77 			mode = mode || 'toggle';
    78 
    78 
   110 					keepSelection = window.tinyMCEPreInit.mceInit[ id ] &&
   110 					keepSelection = window.tinyMCEPreInit.mceInit[ id ] &&
   111 									window.tinyMCEPreInit.mceInit[ id ].wp_keep_scroll_position;
   111 									window.tinyMCEPreInit.mceInit[ id ].wp_keep_scroll_position;
   112 				}
   112 				}
   113 
   113 
   114 				if ( keepSelection ) {
   114 				if ( keepSelection ) {
   115 					// Save the selection
   115 					// Save the selection.
   116 					addHTMLBookmarkInTextAreaContent( $textarea );
   116 					addHTMLBookmarkInTextAreaContent( $textarea );
   117 				}
   117 				}
   118 
   118 
   119 				if ( editor ) {
   119 				if ( editor ) {
   120 					editor.show();
   120 					editor.show();
   129 							editor.theme.resizeTo( null, editorHeight );
   129 							editor.theme.resizeTo( null, editorHeight );
   130 						}
   130 						}
   131 					}
   131 					}
   132 
   132 
   133 					if ( editor.getParam( 'wp_keep_scroll_position' ) ) {
   133 					if ( editor.getParam( 'wp_keep_scroll_position' ) ) {
   134 						// Restore the selection
   134 						// Restore the selection.
   135 						focusHTMLBookmarkInVisualEditor( editor );
   135 						focusHTMLBookmarkInVisualEditor( editor );
   136 					}
   136 					}
   137 				} else {
   137 				} else {
   138 					tinymce.init( window.tinyMCEPreInit.mceInit[ id ] );
   138 					tinymce.init( window.tinyMCEPreInit.mceInit[ id ] );
   139 				}
   139 				}
   147 				if ( editor && editor.isHidden() ) {
   147 				if ( editor && editor.isHidden() ) {
   148 					return false;
   148 					return false;
   149 				}
   149 				}
   150 
   150 
   151 				if ( editor ) {
   151 				if ( editor ) {
   152 					// Don't resize the textarea in iOS. The iframe is forced to 100% height there, we shouldn't match it.
   152 					// Don't resize the textarea in iOS.
       
   153 					// The iframe is forced to 100% height there, we shouldn't match it.
   153 					if ( ! tinymce.Env.iOS ) {
   154 					if ( ! tinymce.Env.iOS ) {
   154 						iframe = editor.iframeElement;
   155 						iframe = editor.iframeElement;
   155 						editorHeight = iframe ? parseInt( iframe.style.height, 10 ) : 0;
   156 						editorHeight = iframe ? parseInt( iframe.style.height, 10 ) : 0;
   156 
   157 
   157 						if ( editorHeight ) {
   158 						if ( editorHeight ) {
   175 
   176 
   176 					if ( selectionRange ) {
   177 					if ( selectionRange ) {
   177 						selectTextInTextArea( editor, selectionRange );
   178 						selectTextInTextArea( editor, selectionRange );
   178 					}
   179 					}
   179 				} else {
   180 				} else {
   180 					// There is probably a JS error on the page. The TinyMCE editor instance doesn't exist. Show the textarea.
   181 					// There is probably a JS error on the page.
       
   182 					// The TinyMCE editor instance doesn't exist. Show the textarea.
   181 					$textarea.css({ 'display': '', 'visibility': '' });
   183 					$textarea.css({ 'display': '', 'visibility': '' });
   182 				}
   184 				}
   183 
   185 
   184 				wrap.removeClass( 'tmce-active' ).addClass( 'html-active' );
   186 				wrap.removeClass( 'tmce-active' ).addClass( 'html-active' );
   185 				$textarea.attr( 'aria-hidden', false );
   187 				$textarea.attr( 'aria-hidden', false );
   200 		 * e.g. `[caption]<img.../>..`.
   202 		 * e.g. `[caption]<img.../>..`.
   201 		 *
   203 		 *
   202 		 * @param {string} content The test content where the cursor is.
   204 		 * @param {string} content The test content where the cursor is.
   203 		 * @param {number} cursorPosition The cursor position inside the content.
   205 		 * @param {number} cursorPosition The cursor position inside the content.
   204 		 *
   206 		 *
   205 		 * @returns {(null|Object)} Null if cursor is not in a tag, Object if the cursor is inside a tag.
   207 		 * @return {(null|Object)} Null if cursor is not in a tag, Object if the cursor is inside a tag.
   206 		 */
   208 		 */
   207 		function getContainingTagInfo( content, cursorPosition ) {
   209 		function getContainingTagInfo( content, cursorPosition ) {
   208 			var lastLtPos = content.lastIndexOf( '<', cursorPosition - 1 ),
   210 			var lastLtPos = content.lastIndexOf( '<', cursorPosition - 1 ),
   209 				lastGtPos = content.lastIndexOf( '>', cursorPosition );
   211 				lastGtPos = content.lastIndexOf( '>', cursorPosition );
   210 
   212 
   211 			if ( lastLtPos > lastGtPos || content.substr( cursorPosition, 1 ) === '>' ) {
   213 			if ( lastLtPos > lastGtPos || content.substr( cursorPosition, 1 ) === '>' ) {
   212 				// find what the tag is
   214 				// Find what the tag is.
   213 				var tagContent = content.substr( lastLtPos ),
   215 				var tagContent = content.substr( lastLtPos ),
   214 					tagMatch = tagContent.match( /<\s*(\/)?(\w+|\!-{2}.*-{2})/ );
   216 					tagMatch = tagContent.match( /<\s*(\/)?(\w+|\!-{2}.*-{2})/ );
   215 
   217 
   216 				if ( ! tagMatch ) {
   218 				if ( ! tagMatch ) {
   217 					return null;
   219 					return null;
   220 				var tagType = tagMatch[2],
   222 				var tagType = tagMatch[2],
   221 					closingGt = tagContent.indexOf( '>' );
   223 					closingGt = tagContent.indexOf( '>' );
   222 
   224 
   223 				return {
   225 				return {
   224 					ltPos: lastLtPos,
   226 					ltPos: lastLtPos,
   225 					gtPos: lastLtPos + closingGt + 1, // offset by one to get the position _after_ the character,
   227 					gtPos: lastLtPos + closingGt + 1, // Offset by one to get the position _after_ the character.
   226 					tagType: tagType,
   228 					tagType: tagType,
   227 					isClosingTag: !! tagMatch[1]
   229 					isClosingTag: !! tagMatch[1]
   228 				};
   230 				};
   229 			}
   231 			}
   230 			return null;
   232 			return null;
   246 		 *
   248 		 *
   247 		 * @param {string} content The text content to check against.
   249 		 * @param {string} content The text content to check against.
   248 		 * @param {number} cursorPosition    The cursor position to check.
   250 		 * @param {number} cursorPosition    The cursor position to check.
   249 		 *
   251 		 *
   250 		 * @return {(undefined|Object)} Undefined if the cursor is not wrapped in a shortcode tag.
   252 		 * @return {(undefined|Object)} Undefined if the cursor is not wrapped in a shortcode tag.
   251 		 *                                Information about the wrapping shortcode tag if it's wrapped in one.
   253 		 *                              Information about the wrapping shortcode tag if it's wrapped in one.
   252 		 */
   254 		 */
   253 		function getShortcodeWrapperInfo( content, cursorPosition ) {
   255 		function getShortcodeWrapperInfo( content, cursorPosition ) {
   254 			var contentShortcodes = getShortCodePositionsInText( content );
   256 			var contentShortcodes = getShortCodePositionsInText( content );
   255 
   257 
   256 			for ( var i = 0; i < contentShortcodes.length; i++ ) {
   258 			for ( var i = 0; i < contentShortcodes.length; i++ ) {
   401 				'keygen', 'link', 'meta', 'param', 'source', 'track', 'wbr'
   403 				'keygen', 'link', 'meta', 'param', 'source', 'track', 'wbr'
   402 			];
   404 			];
   403 
   405 
   404 			var cursorStart = cursorPositions.cursorStart,
   406 			var cursorStart = cursorPositions.cursorStart,
   405 				cursorEnd = cursorPositions.cursorEnd,
   407 				cursorEnd = cursorPositions.cursorEnd,
   406 				// check if the cursor is in a tag and if so, adjust it
   408 				// Check if the cursor is in a tag and if so, adjust it.
   407 				isCursorStartInTag = getContainingTagInfo( content, cursorStart );
   409 				isCursorStartInTag = getContainingTagInfo( content, cursorStart );
   408 
   410 
   409 			if ( isCursorStartInTag ) {
   411 			if ( isCursorStartInTag ) {
   410 				/**
   412 				/**
   411 				 * Only move to the start of the HTML tag (to select the whole element) if the tag
   413 				 * Only move to the start of the HTML tag (to select the whole element) if the tag
   469 		 *
   471 		 *
   470 		 * Adds selection markers in the content of the editor `textarea`.
   472 		 * Adds selection markers in the content of the editor `textarea`.
   471 		 * The method directly manipulates the `textarea` content, to allow TinyMCE plugins
   473 		 * The method directly manipulates the `textarea` content, to allow TinyMCE plugins
   472 		 * to run after the markers are added.
   474 		 * to run after the markers are added.
   473 		 *
   475 		 *
   474 		 * @param {object} $textarea TinyMCE's textarea wrapped as a DomQuery object
   476 		 * @param {Object} $textarea TinyMCE's textarea wrapped as a DomQuery object
   475 		 */
   477 		 */
   476 		function addHTMLBookmarkInTextAreaContent( $textarea ) {
   478 		function addHTMLBookmarkInTextAreaContent( $textarea ) {
   477 			if ( ! $textarea || ! $textarea.length ) {
   479 			if ( ! $textarea || ! $textarea.length ) {
   478 				// If no valid $textarea object is provided, there's nothing we can do.
   480 				// If no valid $textarea object is provided, there's nothing we can do.
   479 				return;
   481 				return;
   504 					bookMarkEnd[0].outerHTML
   506 					bookMarkEnd[0].outerHTML
   505 				].join( '' );
   507 				].join( '' );
   506 			}
   508 			}
   507 
   509 
   508 			textArea.value = [
   510 			textArea.value = [
   509 				textArea.value.slice( 0, htmlModeCursorStartPosition ), // text until the cursor/selection position
   511 				textArea.value.slice( 0, htmlModeCursorStartPosition ), // Text until the cursor/selection position.
   510 				cursorMarkerSkeleton.clone()							// cursor/selection start marker
   512 				cursorMarkerSkeleton.clone()							// Cursor/selection start marker.
   511 					.addClass( 'mce_SELRES_start' )[0].outerHTML,
   513 					.addClass( 'mce_SELRES_start' )[0].outerHTML,
   512 				selectedText, 											// selected text with end cursor/position marker
   514 				selectedText, 											// Selected text with end cursor/position marker.
   513 				textArea.value.slice( htmlModeCursorEndPosition )		// text from last cursor/selection position to end
   515 				textArea.value.slice( htmlModeCursorEndPosition )		// Text from last cursor/selection position to end.
   514 			].join( '' );
   516 			].join( '' );
   515 		}
   517 		}
   516 
   518 
   517 		/**
   519 		/**
   518 		 * Focuses the selection markers in Visual mode.
   520 		 * Focuses the selection markers in Visual mode.
   557 		 * Removes selection marker and the parent node if it is an empty paragraph.
   559 		 * Removes selection marker and the parent node if it is an empty paragraph.
   558 		 *
   560 		 *
   559 		 * By default TinyMCE wraps loose inline tags in a `<p>`.
   561 		 * By default TinyMCE wraps loose inline tags in a `<p>`.
   560 		 * When removing selection markers an empty `<p>` may be left behind, remove it.
   562 		 * When removing selection markers an empty `<p>` may be left behind, remove it.
   561 		 *
   563 		 *
   562 		 * @param {object} $marker The marker to be removed from the editor DOM, wrapped in an instnce of `editor.$`
   564 		 * @param {Object} $marker The marker to be removed from the editor DOM, wrapped in an instnce of `editor.$`
   563 		 */
   565 		 */
   564 		function removeSelectionMarker( $marker ) {
   566 		function removeSelectionMarker( $marker ) {
   565 			var $markerParent = $marker.parent();
   567 			var $markerParent = $marker.parent();
   566 
   568 
   567 			$marker.remove();
   569 			$marker.remove();
   656 		 * Uses the standard DOM selection API to achieve that goal.
   658 		 * Uses the standard DOM selection API to achieve that goal.
   657 		 *
   659 		 *
   658 		 * Check the notes in the comments in the code below for more information on some gotchas
   660 		 * Check the notes in the comments in the code below for more information on some gotchas
   659 		 * and why this solution was chosen.
   661 		 * and why this solution was chosen.
   660 		 *
   662 		 *
   661 		 * @param {Object} editor The editor where we must find the selection
   663 		 * @param {Object} editor The editor where we must find the selection.
   662 		 * @returns {(null|Object)} The selection range position in the editor
   664 		 * @return {(null|Object)} The selection range position in the editor.
   663 		 */
   665 		 */
   664 		function findBookmarkedPosition( editor ) {
   666 		function findBookmarkedPosition( editor ) {
   665 			// Get the TinyMCE `window` reference, since we need to access the raw selection.
   667 			// Get the TinyMCE `window` reference, since we need to access the raw selection.
   666 			var TinyMCEWindow = editor.getWin(),
   668 			var TinyMCEWindow = editor.getWin(),
   667 				selection = TinyMCEWindow.getSelection();
   669 				selection = TinyMCEWindow.getSelection();
   802 
   804 
   803 				if ( endMatch[0].indexOf( 'data-mce-object-selection' ) !== -1 ) {
   805 				if ( endMatch[0].indexOf( 'data-mce-object-selection' ) !== -1 ) {
   804 					endMatchIndex -= endMatch[1].length;
   806 					endMatchIndex -= endMatch[1].length;
   805 				}
   807 				}
   806 
   808 
   807 				// We need to adjust the end position to discard the length of the range start marker
   809 				// We need to adjust the end position to discard the length of the range start marker.
   808 				endIndex = endMatchIndex - startMatchLength;
   810 				endIndex = endMatchIndex - startMatchLength;
   809 			}
   811 			}
   810 
   812 
   811 			return {
   813 			return {
   812 				start: startIndex,
   814 				start: startIndex,
   824 		 *
   826 		 *
   825 		 * @param {Object} editor TinyMCE's editor instance.
   827 		 * @param {Object} editor TinyMCE's editor instance.
   826 		 * @param {Object} selection Selection data.
   828 		 * @param {Object} selection Selection data.
   827 		 */
   829 		 */
   828 		function selectTextInTextArea( editor, selection ) {
   830 		function selectTextInTextArea( editor, selection ) {
   829 			// only valid in the text area mode and if we have selection
   831 			// Only valid in the text area mode and if we have selection.
   830 			if ( ! selection ) {
   832 			if ( ! selection ) {
   831 				return;
   833 				return;
   832 			}
   834 			}
   833 
   835 
   834 			var textArea = editor.getElement(),
   836 			var textArea = editor.getElement(),
   835 				start = selection.start,
   837 				start = selection.start,
   836 				end = selection.end || selection.start;
   838 				end = selection.end || selection.start;
   837 
   839 
   838 			if ( textArea.focus ) {
   840 			if ( textArea.focus ) {
   839 				// Wait for the Visual editor to be hidden, then focus and scroll to the position
   841 				// Wait for the Visual editor to be hidden, then focus and scroll to the position.
   840 				setTimeout( function() {
   842 				setTimeout( function() {
   841 					textArea.setSelectionRange( start, end );
   843 					textArea.setSelectionRange( start, end );
   842 					if ( textArea.blur ) {
   844 					if ( textArea.blur ) {
   843 						// defocus before focusing
   845 						// Defocus before focusing.
   844 						textArea.blur();
   846 						textArea.blur();
   845 					}
   847 					}
   846 					textArea.focus();
   848 					textArea.focus();
   847 				}, 100 );
   849 				}, 100 );
   848 			}
   850 			}
  1002 		 * @since 2.5.0
  1004 		 * @since 2.5.0
  1003 		 *
  1005 		 *
  1004 		 * @memberof switchEditors
  1006 		 * @memberof switchEditors
  1005 		 *
  1007 		 *
  1006 		 * @param {string} text The text input.
  1008 		 * @param {string} text The text input.
  1007 		 * @returns {string} The formatted text.
  1009 		 * @return {string} The formatted text.
  1008 		 */
  1010 		 */
  1009 		function autop( text ) {
  1011 		function autop( text ) {
  1010 			var preserve_linebreaks = false,
  1012 			var preserve_linebreaks = false,
  1011 				preserve_br = false,
  1013 				preserve_br = false,
  1012 				blocklist = 'table|thead|tfoot|caption|col|colgroup|tbody|tr|td|th|div|dl|dd|dt|ul|ol|li|pre' +
  1014 				blocklist = 'table|thead|tfoot|caption|col|colgroup|tbody|tr|td|th|div|dl|dd|dt|ul|ol|li|pre' +
  1128 		 *
  1130 		 *
  1129 		 * @since 2.9.0
  1131 		 * @since 2.9.0
  1130 		 *
  1132 		 *
  1131 		 * @memberof switchEditors
  1133 		 * @memberof switchEditors
  1132 		 *
  1134 		 *
  1133 		 * @param {String} html The content from the visual editor.
  1135 		 * @param {string} html The content from the visual editor.
  1134 		 * @returns {String} the filtered content.
  1136 		 * @return {string} the filtered content.
  1135 		 */
  1137 		 */
  1136 		function pre_wpautop( html ) {
  1138 		function pre_wpautop( html ) {
  1137 			var obj = { o: exports, data: html, unfiltered: html };
  1139 			var obj = { o: exports, data: html, unfiltered: html };
  1138 
  1140 
  1139 			if ( $ ) {
  1141 			if ( $ ) {
  1154 		 *
  1156 		 *
  1155 		 * @since 2.9.0
  1157 		 * @since 2.9.0
  1156 		 *
  1158 		 *
  1157 		 * @memberof switchEditors
  1159 		 * @memberof switchEditors
  1158 		 *
  1160 		 *
  1159 		 * @param {String} text The content from the text editor.
  1161 		 * @param {string} text The content from the text editor.
  1160 		 * @returns {String} filtered content.
  1162 		 * @return {string} filtered content.
  1161 		 */
  1163 		 */
  1162 		function wpautop( text ) {
  1164 		function wpautop( text ) {
  1163 			var obj = { o: exports, data: text, unfiltered: text };
  1165 			var obj = { o: exports, data: text, unfiltered: text };
  1164 
  1166 
  1165 			if ( $ ) {
  1167 			if ( $ ) {
  1221 	 *
  1223 	 *
  1222 	 * @since 4.8.0
  1224 	 * @since 4.8.0
  1223 	 *
  1225 	 *
  1224 	 * @param {string} id The HTML id of the textarea that is used for the editor.
  1226 	 * @param {string} id The HTML id of the textarea that is used for the editor.
  1225 	 *                    Has to be jQuery compliant. No brackets, special chars, etc.
  1227 	 *                    Has to be jQuery compliant. No brackets, special chars, etc.
  1226 	 * @param {object} settings Example:
  1228 	 * @param {Object} settings Example:
  1227 	 * settings = {
  1229 	 * settings = {
  1228 	 *    // See https://www.tinymce.com/docs/configure/integration-and-setup/.
  1230 	 *    // See https://www.tinymce.com/docs/configure/integration-and-setup/.
  1229 	 *    // Alternatively set to `true` to use the defaults.
  1231 	 *    // Alternatively set to `true` to use the defaults.
  1230 	 *    tinymce: {
  1232 	 *    tinymce: {
  1231 	 *        setup: function( editor ) {
  1233 	 *        setup: function( editor ) {
  1247 			return;
  1249 			return;
  1248 		}
  1250 		}
  1249 
  1251 
  1250 		defaults = wp.editor.getDefaultSettings();
  1252 		defaults = wp.editor.getDefaultSettings();
  1251 
  1253 
  1252 		// Initialize TinyMCE by default
  1254 		// Initialize TinyMCE by default.
  1253 		if ( ! settings ) {
  1255 		if ( ! settings ) {
  1254 			settings = {
  1256 			settings = {
  1255 				tinymce: true
  1257 				tinymce: true
  1256 			};
  1258 			};
  1257 		}
  1259 		}