wp/wp-includes/js/mce-view.js
changeset 16 a86126ab1dd4
parent 9 177826044cd9
child 21 48c4eec2b7e6
equal deleted inserted replaced
15:3d4e9c994f10 16:a86126ab1dd4
    44 	wp.mce.views = {
    44 	wp.mce.views = {
    45 
    45 
    46 		/**
    46 		/**
    47 		 * Registers a new view type.
    47 		 * Registers a new view type.
    48 		 *
    48 		 *
    49 		 * @param {String} type   The view type.
    49 		 * @param {string} type   The view type.
    50 		 * @param {Object} extend An object to extend wp.mce.View.prototype with.
    50 		 * @param {Object} extend An object to extend wp.mce.View.prototype with.
    51 		 */
    51 		 */
    52 		register: function( type, extend ) {
    52 		register: function( type, extend ) {
    53 			views[ type ] = wp.mce.View.extend( _.extend( extend, { type: type } ) );
    53 			views[ type ] = wp.mce.View.extend( _.extend( extend, { type: type } ) );
    54 		},
    54 		},
    55 
    55 
    56 		/**
    56 		/**
    57 		 * Unregisters a view type.
    57 		 * Unregisters a view type.
    58 		 *
    58 		 *
    59 		 * @param {String} type The view type.
    59 		 * @param {string} type The view type.
    60 		 */
    60 		 */
    61 		unregister: function( type ) {
    61 		unregister: function( type ) {
    62 			delete views[ type ];
    62 			delete views[ type ];
    63 		},
    63 		},
    64 
    64 
    65 		/**
    65 		/**
    66 		 * Returns the settings of a view type.
    66 		 * Returns the settings of a view type.
    67 		 *
    67 		 *
    68 		 * @param {String} type The view type.
    68 		 * @param {string} type The view type.
    69 		 *
    69 		 *
    70 		 * @return {Function} The view constructor.
    70 		 * @return {Function} The view constructor.
    71 		 */
    71 		 */
    72 		get: function( type ) {
    72 		get: function( type ) {
    73 			return views[ type ];
    73 			return views[ type ];
    86 		/**
    86 		/**
    87 		 * Scans a given string for each view's pattern,
    87 		 * Scans a given string for each view's pattern,
    88 		 * replacing any matches with markers,
    88 		 * replacing any matches with markers,
    89 		 * and creates a new instance for every match.
    89 		 * and creates a new instance for every match.
    90 		 *
    90 		 *
    91 		 * @param {String} content The string to scan.
    91 		 * @param {string} content The string to scan.
    92 		 * @param {tinymce.Editor} editor The editor.
    92 		 * @param {tinymce.Editor} editor The editor.
    93 		 *
    93 		 *
    94 		 * @return {String} The string with markers.
    94 		 * @return {string} The string with markers.
    95 		 */
    95 		 */
    96 		setMarkers: function( content, editor ) {
    96 		setMarkers: function( content, editor ) {
    97 			var pieces = [ { content: content } ],
    97 			var pieces = [ { content: content } ],
    98 				self = this,
    98 				self = this,
    99 				instance, current;
    99 				instance, current;
   147 		},
   147 		},
   148 
   148 
   149 		/**
   149 		/**
   150 		 * Create a view instance.
   150 		 * Create a view instance.
   151 		 *
   151 		 *
   152 		 * @param {String}  type    The view type.
   152 		 * @param {string}  type    The view type.
   153 		 * @param {String}  text    The textual representation of the view.
   153 		 * @param {string}  text    The textual representation of the view.
   154 		 * @param {Object}  options Options.
   154 		 * @param {Object}  options Options.
   155 		 * @param {Boolean} force   Recreate the instance. Optional.
   155 		 * @param {boolean} force   Recreate the instance. Optional.
   156 		 *
   156 		 *
   157 		 * @return {wp.mce.View} The view instance.
   157 		 * @return {wp.mce.View} The view instance.
   158 		 */
   158 		 */
   159 		createInstance: function( type, text, options, force ) {
   159 		createInstance: function( type, text, options, force ) {
   160 			var View = this.get( type ),
   160 			var View = this.get( type ),
   188 		},
   188 		},
   189 
   189 
   190 		/**
   190 		/**
   191 		 * Get a view instance.
   191 		 * Get a view instance.
   192 		 *
   192 		 *
   193 		 * @param {(String|HTMLElement)} object The textual representation of the view or the view node.
   193 		 * @param {(string|HTMLElement)} object The textual representation of the view or the view node.
   194 		 *
   194 		 *
   195 		 * @return {wp.mce.View} The view instance or undefined.
   195 		 * @return {wp.mce.View} The view instance or undefined.
   196 		 */
   196 		 */
   197 		getInstance: function( object ) {
   197 		getInstance: function( object ) {
   198 			if ( typeof object === 'string' ) {
   198 			if ( typeof object === 'string' ) {
   205 		/**
   205 		/**
   206 		 * Given a view node, get the view's text.
   206 		 * Given a view node, get the view's text.
   207 		 *
   207 		 *
   208 		 * @param {HTMLElement} node The view node.
   208 		 * @param {HTMLElement} node The view node.
   209 		 *
   209 		 *
   210 		 * @return {String} The textual representation of the view.
   210 		 * @return {string} The textual representation of the view.
   211 		 */
   211 		 */
   212 		getText: function( node ) {
   212 		getText: function( node ) {
   213 			return decodeURIComponent( $( node ).attr( 'data-wpview-text' ) || '' );
   213 			return decodeURIComponent( $( node ).attr( 'data-wpview-text' ) || '' );
   214 		},
   214 		},
   215 
   215 
   216 		/**
   216 		/**
   217 		 * Renders all view nodes that are not yet rendered.
   217 		 * Renders all view nodes that are not yet rendered.
   218 		 *
   218 		 *
   219 		 * @param {Boolean} force Rerender all view nodes.
   219 		 * @param {boolean} force Rerender all view nodes.
   220 		 */
   220 		 */
   221 		render: function( force ) {
   221 		render: function( force ) {
   222 			_.each( instances, function( instance ) {
   222 			_.each( instances, function( instance ) {
   223 				instance.render( null, force );
   223 				instance.render( null, force );
   224 			} );
   224 			} );
   225 		},
   225 		},
   226 
   226 
   227 		/**
   227 		/**
   228 		 * Update the text of a given view node.
   228 		 * Update the text of a given view node.
   229 		 *
   229 		 *
   230 		 * @param {String}         text   The new text.
   230 		 * @param {string}         text   The new text.
   231 		 * @param {tinymce.Editor} editor The TinyMCE editor instance the view node is in.
   231 		 * @param {tinymce.Editor} editor The TinyMCE editor instance the view node is in.
   232 		 * @param {HTMLElement}    node   The view node to update.
   232 		 * @param {HTMLElement}    node   The view node to update.
   233 		 * @param {Boolean}        force  Recreate the instance. Optional.
   233 		 * @param {boolean}        force  Recreate the instance. Optional.
   234 		 */
   234 		 */
   235 		update: function( text, editor, node, force ) {
   235 		update: function( text, editor, node, force ) {
   236 			var instance = this.getInstance( node );
   236 			var instance = this.getInstance( node );
   237 
   237 
   238 			if ( instance ) {
   238 			if ( instance ) {
   315 		},
   315 		},
   316 
   316 
   317 		/**
   317 		/**
   318 		 * Renders all view nodes tied to this view instance that are not yet rendered.
   318 		 * Renders all view nodes tied to this view instance that are not yet rendered.
   319 		 *
   319 		 *
   320 		 * @param {String}  content The content to render. Optional.
   320 		 * @param {string}  content The content to render. Optional.
   321 		 * @param {Boolean} force   Rerender all view nodes tied to this view instance. Optional.
   321 		 * @param {boolean} force   Rerender all view nodes tied to this view instance. Optional.
   322 		 */
   322 		 */
   323 		render: function( content, force ) {
   323 		render: function( content, force ) {
   324 			if ( content != null ) {
   324 			if ( content != null ) {
   325 				this.content = content;
   325 				this.content = content;
   326 			}
   326 			}
   383 
   383 
   384 		/**
   384 		/**
   385 		 * Gets all view nodes tied to this view instance.
   385 		 * Gets all view nodes tied to this view instance.
   386 		 *
   386 		 *
   387 		 * @param {Function} callback A callback.
   387 		 * @param {Function} callback A callback.
   388 		 * @param {Boolean}  rendered Get (un)rendered view nodes. Optional.
   388 		 * @param {boolean}  rendered Get (un)rendered view nodes. Optional.
   389 		 */
   389 		 */
   390 		getNodes: function( callback, rendered ) {
   390 		getNodes: function( callback, rendered ) {
   391 			this.getEditors( function( editor ) {
   391 			this.getEditors( function( editor ) {
   392 				var self = this;
   392 				var self = this;
   393 
   393 
   442 
   442 
   443 				$viewNode = editor.$(
   443 				$viewNode = editor.$(
   444 					'<div class="wpview wpview-wrap" data-wpview-text="' + this.encodedText + '" data-wpview-type="' + this.type + '" contenteditable="false"></div>'
   444 					'<div class="wpview wpview-wrap" data-wpview-text="' + this.encodedText + '" data-wpview-type="' + this.type + '" contenteditable="false"></div>'
   445 				);
   445 				);
   446 
   446 
   447 				editor.$( node ).replaceWith( $viewNode );
   447 				editor.undoManager.ignore( function() {
       
   448 					editor.$( node ).replaceWith( $viewNode );
       
   449 				} );
   448 
   450 
   449 				if ( selected ) {
   451 				if ( selected ) {
   450 					setTimeout( function() {
   452 					setTimeout( function() {
   451 						editor.selection.select( $viewNode[0] );
   453 						editor.undoManager.ignore( function() {
   452 						editor.selection.collapse();
   454 							editor.selection.select( $viewNode[0] );
       
   455 							editor.selection.collapse();
       
   456 						} );
   453 					} );
   457 					} );
   454 				}
   458 				}
   455 			} );
   459 			} );
   456 		},
   460 		},
   457 
   461 
   467 		/**
   471 		/**
   468 		 * Sets the content for all view nodes tied to this view instance.
   472 		 * Sets the content for all view nodes tied to this view instance.
   469 		 *
   473 		 *
   470 		 * @param {*}        content  The content to set.
   474 		 * @param {*}        content  The content to set.
   471 		 * @param {Function} callback A callback. Optional.
   475 		 * @param {Function} callback A callback. Optional.
   472 		 * @param {Boolean}  rendered Only set for (un)rendered nodes. Optional.
   476 		 * @param {boolean}  rendered Only set for (un)rendered nodes. Optional.
   473 		 */
   477 		 */
   474 		setContent: function( content, callback, rendered ) {
   478 		setContent: function( content, callback, rendered ) {
   475 			if ( _.isObject( content ) && ( content.sandbox || content.head || content.body.indexOf( '<script' ) !== -1 ) ) {
   479 			if ( _.isObject( content ) && ( content.sandbox || content.head || content.body.indexOf( '<script' ) !== -1 ) ) {
   476 				this.setIframes( content.head || '', content.body, callback, rendered );
   480 				this.setIframes( content.head || '', content.body, callback, rendered );
   477 			} else if ( _.isString( content ) && content.indexOf( '<script' ) !== -1 ) {
   481 			} else if ( _.isString( content ) && content.indexOf( '<script' ) !== -1 ) {
   496 		},
   500 		},
   497 
   501 
   498 		/**
   502 		/**
   499 		 * Sets the content in an iframe for all view nodes tied to this view instance.
   503 		 * Sets the content in an iframe for all view nodes tied to this view instance.
   500 		 *
   504 		 *
   501 		 * @param {String}   head     HTML string to be added to the head of the document.
   505 		 * @param {string}   head     HTML string to be added to the head of the document.
   502 		 * @param {String}   body     HTML string to be added to the body of the document.
   506 		 * @param {string}   body     HTML string to be added to the body of the document.
   503 		 * @param {Function} callback A callback. Optional.
   507 		 * @param {Function} callback A callback. Optional.
   504 		 * @param {Boolean}  rendered Only set for (un)rendered nodes. Optional.
   508 		 * @param {boolean}  rendered Only set for (un)rendered nodes. Optional.
   505 		 */
   509 		 */
   506 		setIframes: function( head, body, callback, rendered ) {
   510 		setIframes: function( head, body, callback, rendered ) {
   507 			var self = this;
   511 			var self = this;
   508 
   512 
   509 			if ( body.indexOf( '[' ) !== -1 && body.indexOf( ']' ) !== -1 ) {
   513 			if ( body.indexOf( '[' ) !== -1 && body.indexOf( ']' ) !== -1 ) {
   559 
   563 
   560 					dom.add( node, 'span', { 'class': 'mce-shim' } );
   564 					dom.add( node, 'span', { 'class': 'mce-shim' } );
   561 					dom.add( node, 'span', { 'class': 'wpview-end' } );
   565 					dom.add( node, 'span', { 'class': 'wpview-end' } );
   562 				} );
   566 				} );
   563 
   567 
   564 				// Bail if the iframe node is not attached to the DOM.
   568 				/*
   565 				// Happens when the view is dragged in the editor.
   569 				 * Bail if the iframe node is not attached to the DOM.
   566 				// There is a browser restriction when iframes are moved in the DOM. They get emptied.
   570 				 * Happens when the view is dragged in the editor.
   567 				// The iframe will be rerendered after dropping the view node at the new location.
   571 				 * There is a browser restriction when iframes are moved in the DOM. They get emptied.
       
   572 				 * The iframe will be rerendered after dropping the view node at the new location.
       
   573 				 */
   568 				if ( ! iframe.contentWindow ) {
   574 				if ( ! iframe.contentWindow ) {
   569 					return;
   575 					return;
   570 				}
   576 				}
   571 
   577 
   572 				iframeWin = iframe.contentWindow;
   578 				iframeWin = iframe.contentWindow;
   690 		},
   696 		},
   691 
   697 
   692 		/**
   698 		/**
   693 		 * Sets an error for all view nodes tied to this view instance.
   699 		 * Sets an error for all view nodes tied to this view instance.
   694 		 *
   700 		 *
   695 		 * @param {String} message  The error message to set.
   701 		 * @param {string} message  The error message to set.
   696 		 * @param {String} dashicon A dashicon ID. Optional. {@link https://developer.wordpress.org/resource/dashicons/}
   702 		 * @param {string} dashicon A dashicon ID. Optional. {@link https://developer.wordpress.org/resource/dashicons/}
   697 		 */
   703 		 */
   698 		setError: function( message, dashicon ) {
   704 		setError: function( message, dashicon ) {
   699 			this.setContent(
   705 			this.setContent(
   700 				'<div class="wpview-error">' +
   706 				'<div class="wpview-error">' +
   701 					'<div class="dashicons dashicons-' + ( dashicon || 'no' ) + '"></div>' +
   707 					'<div class="dashicons dashicons-' + ( dashicon || 'no' ) + '"></div>' +
   705 		},
   711 		},
   706 
   712 
   707 		/**
   713 		/**
   708 		 * Tries to find a text match in a given string.
   714 		 * Tries to find a text match in a given string.
   709 		 *
   715 		 *
   710 		 * @param {String} content The string to scan.
   716 		 * @param {string} content The string to scan.
   711 		 *
   717 		 *
   712 		 * @return {Object}
   718 		 * @return {Object}
   713 		 */
   719 		 */
   714 		match: function( content ) {
   720 		match: function( content ) {
   715 			var match = shortcode.next( this.type, content );
   721 			var match = shortcode.next( this.type, content );
   726 		},
   732 		},
   727 
   733 
   728 		/**
   734 		/**
   729 		 * Update the text of a given view node.
   735 		 * Update the text of a given view node.
   730 		 *
   736 		 *
   731 		 * @param {String}         text   The new text.
   737 		 * @param {string}         text   The new text.
   732 		 * @param {tinymce.Editor} editor The TinyMCE editor instance the view node is in.
   738 		 * @param {tinymce.Editor} editor The TinyMCE editor instance the view node is in.
   733 		 * @param {HTMLElement}    node   The view node to update.
   739 		 * @param {HTMLElement}    node   The view node to update.
   734 		 * @param {Boolean}        force  Recreate the instance. Optional.
   740 		 * @param {boolean}        force  Recreate the instance. Optional.
   735 		 */
   741 		 */
   736 		update: function( text, editor, node, force ) {
   742 		update: function( text, editor, node, force ) {
   737 			_.find( views, function( view, type ) {
   743 			_.find( views, function( view, type ) {
   738 				var match = view.prototype.match( text );
   744 				var match = view.prototype.match( text );
   739 
   745 
   959 
   965 
   960 	views.register( 'embed', _.extend( {}, embed ) );
   966 	views.register( 'embed', _.extend( {}, embed ) );
   961 
   967 
   962 	views.register( 'embedURL', _.extend( {}, embed, {
   968 	views.register( 'embedURL', _.extend( {}, embed, {
   963 		match: function( content ) {
   969 		match: function( content ) {
   964 			var re = /(^|<p>)(https?:\/\/[^\s"]+?)(<\/p>\s*|$)/gi,
   970 			// There may be a "bookmark" node next to the URL...
   965 				match = re.exec( content );
   971 			var re = /(^|<p>(?:<span data-mce-type="bookmark"[^>]+>\s*<\/span>)?)(https?:\/\/[^\s"]+?)((?:<span data-mce-type="bookmark"[^>]+>\s*<\/span>)?<\/p>\s*|$)/gi;
       
   972 			var match = re.exec( content );
   966 
   973 
   967 			if ( match ) {
   974 			if ( match ) {
   968 				return {
   975 				return {
   969 					index: match.index + match[1].length,
   976 					index: match.index + match[1].length,
   970 					content: match[2],
   977 					content: match[2],