Changeset 28680
- Timestamp:
- 06/05/2014 05:26:24 PM (11 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/wp-includes/js/mce-view.js
r28594 r28680 23 23 * A Backbone-like View constructor intended for use when rendering a TinyMCE View. The main difference is 24 24 * that the TinyMCE View is not tied to a particular DOM node. 25 * 26 * @param {Object} [options={}] 25 27 */ 26 28 wp.mce.View = function( options ) { 27 options || (options = {});28 _.extend( this, _.pick(options, viewOptions));29 this.initialize.apply( this, arguments);29 options = options || {}; 30 _.extend( this, _.pick( options, viewOptions ) ); 31 this.initialize.apply( this, arguments ); 30 32 }; 31 33 … … 37 39 // Search all tinymce editor instances and update the placeholders 38 40 _.each( tinymce.editors, function( editor ) { 39 var doc,self = this;41 var self = this; 40 42 if ( editor.plugins.wpview ) { 41 doc = editor.getDoc(); 42 $( doc ).find( '[data-wpview-text="' + this.encodedText + '"]' ).each(function (i, elem) { 43 var node = $( elem ); 43 $( editor.getDoc() ).find( '[data-wpview-text="' + this.encodedText + '"]' ).each( function ( i, element ) { 44 var node = $( element ); 44 45 // The <ins> is used to mark the end of the wrapper div. Needed when comparing 45 46 // the content as string for preventing extra undo levels. 46 47 node.html( html ).append( '<ins data-wpview-end="1"></ins>' ); 47 $( self ).trigger( 'ready', elem );48 $( self ).trigger( 'ready', element ); 48 49 }); 49 50 } … … 75 76 */ 76 77 register: function( type, constructor ) { 78 var defaultConstructor = { 79 type: type, 80 View: {}, 81 toView: function( content ) { 82 var match = wp.shortcode.next( this.type, content ); 83 84 if ( ! match ) { 85 return; 86 } 87 88 return { 89 index: match.index, 90 content: match.content, 91 options: { 92 shortcode: match.shortcode 93 } 94 }; 95 } 96 }; 97 98 constructor = _.defaults( constructor, defaultConstructor ); 99 constructor.View = wp.mce.View.extend( constructor.View ); 100 77 101 views[ type ] = constructor; 78 102 }, … … 82 106 * 83 107 * Returns a TinyMCE view constructor. 108 * 109 * @param type 84 110 */ 85 111 get: function( type ) { … … 91 117 * 92 118 * Unregisters a TinyMCE view. 119 * 120 * @param type 93 121 */ 94 122 unregister: function( type ) { … … 113 141 * every match, which triggers the related data to be fetched. 114 142 * 143 * @param content 115 144 */ 116 145 toViews: function( content ) { … … 253 282 }; 254 283 255 wp.mce.gallery = { 256 shortcode: 'gallery', 257 toView: function( content ) { 258 var match = wp.shortcode.next( this.shortcode, content ); 259 260 if ( ! match ) { 261 return; 262 } 263 264 return { 265 index: match.index, 266 content: match.content, 267 options: { 268 shortcode: match.shortcode 269 } 270 }; 271 }, 272 View: wp.mce.View.extend({ 273 className: 'editor-gallery', 274 template: media.template('editor-gallery'), 284 wp.mce.views.register( 'gallery', { 285 View: { 286 template: media.template( 'editor-gallery' ), 275 287 276 288 // The fallback post ID to use as a parent for galleries that don't … … 322 334 323 335 } 324 } ),336 }, 325 337 326 338 edit: function( node ) { … … 339 351 }); 340 352 } 341 342 }; 343 wp.mce.views.register( 'gallery', wp.mce.gallery ); 353 } ); 344 354 345 355 /** 346 * Tiny MCE Views for Audio / Video 347 * 348 */ 349 350 /** 351 * These are base methods that are shared by each shortcode's MCE controller 356 * These are base methods that are shared by the audio and video shortcode's MCE controller. 352 357 * 353 358 * @mixin 354 359 */ 355 wp.mce. media= {360 wp.mce.av = { 356 361 loaded: false, 357 toView: wp.mce.gallery.toView, 362 363 View: _.extend( {}, wp.media.mixin, { 364 initialize: function( options ) { 365 this.players = []; 366 this.shortcode = options.shortcode; 367 _.bindAll( this, 'setPlayer', 'pausePlayers' ); 368 $( this ).on( 'ready', this.setPlayer ); 369 $( 'body' ).on( 'click', '.wp-switch-editor', this.pausePlayers ); 370 $( document ).on( 'media:edit', this.pausePlayers ); 371 }, 372 373 /** 374 * Creates the player instance for the current node 375 * 376 * @global MediaElementPlayer 377 * 378 * @param {Event} e 379 * @param {HTMLElement} node 380 */ 381 setPlayer: function(e, node) { 382 // if the ready event fires on an empty node 383 if ( ! node ) { 384 return; 385 } 386 387 var self = this, 388 media, 389 firefox = this.ua.is( 'ff' ), 390 className = '.wp-' + this.shortcode.tag + '-shortcode'; 391 392 media = $( node ).find( className ); 393 394 if ( ! this.isCompatible( media ) ) { 395 media.closest( '.wpview-wrap' ).addClass( 'wont-play' ); 396 if ( ! media.parent().hasClass( 'wpview-wrap' ) ) { 397 media.parent().replaceWith( media ); 398 } 399 media.replaceWith( '<p>' + media.find( 'source' ).eq(0).prop( 'src' ) + '</p>' ); 400 return; 401 } else { 402 media.closest( '.wpview-wrap' ).removeClass( 'wont-play' ); 403 if ( firefox ) { 404 media.prop( 'preload', 'metadata' ); 405 } else { 406 media.prop( 'preload', 'none' ); 407 } 408 } 409 410 media = wp.media.view.MediaDetails.prepareSrc( media.get(0) ); 411 412 setTimeout( function() { 413 wp.mce.av.loaded = true; 414 self.players.push( new MediaElementPlayer( media, self.mejsSettings ) ); 415 }, wp.mce.av.loaded ? 10 : 500 ); 416 }, 417 418 /** 419 * Pass data to the View's Underscore template and return the compiled output 420 * 421 * @returns {string} 422 */ 423 getHtml: function() { 424 var attrs = this.shortcode.attrs.named; 425 attrs.content = this.shortcode.content; 426 427 return this.template({ model: _.defaults( 428 attrs, 429 wp.media[ this.shortcode.tag ].defaults ) 430 }); 431 }, 432 433 unbind: function() { 434 this.unsetPlayers(); 435 } 436 } ), 358 437 359 438 /** … … 398 477 399 478 /** 400 * Base View class for audio and video shortcodes401 *402 * @constructor403 * @augments wp.mce.View404 * @mixes wp.media.mixin405 */406 wp.mce.media.View = wp.mce.View.extend({407 initialize: function( options ) {408 this.players = [];409 this.shortcode = options.shortcode;410 _.bindAll( this, 'setPlayer', 'pausePlayers' );411 $( this ).on( 'ready', this.setPlayer );412 $( 'body' ).on( 'click', '.wp-switch-editor', this.pausePlayers );413 $( document ).on( 'media:edit', this.pausePlayers );414 },415 416 /**417 * Creates the player instance for the current node418 *419 * @global MediaElementPlayer420 * @global _wpmejsSettings421 *422 * @param {Event} e423 * @param {HTMLElement} node424 */425 setPlayer: function(e, node) {426 // if the ready event fires on an empty node427 if ( ! node ) {428 return;429 }430 431 var self = this,432 media,433 firefox = this.ua.is( 'ff' ),434 className = '.wp-' + this.shortcode.tag + '-shortcode';435 436 media = $( node ).find( className );437 438 if ( ! this.isCompatible( media ) ) {439 media.closest( '.wpview-wrap' ).addClass( 'wont-play' );440 if ( ! media.parent().hasClass( 'wpview-wrap' ) ) {441 media.parent().replaceWith( media );442 }443 media.replaceWith( '<p>' + media.find( 'source' ).eq(0).prop( 'src' ) + '</p>' );444 return;445 } else {446 media.closest( '.wpview-wrap' ).removeClass( 'wont-play' );447 if ( firefox ) {448 media.prop( 'preload', 'metadata' );449 } else {450 media.prop( 'preload', 'none' );451 }452 }453 454 media = wp.media.view.MediaDetails.prepareSrc( media.get(0) );455 456 setTimeout( function() {457 wp.mce.media.loaded = true;458 self.players.push( new MediaElementPlayer( media, self.mejsSettings ) );459 }, wp.mce.media.loaded ? 10 : 500 );460 },461 462 /**463 * Pass data to the View's Underscore template and return the compiled output464 *465 * @returns {string}466 */467 getHtml: function() {468 var attrs = this.shortcode.attrs.named;469 attrs.content = this.shortcode.content;470 471 return this.template({ model: _.defaults(472 attrs,473 wp.media[ this.shortcode.tag ].defaults )474 });475 },476 477 unbind: function() {478 this.unsetPlayers();479 }480 });481 _.extend( wp.mce.media.View.prototype, wp.media.mixin );482 483 /**484 479 * TinyMCE handler for the video shortcode 485 480 * 486 * @mixes wp.mce. media481 * @mixes wp.mce.av 487 482 */ 488 wp.mce.vi deo = _.extend( {}, wp.mce.media, {483 wp.mce.views.register( 'video', _.extend( {}, wp.mce.av, { 489 484 shortcode: 'video', 490 485 state: 'video-details', 491 View: wp.mce.media.View.extend({ 492 className: 'editor-video', 493 template: media.template('editor-video') 494 }) 495 } ); 496 wp.mce.views.register( 'video', wp.mce.video ); 486 View: _.extend( {}, wp.mce.av.View, { 487 template: media.template( 'editor-video' ) 488 } ) 489 } ) ); 497 490 498 491 /** 499 492 * TinyMCE handler for the audio shortcode 500 493 * 501 * @mixes wp.mce. media494 * @mixes wp.mce.av 502 495 */ 503 wp.mce. audio = _.extend( {}, wp.mce.media, {496 wp.mce.views.register( 'audio', _.extend( {}, wp.mce.av, { 504 497 shortcode: 'audio', 505 498 state: 'audio-details', 506 View: wp.mce.media.View.extend({ 507 className: 'editor-audio', 508 template: media.template('editor-audio') 509 }) 510 } ); 511 wp.mce.views.register( 'audio', wp.mce.audio ); 512 513 /** 514 * Base View class for playlist shortcodes 515 * 516 * @constructor 517 * @augments wp.mce.View 518 * @mixes wp.media.mixin 519 */ 520 wp.mce.media.PlaylistView = wp.mce.View.extend({ 521 className: 'editor-playlist', 522 template: media.template('editor-playlist'), 523 524 initialize: function( options ) { 525 this.players = []; 526 this.data = {}; 527 this.attachments = []; 528 this.shortcode = options.shortcode; 529 530 $( 'body' ).on( 'click', '.wp-switch-editor', this.pausePlayers ); 531 $( document ).on( 'media:edit', this.pausePlayers ); 532 533 this.fetch(); 534 }, 535 536 /** 537 * Asynchronously fetch the shortcode's attachments 538 */ 539 fetch: function() { 540 this.attachments = wp.media.playlist.attachments( this.shortcode ); 541 this.dfd = this.attachments.more().done( _.bind( this.render, this ) ); 542 }, 543 544 /** 545 * Get the HTML for the view (which also set's the data), replace the 546 * current HTML, and then invoke the WPPlaylistView instance to render 547 * the playlist in the editor 548 * 549 * @global WPPlaylistView 550 * @global tinymce.editors 551 */ 552 render: function() { 553 var html = this.getHtml(), self = this; 554 555 _.each( tinymce.editors, function( editor ) { 556 var doc; 557 if ( editor.plugins.wpview ) { 558 doc = editor.getDoc(); 559 $( doc ).find( '[data-wpview-text="' + this.encodedText + '"]' ).each(function (i, elem) { 560 var node = $( elem ); 561 562 // The <ins> is used to mark the end of the wrapper div. Needed when comparing 563 // the content as string for preventing extra undo levels. 564 node.html( html ).append( '<ins data-wpview-end="1"></ins>' ); 565 566 if ( ! self.data.tracks ) { 567 return; 568 } 569 570 self.players.push( new WPPlaylistView({ 571 el: $( elem ).find( '.wp-playlist' ).get(0), 572 metadata: self.data 573 }).player ); 574 }); 575 } 576 }, this ); 577 }, 578 579 /** 580 * Set the data that will be used to compile the Underscore template, 581 * compile the template, and then return it. 582 * 583 * @returns {string} 584 */ 585 getHtml: function() { 586 var data = this.shortcode.attrs.named, 587 model = wp.media.playlist, 588 options, 589 attachments, 590 tracks = []; 591 592 // Don't render errors while still fetching attachments 593 if ( this.dfd && 'pending' === this.dfd.state() && ! this.attachments.length ) { 594 return; 595 } 596 597 _.each( model.defaults, function( value, key ) { 598 data[ key ] = model.coerce( data, key ); 599 }); 600 601 options = { 602 type: data.type, 603 style: data.style, 604 tracklist: data.tracklist, 605 tracknumbers: data.tracknumbers, 606 images: data.images, 607 artists: data.artists 608 }; 609 610 if ( ! this.attachments.length ) { 611 return this.template( options ); 612 } 613 614 attachments = this.attachments.toJSON(); 615 616 _.each( attachments, function( attachment ) { 617 var size = {}, resize = {}, track = { 618 src : attachment.url, 619 type : attachment.mime, 620 title : attachment.title, 621 caption : attachment.caption, 622 description : attachment.description, 623 meta : attachment.meta 624 }; 625 626 if ( 'video' === data.type ) { 627 size.width = attachment.width; 628 size.height = attachment.height; 629 if ( media.view.settings.contentWidth ) { 630 resize.width = media.view.settings.contentWidth - 22; 631 resize.height = Math.ceil( ( size.height * resize.width ) / size.width ); 632 if ( ! options.width ) { 633 options.width = resize.width; 634 options.height = resize.height; 635 } 636 } else { 637 if ( ! options.width ) { 638 options.width = attachment.width; 639 options.height = attachment.height; 640 } 641 } 642 track.dimensions = { 643 original : size, 644 resized : _.isEmpty( resize ) ? size : resize 645 }; 646 } else { 647 options.width = 400; 648 } 649 650 track.image = attachment.image; 651 track.thumb = attachment.thumb; 652 653 tracks.push( track ); 654 } ); 655 656 options.tracks = tracks; 657 this.data = options; 658 659 return this.template( options ); 660 }, 661 662 unbind: function() { 663 this.unsetPlayers(); 664 } 665 }); 666 _.extend( wp.mce.media.PlaylistView.prototype, wp.media.mixin ); 499 View: _.extend( {}, wp.mce.av.View, { 500 template: media.template( 'editor-audio' ) 501 } ) 502 } ) ); 667 503 668 504 /** 669 505 * TinyMCE handler for the playlist shortcode 670 506 * 671 * @mixes wp.mce. media507 * @mixes wp.mce.av 672 508 */ 673 wp.mce. playlist = _.extend( {}, wp.mce.media, {509 wp.mce.views.register( 'playlist', _.extend( {}, wp.mce.av, { 674 510 shortcode: 'playlist', 675 511 state: ['playlist-edit', 'video-playlist-edit'], 676 View: wp.mce.media.PlaylistView 677 } ); 678 wp.mce.views.register( 'playlist', wp.mce.playlist ); 512 View: _.extend( {}, wp.media.mixin, { 513 template: media.template( 'editor-playlist' ), 514 515 initialize: function( options ) { 516 this.players = []; 517 this.data = {}; 518 this.attachments = []; 519 this.shortcode = options.shortcode; 520 521 $( 'body' ).on( 'click', '.wp-switch-editor', this.pausePlayers ); 522 $( document ).on( 'media:edit', this.pausePlayers ); 523 524 this.fetch(); 525 526 $( this ).on( 'ready', this.setPlaylist ); 527 }, 528 529 /** 530 * Asynchronously fetch the shortcode's attachments 531 */ 532 fetch: function() { 533 this.attachments = wp.media.playlist.attachments( this.shortcode ); 534 this.dfd = this.attachments.more().done( _.bind( this.render, this ) ); 535 }, 536 537 setPlaylist: function( event, element ) { 538 if ( ! this.data.tracks ) { 539 return; 540 } 541 542 this.players.push( new WPPlaylistView( { 543 el: $( element ).find( '.wp-playlist' ).get( 0 ), 544 metadata: this.data 545 } ).player ); 546 }, 547 548 /** 549 * Set the data that will be used to compile the Underscore template, 550 * compile the template, and then return it. 551 * 552 * @returns {string} 553 */ 554 getHtml: function() { 555 var data = this.shortcode.attrs.named, 556 model = wp.media.playlist, 557 options, 558 attachments, 559 tracks = []; 560 561 // Don't render errors while still fetching attachments 562 if ( this.dfd && 'pending' === this.dfd.state() && ! this.attachments.length ) { 563 return; 564 } 565 566 _.each( model.defaults, function( value, key ) { 567 data[ key ] = model.coerce( data, key ); 568 }); 569 570 options = { 571 type: data.type, 572 style: data.style, 573 tracklist: data.tracklist, 574 tracknumbers: data.tracknumbers, 575 images: data.images, 576 artists: data.artists 577 }; 578 579 if ( ! this.attachments.length ) { 580 return this.template( options ); 581 } 582 583 attachments = this.attachments.toJSON(); 584 585 _.each( attachments, function( attachment ) { 586 var size = {}, resize = {}, track = { 587 src : attachment.url, 588 type : attachment.mime, 589 title : attachment.title, 590 caption : attachment.caption, 591 description : attachment.description, 592 meta : attachment.meta 593 }; 594 595 if ( 'video' === data.type ) { 596 size.width = attachment.width; 597 size.height = attachment.height; 598 if ( media.view.settings.contentWidth ) { 599 resize.width = media.view.settings.contentWidth - 22; 600 resize.height = Math.ceil( ( size.height * resize.width ) / size.width ); 601 if ( ! options.width ) { 602 options.width = resize.width; 603 options.height = resize.height; 604 } 605 } else { 606 if ( ! options.width ) { 607 options.width = attachment.width; 608 options.height = attachment.height; 609 } 610 } 611 track.dimensions = { 612 original : size, 613 resized : _.isEmpty( resize ) ? size : resize 614 }; 615 } else { 616 options.width = 400; 617 } 618 619 track.image = attachment.image; 620 track.thumb = attachment.thumb; 621 622 tracks.push( track ); 623 } ); 624 625 options.tracks = tracks; 626 this.data = options; 627 628 return this.template( options ); 629 }, 630 631 unbind: function() { 632 this.unsetPlayers(); 633 } 634 } ) 635 } ) ); 679 636 680 637 /** 681 638 * TinyMCE handler for the embed shortcode 682 639 */ 683 wp.mce. embed ={640 wp.mce.views.register( 'embed', { 684 641 shortcode: 'embed', 685 toView: wp.mce.gallery.toView, 686 View: wp.mce.View.extend( { 687 className: 'editor-embed', 642 View: _.extend( {}, wp.media.mixin, { 688 643 template: media.template( 'editor-embed' ), 689 644 initialize: function( options ) { … … 738 693 } ), 739 694 edit: function() {} 740 }; 741 742 _.extend( wp.mce.embed.View.prototype, wp.media.mixin ); 743 744 wp.mce.views.register( 'embed', wp.mce.embed ); 695 } ); 745 696 746 697 }(jQuery));
Note: See TracChangeset
for help on using the changeset viewer.