Ticket #29841: 29841.3.patch
File 29841.3.patch, 25.7 KB (added by , 10 years ago) |
---|
-
src/wp-includes/js/mce-view.js
1 1 /* global tinymce */ 2 /** 2 3 /* 4 * The TinyMCE view API. 5 * 3 6 * Note: this API is "experimental" meaning that it will probably change 4 7 * in the next few releases based on feedback from 3.9.0. 5 8 * If you decide to use it, please follow the development closely. 6 9 */ 7 8 // Ensure the global `wp` object exists.9 window.wp = window.wp || {};10 11 10 ( function( $ ) { 12 11 'use strict'; 13 12 14 13 var views = {}, 15 14 instances = {}, 16 media = wp.media, 17 mediaWindows = [], 18 windowIdx = 0, 19 waitInterval = 50, 20 viewOptions = ['encodedText']; 15 viewOptions = [ 'encodedText' ]; 21 16 22 // Create the `wp.mce` object if necessary.17 window.wp = window.wp || {}; 23 18 wp.mce = wp.mce || {}; 24 19 25 20 /** 26 * wp.mce. View21 * wp.mce.views 27 22 * 23 * A set of utilities that simplifies adding custom UI within a TinyMCE editor. 24 * At its core, it serves as a series of converters, transforming text to a 25 * custom UI, and back again. 26 */ 27 wp.mce.views = { 28 29 /** 30 * Registers a new TinyMCE view. 31 * 32 * @param {String} The view type. 33 * @param {Object} A constructor. 34 */ 35 register: function( type, constructor ) { 36 var defaultConstructor = { 37 type: type, 38 View: {}, 39 toView: function( content ) { 40 var match = wp.shortcode.next( this.type, content ); 41 42 if ( ! match ) { 43 return; 44 } 45 46 return { 47 index: match.index, 48 content: match.content, 49 options: { 50 shortcode: match.shortcode 51 } 52 }; 53 } 54 }; 55 56 constructor = _.defaults( constructor, defaultConstructor ); 57 constructor.View = wp.mce.View.extend( constructor.View ); 58 59 views[ type ] = constructor; 60 }, 61 62 /** 63 * Unregisters a TinyMCE view. 64 * 65 * @param {String} The view type. 66 */ 67 unregister: function( type ) { 68 delete views[ type ]; 69 }, 70 71 /** 72 * Returns a TinyMCE view constructor. 73 * 74 * @param {String} The view type. 75 */ 76 get: function( type ) { 77 return views[ type ]; 78 }, 79 80 /** 81 * The editor DOM is being rebuilt, run cleanup. 82 */ 83 unbind: function() { 84 _.each( instances, function( instance ) { 85 instance.unbind(); 86 } ); 87 }, 88 89 /** 90 * Scans a given string for each view's pattern, 91 * replacing any matches with markers, 92 * and creates a new instance for every match, 93 * which triggers the related data to be fetched. 94 * 95 * @param {String} String to scan. 96 */ 97 toViews: function( content ) { 98 var pieces = [ { content: content } ], 99 current; 100 101 _.each( views, function( view, type ) { 102 current = pieces.slice(); 103 pieces = []; 104 105 _.each( current, function( piece ) { 106 var remaining = piece.content, 107 result; 108 109 // Ignore processed pieces, but retain their location. 110 if ( piece.processed ) { 111 pieces.push( piece ); 112 return; 113 } 114 115 // Iterate through the string progressively matching views 116 // and slicing the string as we go. 117 while ( remaining && ( result = view.toView( remaining ) ) ) { 118 // Any text before the match becomes an unprocessed piece. 119 if ( result.index ) { 120 pieces.push( { content: remaining.substring( 0, result.index ) } ); 121 } 122 123 // Add the processed piece for the match. 124 pieces.push( { 125 content: wp.mce.views.toView( type, result.content, result.options ), 126 processed: true 127 } ); 128 129 // Update the remaining content. 130 remaining = remaining.slice( result.index + result.content.length ); 131 } 132 133 // There are no additional matches. If any content remains, 134 // add it as an unprocessed piece. 135 if ( remaining ) { 136 pieces.push( { content: remaining } ); 137 } 138 }); 139 }); 140 141 return _.pluck( pieces, 'content' ).join(''); 142 }, 143 144 /** 145 * Returns the marked text for a particular view type, 146 * and creates a new instance for that view type if it does not exist. 147 * The text will be returned unmarked if no view of that type is registered. 148 * 149 * @param {String} The view type. 150 * @param {String} The textual representation of the view. 151 * @param {Object} Options. 152 * 153 * @return {String} The markered text. 154 */ 155 toView: function( type, text, options ) { 156 var view = this.get( type ), 157 encodedText = window.encodeURIComponent( text ); 158 159 if ( ! view ) { 160 return text; 161 } 162 163 if ( ! this.getInstance( encodedText ) ) { 164 instances[ encodedText ] = new view.View( _.extend( options, { 165 encodedText: encodedText, 166 type: type 167 } ) ); 168 } 169 170 return '<p data-wpview-marker="' + encodedText + '">' + text + '</p>'; 171 }, 172 173 /** 174 * Refresh views after an update is made. 175 * 176 * @param {Object} The view to refresh. 177 * @param {String} The textual representation of the view. 178 * @param {Boolean} Whether or not to force rendering. 179 */ 180 refreshView: function( view, text, force ) { 181 var encodedText = window.encodeURIComponent( text ); 182 183 if ( ! this.getInstance( encodedText ) ) { 184 instances[ encodedText ] = new view.View( _.extend( view.toView( text ).options, { 185 encodedText: encodedText, 186 type: view.type 187 } ) ); 188 } 189 190 instances[ encodedText ].render( force ); 191 }, 192 193 /** 194 * @param {String} The textual representation of the view, encoded. 195 */ 196 getInstance: function( encodedText ) { 197 return instances[ encodedText ]; 198 }, 199 200 /** 201 * Renders any view instances. 202 * 203 * View instances are detected by the presence of markers. 204 * To generate markers, pass your content through `toViews`. 205 * 206 * @param {Boolean} Whether or not to force rendering. 207 */ 208 render: function( force ) { 209 _.each( instances, function( instance ) { 210 instance.render( force ); 211 } ); 212 }, 213 214 /** 215 * @param {HTMLElement} A view node. 216 */ 217 edit: function( node ) { 218 var type = $( node ).data( 'wpview-type' ), 219 view = this.get( type ); 220 221 if ( view ) { 222 view.edit( node ); 223 } 224 } 225 }; 226 227 /** 28 228 * A Backbone-like View constructor intended for use when rendering a TinyMCE View. The main difference is 29 229 * that the TinyMCE View is not tied to a particular DOM node. 30 230 * 31 * @param {Object} [options={}]231 * @param {Object} Options. 32 232 */ 33 233 wp.mce.View = function( options ) { 34 234 options = options || {}; … … 38 238 }; 39 239 40 240 _.extend( wp.mce.View.prototype, { 241 242 /** 243 * Whether or not to display a loader. 244 * 245 * @type {Boolean} 246 */ 247 loader: true, 248 249 /** 250 * Runs after the view instance is created. 251 */ 41 252 initialize: function() {}, 253 254 /** 255 * Retuns the HTML string to render in the view. 256 * 257 * @return {String} The HTML string. 258 */ 42 259 getHtml: function() { 43 260 return ''; 44 261 }, 45 loadingPlaceholder: function() {46 return '' +47 '<div class="loading-placeholder">' +48 '<div class="dashicons dashicons-admin-media"></div>' +49 '<div class="wpview-loading"><ins></ins></div>' +50 '</div>';51 },52 render: function( force ) {53 if ( force || ! this.rendered() ) {54 this.unbind();55 262 56 this.setContent( 57 '<p class="wpview-selection-before">\u00a0</p>' + 58 '<div class="wpview-body" contenteditable="false">' + 59 '<div class="toolbar mce-arrow-down">' + 60 ( _.isFunction( views[ this.type ].edit ) ? '<div class="dashicons dashicons-edit edit"></div>' : '' ) + 61 '<div class="dashicons dashicons-no remove"></div>' + 62 '</div>' + 63 '<div class="wpview-content wpview-type-' + this.type + '">' + 64 ( this.getHtml() || this.loadingPlaceholder() ) + 65 '</div>' + 66 ( this.overlay ? '<div class="wpview-overlay"></div>' : '' ) + 67 '</div>' + 68 '<p class="wpview-selection-after">\u00a0</p>', 69 'wrap' 70 ); 263 /** 264 * Retuns the HTML string to render in the view. 265 * 266 * @param {Boolean} Whether or not to rerender. 267 */ 268 render: function( rerender ) { 269 var i = 0; 71 270 72 $( this ).trigger( 'ready' ); 271 // If there's nothing to render an no loader needs to be shown, stop. 272 if ( ! this.loader && ! this.getHtml() ) { 273 return; 274 } 275 276 // We're about to rerender all views of this instance, so unbind rendered views. 277 rerender && this.unbind(); 278 279 // Replace any left over markers. 280 this.replaceMarkers(); 73 281 74 this.rendered( true ); 282 if ( this.getHtml() ) { 283 this.setContent( this.getHtml(), function( editor, node ) { 284 $( node ).data( 'rendered', true ); 285 this.bindNodes.apply( this, arguments ); 286 i++; 287 }, rerender ? null : false ); 288 289 // Use `bindNodes` if possible. 290 i && $( this ).trigger( 'ready' ); 291 } else { 292 this.setLoader(); 75 293 } 76 294 }, 77 unbind: function() {}, 295 296 /** 297 * Runs after a view is added to the DOM. 298 * 299 * @param {tinymce.Editor} The editor instance the view node is in. 300 * @param {HTMLElement} The view node. 301 * @param {HTMLElement} The view's content node. 302 */ 303 bindNodes: function() {}, 304 305 /** 306 * Runs before a view is removed from the DOM. 307 */ 308 unbind: function() { 309 this.getNodes( function() { 310 this.unbindNodes.apply( this, arguments ); 311 }, true ); 312 }, 313 314 /** 315 * Runs before a view is removed from the DOM. 316 * 317 * @param {tinymce.Editor} The editor instance the view node is in. 318 * @param {HTMLElement} The view node. 319 * @param {HTMLElement} The view's content node. 320 */ 321 unbindNodes: function() {}, 322 323 /** 324 * Gets all the TinyMCE editors that support views. 325 * 326 * @param {Function} A callback (optional). 327 * 328 * @return {tinymce.Editor[]} An array of TinyMCE editor instances. 329 */ 78 330 getEditors: function( callback ) { 79 331 var editors = []; 80 332 81 333 _.each( tinymce.editors, function( editor ) { 82 334 if ( editor.plugins.wpview ) { 83 if ( callback ) {84 callback( editor );85 }86 87 335 editors.push( editor ); 336 callback && callback.call( this, editor ); 88 337 } 89 338 }, this ); 90 339 91 340 return editors; 92 341 }, 93 getNodes: function( callback ) { 94 var nodes = [], 95 self = this; 342 343 /** 344 * Gets all the view nodes in each editor. 345 * 346 * @param {Function} A callback (optional). 347 * @param {Boolean} Get (un)rendered nodes (optional). 348 * 349 * @return {HTMLElement[]} An array of view nodes. 350 */ 351 getNodes: function( callback, rendered ) { 352 var nodes = []; 96 353 97 354 this.getEditors( function( editor ) { 355 var self = this; 356 98 357 $( editor.getBody() ) 99 .find( '[data-wpview-text="' + self.encodedText + '"]' ) 100 .each( function ( i, node ) { 101 if ( callback ) { 102 callback( editor, node, $( node ).find( '.wpview-content' ).get( 0 ) ); 103 } 358 .find( '[data-wpview-text="' + self.encodedText + '"]' ) 359 .filter( function() { 360 var data; 104 361 105 nodes.push( node ); 106 } ); 362 if ( rendered == null ) { 363 return true; 364 } 365 366 data = $( this ).data( 'rendered' ) === true; 367 368 return rendered ? data : ! data; 369 } ) 370 .each( function ( i, node ) { 371 nodes.push( node ); 372 callback && callback.call( self, editor, node, $( node ).find( '.wpview-content' ).get( 0 ) ); 373 } ); 107 374 } ); 108 375 109 376 return nodes; 110 377 }, 111 setContent: function( html, option ) {112 this.getNodes( function ( editor, node, content ) {113 var el = ( option === 'wrap' || option === 'replace' ) ? node : content,114 insert = html;115 378 116 if ( _.isString( insert ) ) { 117 insert = editor.dom.createFragment( insert ); 118 } 379 /** 380 * Gets all the markers in each editor. 381 * 382 * @param {Function} A callback (optional). 383 * 384 * @return {HTMLElement[]} An array of marker nodes. 385 */ 386 getMarkers: function( callback ) { 387 var markers = []; 119 388 120 if ( option === 'replace' ) { 121 editor.dom.replace( insert, el ); 122 } else { 123 el.innerHTML = ''; 124 el.appendChild( insert ); 389 this.getEditors( function( editor ) { 390 var self = this; 391 392 $( editor.getBody() ) 393 .find( '[data-wpview-marker="' + this.encodedText + '"]' ) 394 .each( function( i, node ) { 395 markers.push( node ); 396 callback && callback.call( self, editor, node ); 397 } ); 398 } ); 399 400 return markers; 401 }, 402 403 /** 404 * Replaces all markers with view nodes. 405 */ 406 replaceMarkers: function() { 407 this.getMarkers( function( editor, node ) { 408 if ( $( node ).text() !== decodeURIComponent( this.encodedText ) ) { 409 editor.dom.setAttrib( node, 'data-wpview-marker', null ); 410 return; 125 411 } 412 413 editor.dom.replace( 414 editor.dom.createFragment( 415 '<div class="wpview-wrap" data-wpview-text="' + this.encodedText + '" data-wpview-type="' + this.type + '">' + 416 '<p class="wpview-selection-before">\u00a0</p>' + 417 '<div class="wpview-body" contenteditable="false">' + 418 '<div class="toolbar mce-arrow-down">' + 419 ( _.isFunction( views[ this.type ].edit ) ? '<div class="dashicons dashicons-edit edit"></div>' : '' ) + 420 '<div class="dashicons dashicons-no remove"></div>' + 421 '</div>' + 422 '<div class="wpview-content wpview-type-' + this.type + '"></div>' + 423 ( this.overlay ? '<div class="wpview-overlay"></div>' : '' ) + 424 '</div>' + 425 '<p class="wpview-selection-after">\u00a0</p>' + 426 '</div>' 427 ), 428 node 429 ); 430 } ); 431 }, 432 433 /** 434 * Removes all markers. 435 */ 436 removeMarkers: function() { 437 this.getMarkers( function( editor, node ) { 438 editor.dom.setAttrib( node, 'data-wpview-marker', null ); 126 439 } ); 127 440 }, 128 /* jshint scripturl: true */ 129 setIframes: function ( head, body ) { 441 442 /** 443 * Gets all the markers in each editor. 444 * 445 * @param {(String|HTMLElement)} The content to set. 446 * @param {Function} A callback (optional). 447 * @param {Boolean} Only set for (un)rendered nodes (optional). 448 */ 449 setContent: function( html, callback, rendered ) { 450 this.getNodes( function( editor, node, content ) { 451 content.innerHTML = ''; 452 content.appendChild( _.isString( html ) ? editor.dom.createFragment( html ) : html ); 453 callback && callback.apply( this, arguments ); 454 }, rendered ); 455 }, 456 457 /** 458 * Set an iframe as the view content. 459 */ 460 setIframes: function( head, body ) { 130 461 var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver, 131 462 importStyles = this.type === 'video' || this.type === 'audio' || this.type === 'playlist'; 132 463 133 464 if ( head || body.indexOf( '<script' ) !== -1 ) { 134 this.getNodes( function 465 this.getNodes( function( editor, node, content ) { 135 466 var dom = editor.dom, 136 467 styles = '', 137 468 bodyClasses = editor.getBody().className || '', … … 156 487 } 157 488 } 158 489 159 // Seems Firefox needs a bit of time to insert/set the view nodes, or the iframe will fail160 // especially when switching Text => Visual.490 // Seems Firefox needs a bit of time to insert/set the view nodes, 491 // or the iframe will fail especially when switching Text => Visual. 161 492 setTimeout( function() { 162 493 iframe = dom.add( content, 'iframe', { 494 /* jshint scripturl: true */ 163 495 src: tinymce.Env.ie ? 'javascript:""' : '', 164 496 frameBorder: '0', 165 497 allowTransparency: 'true', … … 241 573 iframeDoc.body.className = editor.getBody().className; 242 574 }); 243 575 } 244 }, waitInterval);576 }, 50 ); 245 577 }); 246 578 } else { 247 579 this.setContent( body ); 248 580 } 249 581 }, 582 583 /** 584 * Set a loader. 585 */ 586 setLoader: function() { 587 this.setContent( 588 '<div class="loading-placeholder">' + 589 '<div class="dashicons dashicons-admin-media"></div>' + 590 '<div class="wpview-loading"><ins></ins></div>' + 591 '</div>' 592 ); 593 }, 594 595 /** 596 * Set an error. 597 * 598 * @param {String} The error message to set. 599 * @param {String} A dashicon ID (optional). {@link https://developer.wordpress.org/resource/dashicons/} 600 */ 250 601 setError: function( message, dashicon ) { 251 602 this.setContent( 252 603 '<div class="wpview-error">' + 253 '<div class="dashicons dashicons-' + ( dashicon ? dashicon :'no' ) + '"></div>' +604 '<div class="dashicons dashicons-' + ( dashicon || 'no' ) + '"></div>' + 254 605 '<p>' + message + '</p>' + 255 606 '</div>' 256 607 ); 257 },258 rendered: function( value ) {259 var notRendered;260 261 this.getNodes( function( editor, node ) {262 if ( value != null ) {263 $( node ).data( 'rendered', value === true );264 } else {265 notRendered = notRendered || ! $( node ).data( 'rendered' );266 }267 } );268 269 return ! notRendered;270 608 } 271 609 } ); 272 610 273 // take advantage of the Backbone extend method611 // Take advantage of the Backbone extend method. 274 612 wp.mce.View.extend = Backbone.View.extend; 613 } )( jQuery ); 275 614 276 /** 277 * wp.mce.views 278 * 279 * A set of utilities that simplifies adding custom UI within a TinyMCE editor. 280 * At its core, it serves as a series of converters, transforming text to a 281 * custom UI, and back again. 282 */ 283 wp.mce.views = { 284 285 /** 286 * wp.mce.views.register( type, view ) 287 * 288 * Registers a new TinyMCE view. 289 * 290 * @param type 291 * @param constructor 292 * 293 */ 294 register: function( type, constructor ) { 295 var defaultConstructor = { 296 type: type, 297 View: {}, 298 toView: function( content ) { 299 var match = wp.shortcode.next( this.type, content ); 300 301 if ( ! match ) { 302 return; 303 } 304 305 return { 306 index: match.index, 307 content: match.content, 308 options: { 309 shortcode: match.shortcode 310 } 311 }; 312 } 313 }; 314 315 constructor = _.defaults( constructor, defaultConstructor ); 316 constructor.View = wp.mce.View.extend( constructor.View ); 317 318 views[ type ] = constructor; 319 }, 320 321 /** 322 * wp.mce.views.get( id ) 323 * 324 * Returns a TinyMCE view constructor. 325 * 326 * @param type 327 */ 328 get: function( type ) { 329 return views[ type ]; 330 }, 331 332 /** 333 * wp.mce.views.unregister( type ) 334 * 335 * Unregisters a TinyMCE view. 336 * 337 * @param type 338 */ 339 unregister: function( type ) { 340 delete views[ type ]; 341 }, 342 343 /** 344 * wp.mce.views.unbind( editor ) 345 * 346 * The editor DOM is being rebuilt, run cleanup. 347 */ 348 unbind: function() { 349 _.each( instances, function( instance ) { 350 instance.unbind(); 351 } ); 352 }, 353 354 /** 355 * toViews( content ) 356 * Scans a `content` string for each view's pattern, replacing any 357 * matches with wrapper elements, and creates a new instance for 358 * every match, which triggers the related data to be fetched. 359 * 360 * @param content 361 */ 362 toViews: function( content ) { 363 var pieces = [ { content: content } ], 364 current; 365 366 _.each( views, function( view, viewType ) { 367 current = pieces.slice(); 368 pieces = []; 369 370 _.each( current, function( piece ) { 371 var remaining = piece.content, 372 result; 373 374 // Ignore processed pieces, but retain their location. 375 if ( piece.processed ) { 376 pieces.push( piece ); 377 return; 378 } 379 380 // Iterate through the string progressively matching views 381 // and slicing the string as we go. 382 while ( remaining && (result = view.toView( remaining )) ) { 383 // Any text before the match becomes an unprocessed piece. 384 if ( result.index ) { 385 pieces.push({ content: remaining.substring( 0, result.index ) }); 386 } 387 388 // Add the processed piece for the match. 389 pieces.push({ 390 content: wp.mce.views.toView( viewType, result.content, result.options ), 391 processed: true 392 }); 393 394 // Update the remaining content. 395 remaining = remaining.slice( result.index + result.content.length ); 396 } 397 398 // There are no additional matches. If any content remains, 399 // add it as an unprocessed piece. 400 if ( remaining ) { 401 pieces.push({ content: remaining }); 402 } 403 }); 404 }); 405 406 return _.pluck( pieces, 'content' ).join(''); 407 }, 408 409 /** 410 * Create a placeholder for a particular view type 411 * 412 * @param viewType 413 * @param text 414 * @param options 415 * 416 */ 417 toView: function( viewType, text, options ) { 418 var view = wp.mce.views.get( viewType ), 419 encodedText = window.encodeURIComponent( text ), 420 instance, viewOptions; 421 422 423 if ( ! view ) { 424 return text; 425 } 426 427 if ( ! wp.mce.views.getInstance( encodedText ) ) { 428 viewOptions = options; 429 viewOptions.type = viewType; 430 viewOptions.encodedText = encodedText; 431 instance = new view.View( viewOptions ); 432 instances[ encodedText ] = instance; 433 } 434 435 return wp.html.string({ 436 tag: 'div', 437 438 attrs: { 439 'class': 'wpview-wrap', 440 'data-wpview-text': encodedText, 441 'data-wpview-type': viewType 442 }, 443 444 content: '\u00a0' 445 }); 446 }, 447 448 /** 449 * Refresh views after an update is made 450 * 451 * @param view {object} being refreshed 452 * @param text {string} textual representation of the view 453 * @param force {Boolean} whether to force rendering 454 */ 455 refreshView: function( view, text, force ) { 456 var encodedText = window.encodeURIComponent( text ), 457 viewOptions, 458 result, instance; 459 460 instance = wp.mce.views.getInstance( encodedText ); 461 462 if ( ! instance ) { 463 result = view.toView( text ); 464 viewOptions = result.options; 465 viewOptions.type = view.type; 466 viewOptions.encodedText = encodedText; 467 instance = new view.View( viewOptions ); 468 instances[ encodedText ] = instance; 469 } 470 471 instance.render( force ); 472 }, 473 474 getInstance: function( encodedText ) { 475 return instances[ encodedText ]; 476 }, 477 478 /** 479 * render( scope ) 480 * 481 * Renders any view instances inside a DOM node `scope`. 482 * 483 * View instances are detected by the presence of wrapper elements. 484 * To generate wrapper elements, pass your content through 485 * `wp.mce.view.toViews( content )`. 486 */ 487 render: function( force ) { 488 _.each( instances, function( instance ) { 489 instance.render( force ); 490 } ); 491 }, 492 493 edit: function( node ) { 494 var viewType = $( node ).data('wpview-type'), 495 view = wp.mce.views.get( viewType ); 496 497 if ( view ) { 498 view.edit( node ); 499 } 500 } 501 }; 615 /* 616 * The WordPress core TinyMCE views. 617 * Views for the gallery, audio, video, playlist and embed shortcodes, 618 * and a view for embeddable URLs. 619 */ 620 ( function( $, views ) { 621 var mediaWindows = [], 622 windowIdx = 0, 623 waitInterval = 50; 502 624 503 wp.mce.views.register( 'gallery', {625 views.register( 'gallery', { 504 626 View: { 505 template: media.template( 'editor-gallery' ),627 template: wp.media.template( 'editor-gallery' ), 506 628 507 629 // The fallback post ID to use as a parent for galleries that don't 508 630 // specify the `ids` or `include` parameters. … … 520 642 521 643 this.attachments = wp.media.gallery.attachments( this.shortcode, this.postID ); 522 644 this.dfd = this.attachments.more().done( function() { 523 self.render( true);645 self.render(); 524 646 } ); 525 647 }, 526 648 … … 681 803 } 682 804 } ) 683 805 .done( function( response ) { 684 if ( response ) { 685 self.parsed = response; 686 self.setIframes( response.head, response.body ); 687 self.deferredListen(); 688 } else { 689 self.fail( true ); 690 } 806 self.parsed = response; 807 self.render(); 691 808 } ) 692 809 .fail( function( response ) { 693 810 self.fail( response || true ); … … 703 820 } 704 821 } 705 822 706 if ( this.error.message ) { 707 if ( ( this.error.type === 'not-embeddable' && this.type === 'embed' ) || this.error.type === 'not-ssl' || 708 this.error.type === 'no-items' ) { 709 710 this.setError( this.error.message, 'admin-media' ); 711 } else { 712 this.setContent( '<p>' + this.original + '</p>', 'replace' ); 713 } 714 } else if ( this.error.statusText ) { 715 this.setError( this.error.statusText, 'admin-media' ); 716 } else if ( this.original ) { 717 this.setContent( '<p>' + this.original + '</p>', 'replace' ); 823 if ( this.type === 'embedURL' ) { 824 this.removeMarkers(); 825 } else { 826 this.setError( this.error.message || this.error.statusText, 'admin-media' ); 718 827 } 719 828 }, 720 829 … … 742 851 743 852 unbind: function() { 744 853 this.stopPlayers( 'remove' ); 854 }, 855 856 getHtml: function() { 857 return this.parsed ? '\u00a0' : ''; 745 858 } 746 859 }, 747 860 … … 790 903 * 791 904 * @mixes wp.mce.av 792 905 */ 793 wp.mce.views.register( 'video', _.extend( {}, wp.mce.av, {906 views.register( 'video', _.extend( {}, wp.mce.av, { 794 907 state: 'video-details' 795 908 } ) ); 796 909 … … 799 912 * 800 913 * @mixes wp.mce.av 801 914 */ 802 wp.mce.views.register( 'audio', _.extend( {}, wp.mce.av, {915 views.register( 'audio', _.extend( {}, wp.mce.av, { 803 916 state: 'audio-details' 804 917 } ) ); 805 918 … … 808 921 * 809 922 * @mixes wp.mce.av 810 923 */ 811 wp.mce.views.register( 'playlist', _.extend( {}, wp.mce.av, {924 views.register( 'playlist', _.extend( {}, wp.mce.av, { 812 925 state: [ 'playlist-edit', 'video-playlist-edit' ] 813 926 } ) ); 814 927 … … 821 934 action: 'parse-embed', 822 935 initialize: function( options ) { 823 936 this.content = options.content; 824 this.original = options.url || options.shortcode.string();825 937 826 938 if ( options.url ) { 827 this.shortcode = media.embed.shortcode( { 939 this.loader = false; 940 this.shortcode = wp.media.embed.shortcode( { 828 941 url: options.url 829 942 } ); 830 943 } else { … … 838 951 } 839 952 } ), 840 953 edit: function( node ) { 841 var embed = media.embed,954 var embed = wp.media.embed, 842 955 self = this, 843 956 frame, 844 957 data, … … 873 986 } 874 987 }; 875 988 876 wp.mce.views.register( 'embed', _.extend( {}, wp.mce.embedMixin ) );989 views.register( 'embed', _.extend( {}, wp.mce.embedMixin ) ); 877 990 878 wp.mce.views.register( 'embedURL', _.extend( {}, wp.mce.embedMixin, {991 views.register( 'embedURL', _.extend( {}, wp.mce.embedMixin, { 879 992 toView: function( content ) { 880 993 var re = /(^|<p>)(https?:\/\/[^\s"]+?)(<\/p>\s*|$)/gi, 881 994 match = re.exec( tinymce.trim( content ) ); … … 893 1006 }; 894 1007 } 895 1008 } ) ); 896 897 }(jQuery)); 1009 } )( jQuery, wp.mce.views );