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>' + |
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], |