Ticket #40894: 40894.6.diff
File 40894.6.diff, 530.0 KB (added by , 7 years ago) |
---|
-
.jshintrc
diff --git .jshintrc .jshintrc index 0a082dded5..278eac22c3 100644
21 21 "Backbone": false, 22 22 "jQuery": false, 23 23 "JSON": false, 24 "wp": false 24 "wp": false, 25 "export": false, 26 "module": false, 27 "require": false 25 28 } 26 29 } -
Gruntfile.js
diff --git Gruntfile.js Gruntfile.js index 199f9faa2c..3cf55b2c93 100644
1 1 /* jshint node:true */ 2 var webpackConfig = require( './webpack.config' ); 3 var webpackDevConfig = require( './webpack-dev.config' ); 4 2 5 module.exports = function(grunt) { 3 6 var path = require('path'), 4 7 fs = require( 'fs' ), 5 8 SOURCE_DIR = 'src/', 6 9 BUILD_DIR = 'build/', 7 10 BANNER_TEXT = '/*! This file is auto-generated */', 8 autoprefixer = require('autoprefixer'), 9 mediaConfig = {}, 10 mediaBuilds = ['audiovideo', 'grid', 'models', 'views']; 11 autoprefixer = require( 'autoprefixer' ); 11 12 12 13 // Load tasks. 13 14 require('matchdep').filterDev(['grunt-*', '!grunt-legacy-util']).forEach( grunt.loadNpmTasks ); 14 15 // Load legacy utils 15 16 grunt.util = require('grunt-legacy-util'); 16 17 17 mediaBuilds.forEach( function ( build ) {18 var path = SOURCE_DIR + 'wp-includes/js/media';19 mediaConfig[ build ] = { files : {} };20 mediaConfig[ build ].files[ path + '-' + build + '.js' ] = [ path + '/' + build + '.manifest.js' ];21 } );22 23 18 // Project configuration. 24 19 grunt.initConfig({ 25 20 postcss: { … … module.exports = function(grunt) { 175 170 } 176 171 } 177 172 }, 178 browserify: mediaConfig,179 173 sass: { 180 174 colors: { 181 175 expand: true, … … module.exports = function(grunt) { 336 330 ] 337 331 }, 338 332 media: { 339 options: {340 browserify: true341 },342 333 src: [ 343 334 SOURCE_DIR + 'wp-includes/js/media/**/*.js' 344 335 ] … … module.exports = function(grunt) { 548 539 dest: SOURCE_DIR + 'wp-includes/js/jquery/jquery.masonry.min.js' 549 540 } 550 541 }, 551 542 webpack: { 543 prod: webpackConfig, 544 dev: webpackDevConfig 545 }, 552 546 concat: { 553 547 tinymce: { 554 548 options: { … … module.exports = function(grunt) { 731 725 } 732 726 }, 733 727 config: { 734 files: 'Gruntfile.js' 728 files: [ 729 'Gruntfile.js', 730 'webpack-dev.config.js', 731 'webpack.config.js' 732 ] 735 733 }, 736 734 colors: { 737 735 files: [SOURCE_DIR + 'wp-admin/css/colors/**'], … … module.exports = function(grunt) { 769 767 770 768 // Register tasks. 771 769 770 // Webpack task. 771 grunt.loadNpmTasks( 'grunt-webpack' ); 772 772 773 // RTL task. 773 774 grunt.registerTask('rtl', ['rtlcss:core', 'rtlcss:colors']); 774 775 … … module.exports = function(grunt) { 792 793 grunt.renameTask( 'watch', '_watch' ); 793 794 794 795 grunt.registerTask( 'watch', function() { 795 if ( ! this.args.length || this.args.indexOf( 'browserify' ) > -1 ) { 796 grunt.config( 'browserify.options', { 797 browserifyOptions: { 798 debug: true 799 }, 800 watch: true 801 } ); 796 if ( ! this.args.length || this.args.indexOf( 'webpack' ) > -1 ) { 802 797 803 grunt.task.run( ' browserify' );798 grunt.task.run( 'webpack:dev' ); 804 799 } 805 800 806 801 grunt.task.run( '_' + this.nameArgs ); … … module.exports = function(grunt) { 811 806 ] ); 812 807 813 808 grunt.registerTask( 'precommit:js', [ 814 ' browserify',809 'webpack:prod', 815 810 'jshint:corejs', 816 811 'uglify:masonry', 817 812 'qunit:compiled' … … module.exports = function(grunt) { 879 874 return regex.test( result.stdout ); 880 875 } 881 876 882 if ( [ 'package.json', 'Gruntfile.js' ].some( testPath ) ) {877 if ( [ 'package.json', 'Gruntfile.js', 'webpack.config.js' ].some( testPath ) ) { 883 878 grunt.log.writeln( 'Configuration files modified. Running `prerelease`.' ); 884 879 taskList.push( 'prerelease' ); 885 880 } else { … … module.exports = function(grunt) { 977 972 grunt.event.on('watch', function( action, filepath, target ) { 978 973 var src; 979 974 980 if ( [ 'all', 'rtl', ' browserify' ].indexOf( target ) === -1 ) {975 if ( [ 'all', 'rtl', 'webpack' ].indexOf( target ) === -1 ) { 981 976 return; 982 977 } 983 978 -
package.json
diff --git package.json package.json index 71695dfb77..4e1655c099 100644
15 15 "autoprefixer": "^6.5.1", 16 16 "grunt": "~0.4.5", 17 17 "grunt-banner": "^0.6.0", 18 "grunt-browserify": "~5.0.0",19 18 "grunt-contrib-clean": "~1.0.0", 20 19 "grunt-contrib-compress": "~1.3.0", 21 20 "grunt-contrib-concat": "~1.0.0", … … 36 35 "grunt-rtlcss": "~2.0.1", 37 36 "grunt-sass": "~1.2.1", 38 37 "ink-docstrap": "^1.3.0", 39 "matchdep": "~1.0.0" 38 "grunt-webpack": "^3.0.2", 39 "matchdep": "~1.0.0", 40 "webpack": "^3.5.4", 41 "webpack-dev-server": "^2.7.1" 40 42 } 41 43 } -
deleted file src/wp-content/plugins/hello.php
diff --git src/wp-content/plugins/hello.php src/wp-content/plugins/hello.php deleted file mode 100644 index 2b1e07b59e..0000000000
+ - 1 <?php2 /**3 * @package Hello_Dolly4 * @version 1.65 */6 /*7 Plugin Name: Hello Dolly8 Plugin URI: http://wordpress.org/plugins/hello-dolly/9 Description: This is not just a plugin, it symbolizes the hope and enthusiasm of an entire generation summed up in two words sung most famously by Louis Armstrong: Hello, Dolly. When activated you will randomly see a lyric from <cite>Hello, Dolly</cite> in the upper right of your admin screen on every page.10 Author: Matt Mullenweg11 Version: 1.612 Author URI: http://ma.tt/13 */14 15 function hello_dolly_get_lyric() {16 /** These are the lyrics to Hello Dolly */17 $lyrics = "Hello, Dolly18 Well, hello, Dolly19 It's so nice to have you back where you belong20 You're lookin' swell, Dolly21 I can tell, Dolly22 You're still glowin', you're still crowin'23 You're still goin' strong24 We feel the room swayin'25 While the band's playin'26 One of your old favourite songs from way back when27 So, take her wrap, fellas28 Find her an empty lap, fellas29 Dolly'll never go away again30 Hello, Dolly31 Well, hello, Dolly32 It's so nice to have you back where you belong33 You're lookin' swell, Dolly34 I can tell, Dolly35 You're still glowin', you're still crowin'36 You're still goin' strong37 We feel the room swayin'38 While the band's playin'39 One of your old favourite songs from way back when40 Golly, gee, fellas41 Find her a vacant knee, fellas42 Dolly'll never go away43 Dolly'll never go away44 Dolly'll never go away again";45 46 // Here we split it into lines47 $lyrics = explode( "\n", $lyrics );48 49 // And then randomly choose a line50 return wptexturize( $lyrics[ mt_rand( 0, count( $lyrics ) - 1 ) ] );51 }52 53 // This just echoes the chosen line, we'll position it later54 function hello_dolly() {55 $chosen = hello_dolly_get_lyric();56 echo "<p id='dolly'>$chosen</p>";57 }58 59 // Now we set that function up to execute when the admin_notices action is called60 add_action( 'admin_notices', 'hello_dolly' );61 62 // We need some CSS to position the paragraph63 function dolly_css() {64 // This makes sure that the positioning is also good for right-to-left languages65 $x = is_rtl() ? 'left' : 'right';66 67 echo "68 <style type='text/css'>69 #dolly {70 float: $x;71 padding-$x: 15px;72 padding-top: 5px;73 margin: 0;74 font-size: 11px;75 }76 </style>77 ";78 }79 80 add_action( 'admin_head', 'dolly_css' );81 82 ?> -
deleted file src/wp-content/plugins/index.php
diff --git src/wp-content/plugins/index.php src/wp-content/plugins/index.php deleted file mode 100644 index 62200328fd..0000000000
+ - 1 <?php2 // Silence is golden. -
src/wp-includes/js/media-audiovideo.js
diff --git src/wp-includes/js/media-audiovideo.js src/wp-includes/js/media-audiovideo.js index 0a819747d2..cb2abd57ae 100644
1 (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ 1 /******/ (function(modules) { // webpackBootstrap 2 /******/ // The module cache 3 /******/ var installedModules = {}; 4 /******/ 5 /******/ // The require function 6 /******/ function __webpack_require__(moduleId) { 7 /******/ 8 /******/ // Check if module is in cache 9 /******/ if(installedModules[moduleId]) { 10 /******/ return installedModules[moduleId].exports; 11 /******/ } 12 /******/ // Create a new module (and put it into the cache) 13 /******/ var module = installedModules[moduleId] = { 14 /******/ i: moduleId, 15 /******/ l: false, 16 /******/ exports: {} 17 /******/ }; 18 /******/ 19 /******/ // Execute the module function 20 /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); 21 /******/ 22 /******/ // Flag the module as loaded 23 /******/ module.l = true; 24 /******/ 25 /******/ // Return the exports of the module 26 /******/ return module.exports; 27 /******/ } 28 /******/ 29 /******/ 30 /******/ // expose the modules object (__webpack_modules__) 31 /******/ __webpack_require__.m = modules; 32 /******/ 33 /******/ // expose the module cache 34 /******/ __webpack_require__.c = installedModules; 35 /******/ 36 /******/ // define getter function for harmony exports 37 /******/ __webpack_require__.d = function(exports, name, getter) { 38 /******/ if(!__webpack_require__.o(exports, name)) { 39 /******/ Object.defineProperty(exports, name, { 40 /******/ configurable: false, 41 /******/ enumerable: true, 42 /******/ get: getter 43 /******/ }); 44 /******/ } 45 /******/ }; 46 /******/ 47 /******/ // getDefaultExport function for compatibility with non-harmony modules 48 /******/ __webpack_require__.n = function(module) { 49 /******/ var getter = module && module.__esModule ? 50 /******/ function getDefault() { return module['default']; } : 51 /******/ function getModuleExports() { return module; }; 52 /******/ __webpack_require__.d(getter, 'a', getter); 53 /******/ return getter; 54 /******/ }; 55 /******/ 56 /******/ // Object.prototype.hasOwnProperty.call 57 /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; 58 /******/ 59 /******/ // __webpack_public_path__ 60 /******/ __webpack_require__.p = ""; 61 /******/ 62 /******/ // Load entry module and return exports 63 /******/ return __webpack_require__(__webpack_require__.s = 0); 64 /******/ }) 65 /************************************************************************/ 66 /******/ ([ 67 /* 0 */ 68 /***/ (function(module, exports, __webpack_require__) { 69 2 70 var media = wp.media, 3 71 baseSettings = window._wpmejsSettings || {}, 4 72 l10n = window._wpMediaViewsL10n || {}; … … wp.media.video = { 268 336 } 269 337 }; 270 338 271 media.model.PostMedia = require( './models/post-media.js' ); 272 media.controller.AudioDetails = require( './controllers/audio-details.js' ); 273 media.controller.VideoDetails = require( './controllers/video-details.js' ); 274 media.view.MediaFrame.MediaDetails = require( './views/frame/media-details.js' ); 275 media.view.MediaFrame.AudioDetails = require( './views/frame/audio-details.js' ); 276 media.view.MediaFrame.VideoDetails = require( './views/frame/video-details.js' ); 277 media.view.MediaDetails = require( './views/media-details.js' ); 278 media.view.AudioDetails = require( './views/audio-details.js' ); 279 media.view.VideoDetails = require( './views/video-details.js' ); 280 281 },{"./controllers/audio-details.js":2,"./controllers/video-details.js":3,"./models/post-media.js":4,"./views/audio-details.js":5,"./views/frame/audio-details.js":6,"./views/frame/media-details.js":7,"./views/frame/video-details.js":8,"./views/media-details.js":9,"./views/video-details.js":10}],2:[function(require,module,exports){ 282 var State = wp.media.controller.State, 283 l10n = wp.media.view.l10n, 284 AudioDetails; 285 286 /** 287 * wp.media.controller.AudioDetails 288 * 289 * The controller for the Audio Details state 290 * 291 * @memberOf wp.media.controller 292 * 293 * @class 294 * @augments wp.media.controller.State 295 * @augments Backbone.Model 296 */ 297 AudioDetails = State.extend(/** @lends wp.media.controller.AudioDetails.prototype */{ 298 defaults: { 299 id: 'audio-details', 300 toolbar: 'audio-details', 301 title: l10n.audioDetailsTitle, 302 content: 'audio-details', 303 menu: 'audio-details', 304 router: false, 305 priority: 60 306 }, 307 308 initialize: function( options ) { 309 this.media = options.media; 310 State.prototype.initialize.apply( this, arguments ); 311 } 312 }); 313 314 module.exports = AudioDetails; 315 316 },{}],3:[function(require,module,exports){ 317 /** 318 * wp.media.controller.VideoDetails 319 * 320 * The controller for the Video Details state 321 * 322 * @memberOf wp.media.controller 323 * 324 * @class 325 * @augments wp.media.controller.State 326 * @augments Backbone.Model 327 */ 328 var State = wp.media.controller.State, 329 l10n = wp.media.view.l10n, 330 VideoDetails; 331 332 VideoDetails = State.extend(/** @lends wp.media.controller.VideoDetails.prototype */{ 333 defaults: { 334 id: 'video-details', 335 toolbar: 'video-details', 336 title: l10n.videoDetailsTitle, 337 content: 'video-details', 338 menu: 'video-details', 339 router: false, 340 priority: 60 341 }, 339 media.model.PostMedia = __webpack_require__( 1 ); 340 media.controller.AudioDetails = __webpack_require__( 2 ); 341 media.controller.VideoDetails = __webpack_require__( 3 ); 342 media.view.MediaFrame.MediaDetails = __webpack_require__( 4 ); 343 media.view.MediaFrame.AudioDetails = __webpack_require__( 5 ); 344 media.view.MediaFrame.VideoDetails = __webpack_require__( 6 ); 345 media.view.MediaDetails = __webpack_require__( 7 ); 346 media.view.AudioDetails = __webpack_require__( 8 ); 347 media.view.VideoDetails = __webpack_require__( 9 ); 342 348 343 initialize: function( options ) {344 this.media = options.media;345 State.prototype.initialize.apply( this, arguments );346 }347 });348 349 349 module.exports = VideoDetails; 350 /***/ }), 351 /* 1 */ 352 /***/ (function(module, exports) { 350 353 351 },{}],4:[function(require,module,exports){352 354 /** 353 355 * wp.media.model.PostMedia 354 356 * … … var PostMedia = Backbone.Model.extend(/** @lends wp.media.model.PostMedia.protot 392 394 393 395 module.exports = PostMedia; 394 396 395 },{}],5:[function(require,module,exports){ 396 var MediaDetails = wp.media.view.MediaDetails, 397 398 /***/ }), 399 /* 2 */ 400 /***/ (function(module, exports) { 401 402 var State = wp.media.controller.State, 403 l10n = wp.media.view.l10n, 397 404 AudioDetails; 398 405 399 406 /** 400 * wp.media. view.AudioDetails407 * wp.media.controller.AudioDetails 401 408 * 402 * @memberOf wp.media.view 409 * The controller for the Audio Details state 410 * 411 * @memberOf wp.media.controller 403 412 * 404 413 * @class 405 * @augments wp.media.view.MediaDetails 406 * @augments wp.media.view.Settings.AttachmentDisplay 407 * @augments wp.media.view.Settings 408 * @augments wp.media.View 409 * @augments wp.Backbone.View 410 * @augments Backbone.View 414 * @augments wp.media.controller.State 415 * @augments Backbone.Model 411 416 */ 412 AudioDetails = MediaDetails.extend(/** @lends wp.media.view.AudioDetails.prototype */{ 413 className: 'audio-details', 414 template: wp.template('audio-details'), 415 416 setMedia: function() { 417 var audio = this.$('.wp-audio-shortcode'); 418 419 if ( audio.find( 'source' ).length ) { 420 if ( audio.is(':hidden') ) { 421 audio.show(); 422 } 423 this.media = MediaDetails.prepareSrc( audio.get(0) ); 424 } else { 425 audio.hide(); 426 this.media = false; 427 } 417 AudioDetails = State.extend(/** @lends wp.media.controller.AudioDetails.prototype */{ 418 defaults: { 419 id: 'audio-details', 420 toolbar: 'audio-details', 421 title: l10n.audioDetailsTitle, 422 content: 'audio-details', 423 menu: 'audio-details', 424 router: false, 425 priority: 60 426 }, 428 427 429 return this; 428 initialize: function( options ) { 429 this.media = options.media; 430 State.prototype.initialize.apply( this, arguments ); 430 431 } 431 432 }); 432 433 433 434 module.exports = AudioDetails; 434 435 435 },{}],6:[function(require,module,exports){436 var MediaDetails = wp.media.view.MediaFrame.MediaDetails,437 MediaLibrary = wp.media.controller.MediaLibrary,438 436 439 l10n = wp.media.view.l10n, 440 AudioDetails; 437 /***/ }), 438 /* 3 */ 439 /***/ (function(module, exports) { 441 440 442 441 /** 443 * wp.media. view.MediaFrame.AudioDetails442 * wp.media.controller.VideoDetails 444 443 * 445 * @memberOf wp.media.view.MediaFrame 444 * The controller for the Video Details state 445 * 446 * @memberOf wp.media.controller 446 447 * 447 448 * @class 448 * @augments wp.media.view.MediaFrame.MediaDetails 449 * @augments wp.media.view.MediaFrame.Select 450 * @augments wp.media.view.MediaFrame 451 * @augments wp.media.view.Frame 452 * @augments wp.media.View 453 * @augments wp.Backbone.View 454 * @augments Backbone.View 455 * @mixes wp.media.controller.StateMachine 449 * @augments wp.media.controller.State 450 * @augments Backbone.Model 456 451 */ 457 AudioDetails = MediaDetails.extend(/** @lends wp.media.view.MediaFrame.AudioDetails.prototype */{ 452 var State = wp.media.controller.State, 453 l10n = wp.media.view.l10n, 454 VideoDetails; 455 456 VideoDetails = State.extend(/** @lends wp.media.controller.VideoDetails.prototype */{ 458 457 defaults: { 459 id: 'audio', 460 url: '', 461 menu: 'audio-details', 462 content: 'audio-details', 463 toolbar: 'audio-details', 464 type: 'link', 465 title: l10n.audioDetailsTitle, 466 priority: 120 458 id: 'video-details', 459 toolbar: 'video-details', 460 title: l10n.videoDetailsTitle, 461 content: 'video-details', 462 menu: 'video-details', 463 router: false, 464 priority: 60 467 465 }, 468 466 469 467 initialize: function( options ) { 470 options.DetailsView = wp.media.view.AudioDetails; 471 options.cancelText = l10n.audioDetailsCancel; 472 options.addText = l10n.audioAddSourceTitle; 473 474 MediaDetails.prototype.initialize.call( this, options ); 475 }, 476 477 bindHandlers: function() { 478 MediaDetails.prototype.bindHandlers.apply( this, arguments ); 479 480 this.on( 'toolbar:render:replace-audio', this.renderReplaceToolbar, this ); 481 this.on( 'toolbar:render:add-audio-source', this.renderAddSourceToolbar, this ); 482 }, 483 484 createStates: function() { 485 this.states.add([ 486 new wp.media.controller.AudioDetails( { 487 media: this.media 488 } ), 489 490 new MediaLibrary( { 491 type: 'audio', 492 id: 'replace-audio', 493 title: l10n.audioReplaceTitle, 494 toolbar: 'replace-audio', 495 media: this.media, 496 menu: 'audio-details' 497 } ), 498 499 new MediaLibrary( { 500 type: 'audio', 501 id: 'add-audio-source', 502 title: l10n.audioAddSourceTitle, 503 toolbar: 'add-audio-source', 504 media: this.media, 505 menu: false 506 } ) 507 ]); 468 this.media = options.media; 469 State.prototype.initialize.apply( this, arguments ); 508 470 } 509 471 }); 510 472 511 module.exports = AudioDetails; 473 module.exports = VideoDetails; 474 475 476 /***/ }), 477 /* 4 */ 478 /***/ (function(module, exports) { 512 479 513 },{}],7:[function(require,module,exports){514 480 var Select = wp.media.view.MediaFrame.Select, 515 481 l10n = wp.media.view.l10n, 516 482 MediaDetails; … … MediaDetails = Select.extend(/** @lends wp.media.view.MediaFrame.MediaDetails.pr 642 608 643 609 module.exports = MediaDetails; 644 610 645 },{}],8:[function(require,module,exports){ 611 612 /***/ }), 613 /* 5 */ 614 /***/ (function(module, exports) { 615 616 var MediaDetails = wp.media.view.MediaFrame.MediaDetails, 617 MediaLibrary = wp.media.controller.MediaLibrary, 618 619 l10n = wp.media.view.l10n, 620 AudioDetails; 621 622 /** 623 * wp.media.view.MediaFrame.AudioDetails 624 * 625 * @memberOf wp.media.view.MediaFrame 626 * 627 * @class 628 * @augments wp.media.view.MediaFrame.MediaDetails 629 * @augments wp.media.view.MediaFrame.Select 630 * @augments wp.media.view.MediaFrame 631 * @augments wp.media.view.Frame 632 * @augments wp.media.View 633 * @augments wp.Backbone.View 634 * @augments Backbone.View 635 * @mixes wp.media.controller.StateMachine 636 */ 637 AudioDetails = MediaDetails.extend(/** @lends wp.media.view.MediaFrame.AudioDetails.prototype */{ 638 defaults: { 639 id: 'audio', 640 url: '', 641 menu: 'audio-details', 642 content: 'audio-details', 643 toolbar: 'audio-details', 644 type: 'link', 645 title: l10n.audioDetailsTitle, 646 priority: 120 647 }, 648 649 initialize: function( options ) { 650 options.DetailsView = wp.media.view.AudioDetails; 651 options.cancelText = l10n.audioDetailsCancel; 652 options.addText = l10n.audioAddSourceTitle; 653 654 MediaDetails.prototype.initialize.call( this, options ); 655 }, 656 657 bindHandlers: function() { 658 MediaDetails.prototype.bindHandlers.apply( this, arguments ); 659 660 this.on( 'toolbar:render:replace-audio', this.renderReplaceToolbar, this ); 661 this.on( 'toolbar:render:add-audio-source', this.renderAddSourceToolbar, this ); 662 }, 663 664 createStates: function() { 665 this.states.add([ 666 new wp.media.controller.AudioDetails( { 667 media: this.media 668 } ), 669 670 new MediaLibrary( { 671 type: 'audio', 672 id: 'replace-audio', 673 title: l10n.audioReplaceTitle, 674 toolbar: 'replace-audio', 675 media: this.media, 676 menu: 'audio-details' 677 } ), 678 679 new MediaLibrary( { 680 type: 'audio', 681 id: 'add-audio-source', 682 title: l10n.audioAddSourceTitle, 683 toolbar: 'add-audio-source', 684 media: this.media, 685 menu: false 686 } ) 687 ]); 688 } 689 }); 690 691 module.exports = AudioDetails; 692 693 694 /***/ }), 695 /* 6 */ 696 /***/ (function(module, exports) { 697 646 698 var MediaDetails = wp.media.view.MediaFrame.MediaDetails, 647 699 MediaLibrary = wp.media.controller.MediaLibrary, 648 700 l10n = wp.media.view.l10n, … … VideoDetails = MediaDetails.extend(/** @lends wp.media.view.MediaFrame.VideoDeta 779 831 780 832 module.exports = VideoDetails; 781 833 782 },{}],9:[function(require,module,exports){ 834 835 /***/ }), 836 /* 7 */ 837 /***/ (function(module, exports) { 838 783 839 /* global MediaElementPlayer */ 784 840 var AttachmentDisplay = wp.media.view.Settings.AttachmentDisplay, 785 841 $ = jQuery, … … MediaDetails = AttachmentDisplay.extend(/** @lends wp.media.view.MediaDetails.pr 949 1005 950 1006 module.exports = MediaDetails; 951 1007 952 },{}],10:[function(require,module,exports){ 1008 1009 /***/ }), 1010 /* 8 */ 1011 /***/ (function(module, exports) { 1012 1013 var MediaDetails = wp.media.view.MediaDetails, 1014 AudioDetails; 1015 1016 /** 1017 * wp.media.view.AudioDetails 1018 * 1019 * @memberOf wp.media.view 1020 * 1021 * @class 1022 * @augments wp.media.view.MediaDetails 1023 * @augments wp.media.view.Settings.AttachmentDisplay 1024 * @augments wp.media.view.Settings 1025 * @augments wp.media.View 1026 * @augments wp.Backbone.View 1027 * @augments Backbone.View 1028 */ 1029 AudioDetails = MediaDetails.extend(/** @lends wp.media.view.AudioDetails.prototype */{ 1030 className: 'audio-details', 1031 template: wp.template('audio-details'), 1032 1033 setMedia: function() { 1034 var audio = this.$('.wp-audio-shortcode'); 1035 1036 if ( audio.find( 'source' ).length ) { 1037 if ( audio.is(':hidden') ) { 1038 audio.show(); 1039 } 1040 this.media = MediaDetails.prepareSrc( audio.get(0) ); 1041 } else { 1042 audio.hide(); 1043 this.media = false; 1044 } 1045 1046 return this; 1047 } 1048 }); 1049 1050 module.exports = AudioDetails; 1051 1052 1053 /***/ }), 1054 /* 9 */ 1055 /***/ (function(module, exports) { 1056 953 1057 var MediaDetails = wp.media.view.MediaDetails, 954 1058 VideoDetails; 955 1059 … … VideoDetails = MediaDetails.extend(/** @lends wp.media.view.VideoDetails.prototy 994 1098 995 1099 module.exports = VideoDetails; 996 1100 997 },{}]},{},[1]); 1101 1102 /***/ }) 1103 /******/ ]); 1104 No newline at end of file -
src/wp-includes/js/media-grid.js
diff --git src/wp-includes/js/media-grid.js src/wp-includes/js/media-grid.js index 6e35fb52a6..c9babd6d50 100644
1 (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ 1 /******/ (function(modules) { // webpackBootstrap 2 /******/ // The module cache 3 /******/ var installedModules = {}; 4 /******/ 5 /******/ // The require function 6 /******/ function __webpack_require__(moduleId) { 7 /******/ 8 /******/ // Check if module is in cache 9 /******/ if(installedModules[moduleId]) { 10 /******/ return installedModules[moduleId].exports; 11 /******/ } 12 /******/ // Create a new module (and put it into the cache) 13 /******/ var module = installedModules[moduleId] = { 14 /******/ i: moduleId, 15 /******/ l: false, 16 /******/ exports: {} 17 /******/ }; 18 /******/ 19 /******/ // Execute the module function 20 /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); 21 /******/ 22 /******/ // Flag the module as loaded 23 /******/ module.l = true; 24 /******/ 25 /******/ // Return the exports of the module 26 /******/ return module.exports; 27 /******/ } 28 /******/ 29 /******/ 30 /******/ // expose the modules object (__webpack_modules__) 31 /******/ __webpack_require__.m = modules; 32 /******/ 33 /******/ // expose the module cache 34 /******/ __webpack_require__.c = installedModules; 35 /******/ 36 /******/ // define getter function for harmony exports 37 /******/ __webpack_require__.d = function(exports, name, getter) { 38 /******/ if(!__webpack_require__.o(exports, name)) { 39 /******/ Object.defineProperty(exports, name, { 40 /******/ configurable: false, 41 /******/ enumerable: true, 42 /******/ get: getter 43 /******/ }); 44 /******/ } 45 /******/ }; 46 /******/ 47 /******/ // getDefaultExport function for compatibility with non-harmony modules 48 /******/ __webpack_require__.n = function(module) { 49 /******/ var getter = module && module.__esModule ? 50 /******/ function getDefault() { return module['default']; } : 51 /******/ function getModuleExports() { return module; }; 52 /******/ __webpack_require__.d(getter, 'a', getter); 53 /******/ return getter; 54 /******/ }; 55 /******/ 56 /******/ // Object.prototype.hasOwnProperty.call 57 /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; 58 /******/ 59 /******/ // __webpack_public_path__ 60 /******/ __webpack_require__.p = ""; 61 /******/ 62 /******/ // Load entry module and return exports 63 /******/ return __webpack_require__(__webpack_require__.s = 10); 64 /******/ }) 65 /************************************************************************/ 66 /******/ ([ 67 /* 0 */, 68 /* 1 */, 69 /* 2 */, 70 /* 3 */, 71 /* 4 */, 72 /* 5 */, 73 /* 6 */, 74 /* 7 */, 75 /* 8 */, 76 /* 9 */, 77 /* 10 */ 78 /***/ (function(module, exports, __webpack_require__) { 79 80 var media = wp.media; 81 82 media.controller.EditAttachmentMetadata = __webpack_require__( 11 ); 83 media.view.MediaFrame.Manage = __webpack_require__( 12 ); 84 media.view.Attachment.Details.TwoColumn = __webpack_require__( 13 ); 85 media.view.MediaFrame.Manage.Router = __webpack_require__( 14 ); 86 media.view.EditImage.Details = __webpack_require__( 15 ); 87 media.view.MediaFrame.EditAttachments = __webpack_require__( 16 ); 88 media.view.SelectModeToggleButton = __webpack_require__( 17 ); 89 media.view.DeleteSelectedButton = __webpack_require__( 18 ); 90 media.view.DeleteSelectedPermanentlyButton = __webpack_require__( 19 ); 91 92 93 /***/ }), 94 /* 11 */ 95 /***/ (function(module, exports) { 96 2 97 var l10n = wp.media.view.l10n, 3 98 EditAttachmentMetadata; 4 99 … … EditAttachmentMetadata = wp.media.controller.State.extend(/** @lends wp.media.co 28 123 29 124 module.exports = EditAttachmentMetadata; 30 125 31 },{}],2:[function(require,module,exports){32 var media = wp.media;33 126 34 media.controller.EditAttachmentMetadata = require( './controllers/edit-attachment-metadata.js' ); 35 media.view.MediaFrame.Manage = require( './views/frame/manage.js' ); 36 media.view.Attachment.Details.TwoColumn = require( './views/attachment/details-two-column.js' ); 37 media.view.MediaFrame.Manage.Router = require( './routers/manage.js' ); 38 media.view.EditImage.Details = require( './views/edit-image-details.js' ); 39 media.view.MediaFrame.EditAttachments = require( './views/frame/edit-attachments.js' ); 40 media.view.SelectModeToggleButton = require( './views/button/select-mode-toggle.js' ); 41 media.view.DeleteSelectedButton = require( './views/button/delete-selected.js' ); 42 media.view.DeleteSelectedPermanentlyButton = require( './views/button/delete-selected-permanently.js' ); 43 44 },{"./controllers/edit-attachment-metadata.js":1,"./routers/manage.js":3,"./views/attachment/details-two-column.js":4,"./views/button/delete-selected-permanently.js":5,"./views/button/delete-selected.js":6,"./views/button/select-mode-toggle.js":7,"./views/edit-image-details.js":8,"./views/frame/edit-attachments.js":9,"./views/frame/manage.js":10}],3:[function(require,module,exports){ 127 /***/ }), 128 /* 12 */ 129 /***/ (function(module, exports) { 130 131 var MediaFrame = wp.media.view.MediaFrame, 132 Library = wp.media.controller.Library, 133 134 $ = Backbone.$, 135 Manage; 136 45 137 /** 46 * wp.media.view.MediaFrame.Manage .Router138 * wp.media.view.MediaFrame.Manage 47 139 * 48 * A router for handling the browser history and application state.140 * A generic management frame workflow. 49 141 * 50 * @memberOf wp.media.view.MediaFrame.Manage 142 * Used in the media grid view. 143 * 144 * @memberOf wp.media.view.MediaFrame 51 145 * 52 146 * @class 53 * @augments Backbone.Router 147 * @augments wp.media.view.MediaFrame 148 * @augments wp.media.view.Frame 149 * @augments wp.media.View 150 * @augments wp.Backbone.View 151 * @augments Backbone.View 152 * @mixes wp.media.controller.StateMachine 54 153 */ 55 var Router = Backbone.Router.extend(/** @lends wp.media.view.MediaFrame.Manage.Router.prototype */{ 56 routes: { 57 'upload.php?item=:slug&mode=edit': 'editItem', 58 'upload.php?item=:slug': 'showItem', 59 'upload.php?search=:query': 'search', 60 'upload.php': 'reset' 61 }, 154 Manage = MediaFrame.extend(/** @lends wp.media.view.MediaFrame.Manage.prototype */{ 155 /** 156 * @constructs 157 */ 158 initialize: function() { 159 _.defaults( this.options, { 160 title: '', 161 modal: false, 162 selection: [], 163 library: {}, // Options hash for the query to the media library. 164 multiple: 'add', 165 state: 'library', 166 uploader: true, 167 mode: [ 'grid', 'edit' ] 168 }); 62 169 63 // Map routes against the page URL 64 baseUrl: function( url ) { 65 return 'upload.php' + url; 66 }, 170 this.$body = $( document.body ); 171 this.$window = $( window ); 172 this.$adminBar = $( '#wpadminbar' ); 173 // Store the Add New button for later reuse in wp.media.view.UploaderInline. 174 this.$uploaderToggler = $( '.page-title-action' ) 175 .attr( 'aria-expanded', 'false' ) 176 .on( 'click', _.bind( this.addNewClickHandler, this ) ); 67 177 68 reset: function() { 69 var frame = wp.media.frames.edit; 178 this.$window.on( 'scroll resize', _.debounce( _.bind( this.fixPosition, this ), 15 ) ); 70 179 71 if ( frame ) { 72 frame.close(); 180 // Ensure core and media grid view UI is enabled. 181 this.$el.addClass('wp-core-ui'); 182 183 // Force the uploader off if the upload limit has been exceeded or 184 // if the browser isn't supported. 185 if ( wp.Uploader.limitExceeded || ! wp.Uploader.browser.supported ) { 186 this.options.uploader = false; 73 187 } 74 },75 188 76 // Respond to the search route by filling the search field and trigggering the input event 77 search: function( query ) { 78 jQuery( '#media-search-input' ).val( query ).trigger( 'input' ); 79 }, 189 // Initialize a window-wide uploader. 190 if ( this.options.uploader ) { 191 this.uploader = new wp.media.view.UploaderWindow({ 192 controller: this, 193 uploader: { 194 dropzone: document.body, 195 container: document.body 196 } 197 }).render(); 198 this.uploader.ready(); 199 $('body').append( this.uploader.el ); 80 200 81 // Show the modal with a specific item 82 showItem: function( query ) { 83 var media = wp.media, 84 frame = media.frames.browse, 85 library = frame.state().get('library'), 86 item; 201 this.options.uploader = false; 202 } 87 203 88 // Trigger the media frame to open the correct item 89 item = library.findWhere( { id: parseInt( query, 10 ) } ); 90 item.set( 'skipHistory', true ); 204 this.gridRouter = new wp.media.view.MediaFrame.Manage.Router(); 91 205 92 if ( item ) { 93 frame.trigger( 'edit:attachment', item ); 94 } else { 95 item = media.attachment( query ); 96 frame.listenTo( item, 'change', function( model ) { 97 frame.stopListening( item ); 98 frame.trigger( 'edit:attachment', model ); 99 } ); 100 item.fetch(); 101 } 102 }, 206 // Call 'initialize' directly on the parent class. 207 MediaFrame.prototype.initialize.apply( this, arguments ); 103 208 104 // Show the modal in edit mode with a specific item. 105 editItem: function( query ) { 106 this.showItem( query ); 107 wp.media.frames.edit.content.mode( 'edit-details' ); 108 } 109 }); 209 // Append the frame view directly the supplied container. 210 this.$el.appendTo( this.options.container ); 110 211 111 module.exports = Router; 212 this.createStates(); 213 this.bindRegionModeHandlers(); 214 this.render(); 215 this.bindSearchHandler(); 112 216 113 },{}],4:[function(require,module,exports){ 114 var Details = wp.media.view.Attachment.Details, 115 TwoColumn; 217 wp.media.frames.browse = this; 218 }, 116 219 117 /** 118 * wp.media.view.Attachment.Details.TwoColumn 119 * 120 * A similar view to media.view.Attachment.Details 121 * for use in the Edit Attachment modal. 122 * 123 * @memberOf wp.media.view.Attachment.Details 124 * 125 * @class 126 * @augments wp.media.view.Attachment.Details 127 * @augments wp.media.view.Attachment 128 * @augments wp.media.View 129 * @augments wp.Backbone.View 130 * @augments Backbone.View 131 */ 132 TwoColumn = Details.extend(/** @lends wp.media.view.Attachment.Details.TowColumn.prototype */{ 133 template: wp.template( 'attachment-details-two-column' ), 220 bindSearchHandler: function() { 221 var search = this.$( '#media-search-input' ), 222 searchView = this.browserView.toolbar.get( 'search' ).$el, 223 listMode = this.$( '.view-list' ), 134 224 135 initialize: function() { 136 this.controller.on( 'content:activate:edit-details', _.bind( this.editAttachment, this ) ); 225 input = _.throttle( function (e) { 226 var val = $( e.currentTarget ).val(), 227 url = ''; 137 228 138 Details.prototype.initialize.apply( this, arguments ); 139 }, 229 if ( val ) { 230 url += '?search=' + val; 231 this.gridRouter.navigate( this.gridRouter.baseUrl( url ), { replace: true } ); 232 } 233 }, 1000 ); 140 234 141 editAttachment: function( event ) { 142 if ( event ) { 143 event.preventDefault(); 144 } 145 this.controller.content.mode( 'edit-image' ); 235 // Update the URL when entering search string (at most once per second) 236 search.on( 'input', _.bind( input, this ) ); 237 238 this.gridRouter 239 .on( 'route:search', function () { 240 var href = window.location.href; 241 if ( href.indexOf( 'mode=' ) > -1 ) { 242 href = href.replace( /mode=[^&]+/g, 'mode=list' ); 243 } else { 244 href += href.indexOf( '?' ) > -1 ? '&mode=list' : '?mode=list'; 245 } 246 href = href.replace( 'search=', 's=' ); 247 listMode.prop( 'href', href ); 248 }) 249 .on( 'route:reset', function() { 250 searchView.val( '' ).trigger( 'input' ); 251 }); 146 252 }, 147 253 148 254 /** 149 * Noop this from parent class, doesn't apply here.255 * Create the default states for the frame. 150 256 */ 151 toggleSelectionHandler: function() {}, 152 153 render: function() { 154 Details.prototype.render.apply( this, arguments ); 257 createStates: function() { 258 var options = this.options; 155 259 156 wp.media.mixin.removeAllPlayers(); 157 this.$( 'audio, video' ).each( function (i, elem) { 158 var el = wp.media.view.MediaDetails.prepareSrc( elem ); 159 new window.MediaElementPlayer( el, wp.media.mixin.mejsSettings ); 160 } ); 161 } 162 }); 260 if ( this.options.states ) { 261 return; 262 } 163 263 164 module.exports = TwoColumn; 264 // Add the default states. 265 this.states.add([ 266 new Library({ 267 library: wp.media.query( options.library ), 268 multiple: options.multiple, 269 title: options.title, 270 content: 'browse', 271 toolbar: 'select', 272 contentUserSetting: false, 273 filterable: 'all', 274 autoSelect: false 275 }) 276 ]); 277 }, 165 278 166 },{}],5:[function(require,module,exports){ 167 var Button = wp.media.view.Button, 168 DeleteSelected = wp.media.view.DeleteSelectedButton, 169 DeleteSelectedPermanently; 279 /** 280 * Bind region mode activation events to proper handlers. 281 */ 282 bindRegionModeHandlers: function() { 283 this.on( 'content:create:browse', this.browseContent, this ); 170 284 171 /** 172 * wp.media.view.DeleteSelectedPermanentlyButton 173 * 174 * When MEDIA_TRASH is true, a button that handles bulk Delete Permanently logic 175 * 176 * @memberOf wp.media.view 177 * 178 * @class 179 * @augments wp.media.view.DeleteSelectedButton 180 * @augments wp.media.view.Button 181 * @augments wp.media.View 182 * @augments wp.Backbone.View 183 * @augments Backbone.View 184 */ 185 DeleteSelectedPermanently = DeleteSelected.extend(/** @lends wp.media.view.DeleteSelectedPermanentlyButton.prototype */{ 186 initialize: function() { 187 DeleteSelected.prototype.initialize.apply( this, arguments ); 188 this.controller.on( 'select:activate', this.selectActivate, this ); 189 this.controller.on( 'select:deactivate', this.selectDeactivate, this ); 190 }, 285 // Handle a frame-level event for editing an attachment. 286 this.on( 'edit:attachment', this.openEditAttachmentModal, this ); 191 287 192 filterChange: function( model ) {193 this. canShow = ( 'trash' === model.get( 'status' ));288 this.on( 'select:activate', this.bindKeydown, this ); 289 this.on( 'select:deactivate', this.unbindKeydown, this ); 194 290 }, 195 291 196 selectActivate: function() { 197 this.toggleDisabled(); 198 this.$el.toggleClass( 'hidden', ! this.canShow ); 292 handleKeydown: function( e ) { 293 if ( 27 === e.which ) { 294 e.preventDefault(); 295 this.deactivateMode( 'select' ).activateMode( 'edit' ); 296 } 199 297 }, 200 298 201 selectDeactivate: function() { 202 this.toggleDisabled(); 203 this.$el.addClass( 'hidden' ); 299 bindKeydown: function() { 300 this.$body.on( 'keydown.select', _.bind( this.handleKeydown, this ) ); 204 301 }, 205 302 206 render: function() { 207 Button.prototype.render.apply( this, arguments ); 208 this.selectActivate(); 209 return this; 210 } 211 }); 303 unbindKeydown: function() { 304 this.$body.off( 'keydown.select' ); 305 }, 212 306 213 module.exports = DeleteSelectedPermanently; 307 fixPosition: function() { 308 var $browser, $toolbar; 309 if ( ! this.isModeActive( 'select' ) ) { 310 return; 311 } 214 312 215 },{}],6:[function(require,module,exports){ 216 var Button = wp.media.view.Button, 217 l10n = wp.media.view.l10n, 218 DeleteSelected; 313 $browser = this.$('.attachments-browser'); 314 $toolbar = $browser.find('.media-toolbar'); 219 315 220 /** 221 * wp.media.view.DeleteSelectedButton 222 * 223 * A button that handles bulk Delete/Trash logic 224 * 225 * @memberOf wp.media.view 226 * 227 * @class 228 * @augments wp.media.view.Button 229 * @augments wp.media.View 230 * @augments wp.Backbone.View 231 * @augments Backbone.View 232 */ 233 DeleteSelected = Button.extend(/** @lends wp.media.view.DeleteSelectedButton.prototype */{ 234 initialize: function() { 235 Button.prototype.initialize.apply( this, arguments ); 236 if ( this.options.filters ) { 237 this.options.filters.model.on( 'change', this.filterChange, this ); 316 // Offset doesn't appear to take top margin into account, hence +16 317 if ( ( $browser.offset().top + 16 ) < this.$window.scrollTop() + this.$adminBar.height() ) { 318 $browser.addClass( 'fixed' ); 319 $toolbar.css('width', $browser.width() + 'px'); 320 } else { 321 $browser.removeClass( 'fixed' ); 322 $toolbar.css('width', ''); 238 323 } 239 this.controller.on( 'selection:toggle', this.toggleDisabled, this );240 324 }, 241 325 242 filterChange: function( model ) { 243 if ( 'trash' === model.get( 'status' ) ) { 244 this.model.set( 'text', l10n.untrashSelected ); 245 } else if ( wp.media.view.settings.mediaTrash ) { 246 this.model.set( 'text', l10n.trashSelected ); 326 /** 327 * Click handler for the `Add New` button. 328 */ 329 addNewClickHandler: function( event ) { 330 event.preventDefault(); 331 this.trigger( 'toggle:upload:attachment' ); 332 333 if ( this.uploader ) { 334 this.uploader.refresh(); 335 } 336 }, 337 338 /** 339 * Open the Edit Attachment modal. 340 */ 341 openEditAttachmentModal: function( model ) { 342 // Create a new EditAttachment frame, passing along the library and the attachment model. 343 if ( wp.media.frames.edit ) { 344 wp.media.frames.edit.open().trigger( 'refresh', model ); 247 345 } else { 248 this.model.set( 'text', l10n.deleteSelected ); 346 wp.media.frames.edit = wp.media( { 347 frame: 'edit-attachments', 348 controller: this, 349 library: this.state().get('library'), 350 model: model 351 } ); 249 352 } 250 353 }, 251 354 252 toggleDisabled: function() { 253 this.model.set( 'disabled', ! this.controller.state().get( 'selection' ).length ); 355 /** 356 * Create an attachments browser view within the content region. 357 * 358 * @param {Object} contentRegion Basic object with a `view` property, which 359 * should be set with the proper region view. 360 * @this wp.media.controller.Region 361 */ 362 browseContent: function( contentRegion ) { 363 var state = this.state(); 364 365 // Browse our library of attachments. 366 this.browserView = contentRegion.view = new wp.media.view.AttachmentsBrowser({ 367 controller: this, 368 collection: state.get('library'), 369 selection: state.get('selection'), 370 model: state, 371 sortable: state.get('sortable'), 372 search: state.get('searchable'), 373 filters: state.get('filterable'), 374 date: state.get('date'), 375 display: state.get('displaySettings'), 376 dragInfo: state.get('dragInfo'), 377 sidebar: 'errors', 378 379 suggestedWidth: state.get('suggestedWidth'), 380 suggestedHeight: state.get('suggestedHeight'), 381 382 AttachmentView: state.get('AttachmentView'), 383 384 scrollElement: document 385 }); 386 this.browserView.on( 'ready', _.bind( this.bindDeferred, this ) ); 387 388 this.errors = wp.Uploader.errors; 389 this.errors.on( 'add remove reset', this.sidebarVisibility, this ); 254 390 }, 255 391 256 render: function() { 257 Button.prototype.render.apply( this, arguments ); 258 if ( this.controller.isModeActive( 'select' ) ) { 259 this.$el.addClass( 'delete-selected-button' ); 260 } else { 261 this.$el.addClass( 'delete-selected-button hidden' ); 392 sidebarVisibility: function() { 393 this.browserView.$( '.media-sidebar' ).toggle( !! this.errors.length ); 394 }, 395 396 bindDeferred: function() { 397 if ( ! this.browserView.dfd ) { 398 return; 399 } 400 this.browserView.dfd.done( _.bind( this.startHistory, this ) ); 401 }, 402 403 startHistory: function() { 404 // Verify pushState support and activate 405 if ( window.history && window.history.pushState ) { 406 if ( Backbone.History.started ) { 407 Backbone.history.stop(); 408 } 409 Backbone.history.start( { 410 root: window._wpMediaGridSettings.adminUrl, 411 pushState: true 412 } ); 262 413 } 263 this.toggleDisabled();264 return this;265 414 } 266 415 }); 267 416 268 module.exports = DeleteSelected;417 module.exports = Manage; 269 418 270 },{}],7:[function(require,module,exports){271 419 272 var Button = wp.media.view.Button, 273 l10n = wp.media.view.l10n, 274 SelectModeToggle; 420 /***/ }), 421 /* 13 */ 422 /***/ (function(module, exports) { 423 424 var Details = wp.media.view.Attachment.Details, 425 TwoColumn; 275 426 276 427 /** 277 * wp.media.view. SelectModeToggleButton428 * wp.media.view.Attachment.Details.TwoColumn 278 429 * 279 * @memberOf wp.media.view 430 * A similar view to media.view.Attachment.Details 431 * for use in the Edit Attachment modal. 432 * 433 * @memberOf wp.media.view.Attachment.Details 280 434 * 281 435 * @class 282 * @augments wp.media.view.Button 436 * @augments wp.media.view.Attachment.Details 437 * @augments wp.media.view.Attachment 283 438 * @augments wp.media.View 284 439 * @augments wp.Backbone.View 285 440 * @augments Backbone.View 286 441 */ 287 SelectModeToggle = Button.extend(/** @lends wp.media.view.SelectModeToggle.prototype */{ 288 initialize: function() { 289 _.defaults( this.options, { 290 size : '' 291 } ); 442 TwoColumn = Details.extend(/** @lends wp.media.view.Attachment.Details.TowColumn.prototype */{ 443 template: wp.template( 'attachment-details-two-column' ), 292 444 293 Button.prototype.initialize.apply( this, arguments ); 294 this.controller.on( 'select:activate select:deactivate', this.toggleBulkEditHandler, this ); 295 this.controller.on( 'selection:action:done', this.back, this ); 296 }, 445 initialize: function() { 446 this.controller.on( 'content:activate:edit-details', _.bind( this.editAttachment, this ) ); 297 447 298 back: function () { 299 this.controller.deactivateMode( 'select' ).activateMode( 'edit' ); 448 Details.prototype.initialize.apply( this, arguments ); 300 449 }, 301 450 302 click: function() { 303 Button.prototype.click.apply( this, arguments ); 304 if ( this.controller.isModeActive( 'select' ) ) { 305 this.back(); 306 } else { 307 this.controller.deactivateMode( 'edit' ).activateMode( 'select' ); 451 editAttachment: function( event ) { 452 if ( event ) { 453 event.preventDefault(); 308 454 } 455 this.controller.content.mode( 'edit-image' ); 309 456 }, 310 457 311 render: function() { 312 Button.prototype.render.apply( this, arguments ); 313 this.$el.addClass( 'select-mode-toggle-button' ); 314 return this; 315 }, 316 317 toggleBulkEditHandler: function() { 318 var toolbar = this.controller.content.get().toolbar, children; 458 /** 459 * Noop this from parent class, doesn't apply here. 460 */ 461 toggleSelectionHandler: function() {}, 319 462 320 children = toolbar.$( '.media-toolbar-secondary > *, .media-toolbar-primary > *' ); 463 render: function() { 464 Details.prototype.render.apply( this, arguments ); 321 465 322 // TODO: the Frame should be doing all of this. 323 if ( this.controller.isModeActive( 'select' ) ) { 324 this.model.set( { 325 size: 'large', 326 text: l10n.cancelSelection 327 } ); 328 children.not( '.spinner, .media-button' ).hide(); 329 this.$el.show(); 330 toolbar.$( '.delete-selected-button' ).removeClass( 'hidden' ); 331 } else { 332 this.model.set( { 333 size: '', 334 text: l10n.bulkSelect 335 } ); 336 this.controller.content.get().$el.removeClass( 'fixed' ); 337 toolbar.$el.css( 'width', '' ); 338 toolbar.$( '.delete-selected-button' ).addClass( 'hidden' ); 339 children.not( '.media-button' ).show(); 340 this.controller.state().get( 'selection' ).reset(); 341 } 466 wp.media.mixin.removeAllPlayers(); 467 this.$( 'audio, video' ).each( function (i, elem) { 468 var el = wp.media.view.MediaDetails.prepareSrc( elem ); 469 new window.MediaElementPlayer( el, wp.media.mixin.mejsSettings ); 470 } ); 342 471 } 343 472 }); 344 473 345 module.exports = SelectModeToggle;474 module.exports = TwoColumn; 346 475 347 },{}],8:[function(require,module,exports){ 348 var View = wp.media.View,349 EditImage = wp.media.view.EditImage, 350 Details; 476 477 /***/ }), 478 /* 14 */ 479 /***/ (function(module, exports) { 351 480 352 481 /** 353 * wp.media.view. EditImage.Details482 * wp.media.view.MediaFrame.Manage.Router 354 483 * 355 * @memberOf wp.media.view.EditImage 484 * A router for handling the browser history and application state. 485 * 486 * @memberOf wp.media.view.MediaFrame.Manage 356 487 * 357 488 * @class 358 * @augments wp.media.view.EditImage 359 * @augments wp.media.View 360 * @augments wp.Backbone.View 361 * @augments Backbone.View 489 * @augments Backbone.Router 362 490 */ 363 Details = EditImage.extend(/** @lends wp.media.view.EditImage.Details.prototype */{ 491 var Router = Backbone.Router.extend(/** @lends wp.media.view.MediaFrame.Manage.Router.prototype */{ 492 routes: { 493 'upload.php?item=:slug&mode=edit': 'editItem', 494 'upload.php?item=:slug': 'showItem', 495 'upload.php?search=:query': 'search', 496 'upload.php': 'reset' 497 }, 498 499 // Map routes against the page URL 500 baseUrl: function( url ) { 501 return 'upload.php' + url; 502 }, 503 504 reset: function() { 505 var frame = wp.media.frames.edit; 506 507 if ( frame ) { 508 frame.close(); 509 } 510 }, 511 512 // Respond to the search route by filling the search field and trigggering the input event 513 search: function( query ) { 514 jQuery( '#media-search-input' ).val( query ).trigger( 'input' ); 515 }, 516 517 // Show the modal with a specific item 518 showItem: function( query ) { 519 var media = wp.media, 520 frame = media.frames.browse, 521 library = frame.state().get('library'), 522 item; 523 524 // Trigger the media frame to open the correct item 525 item = library.findWhere( { id: parseInt( query, 10 ) } ); 526 item.set( 'skipHistory', true ); 527 528 if ( item ) { 529 frame.trigger( 'edit:attachment', item ); 530 } else { 531 item = media.attachment( query ); 532 frame.listenTo( item, 'change', function( model ) { 533 frame.stopListening( item ); 534 frame.trigger( 'edit:attachment', model ); 535 } ); 536 item.fetch(); 537 } 538 }, 539 540 // Show the modal in edit mode with a specific item. 541 editItem: function( query ) { 542 this.showItem( query ); 543 wp.media.frames.edit.content.mode( 'edit-details' ); 544 } 545 }); 546 547 module.exports = Router; 548 549 550 /***/ }), 551 /* 15 */ 552 /***/ (function(module, exports) { 553 554 var View = wp.media.View, 555 EditImage = wp.media.view.EditImage, 556 Details; 557 558 /** 559 * wp.media.view.EditImage.Details 560 * 561 * @memberOf wp.media.view.EditImage 562 * 563 * @class 564 * @augments wp.media.view.EditImage 565 * @augments wp.media.View 566 * @augments wp.Backbone.View 567 * @augments Backbone.View 568 */ 569 Details = EditImage.extend(/** @lends wp.media.view.EditImage.Details.prototype */{ 364 570 initialize: function( options ) { 365 571 this.editor = window.imageEdit; 366 572 this.frame = options.frame; … … Details = EditImage.extend(/** @lends wp.media.view.EditImage.Details.prototype 381 587 382 588 module.exports = Details; 383 589 384 },{}],9:[function(require,module,exports){ 590 591 /***/ }), 592 /* 16 */ 593 /***/ (function(module, exports) { 594 385 595 var Frame = wp.media.view.Frame, 386 596 MediaFrame = wp.media.view.MediaFrame, 387 597 … … EditAttachments = MediaFrame.extend(/** @lends wp.media.view.MediaFrame.EditAtta 643 853 644 854 module.exports = EditAttachments; 645 855 646 },{}],10:[function(require,module,exports){647 var MediaFrame = wp.media.view.MediaFrame,648 Library = wp.media.controller.Library,649 856 650 $ = Backbone.$, 651 Manage; 857 /***/ }), 858 /* 17 */ 859 /***/ (function(module, exports) { 860 861 862 var Button = wp.media.view.Button, 863 l10n = wp.media.view.l10n, 864 SelectModeToggle; 652 865 653 866 /** 654 * wp.media.view.MediaFrame.Manage 655 * 656 * A generic management frame workflow. 657 * 658 * Used in the media grid view. 867 * wp.media.view.SelectModeToggleButton 659 868 * 660 * @memberOf wp.media.view .MediaFrame869 * @memberOf wp.media.view 661 870 * 662 871 * @class 663 * @augments wp.media.view.MediaFrame 664 * @augments wp.media.view.Frame 872 * @augments wp.media.view.Button 665 873 * @augments wp.media.View 666 874 * @augments wp.Backbone.View 667 875 * @augments Backbone.View 668 * @mixes wp.media.controller.StateMachine669 876 */ 670 Manage = MediaFrame.extend(/** @lends wp.media.view.MediaFrame.Manage.prototype */{ 671 /** 672 * @constructs 673 */ 877 SelectModeToggle = Button.extend(/** @lends wp.media.view.SelectModeToggle.prototype */{ 674 878 initialize: function() { 675 879 _.defaults( this.options, { 676 title: '', 677 modal: false, 678 selection: [], 679 library: {}, // Options hash for the query to the media library. 680 multiple: 'add', 681 state: 'library', 682 uploader: true, 683 mode: [ 'grid', 'edit' ] 684 }); 685 686 this.$body = $( document.body ); 687 this.$window = $( window ); 688 this.$adminBar = $( '#wpadminbar' ); 689 // Store the Add New button for later reuse in wp.media.view.UploaderInline. 690 this.$uploaderToggler = $( '.page-title-action' ) 691 .attr( 'aria-expanded', 'false' ) 692 .on( 'click', _.bind( this.addNewClickHandler, this ) ); 693 694 this.$window.on( 'scroll resize', _.debounce( _.bind( this.fixPosition, this ), 15 ) ); 695 696 // Ensure core and media grid view UI is enabled. 697 this.$el.addClass('wp-core-ui'); 880 size : '' 881 } ); 698 882 699 // Force the uploader off if the upload limit has been exceeded or 700 // if the browser isn't supported. 701 if ( wp.Uploader.limitExceeded || ! wp.Uploader.browser.supported ) { 702 this.options.uploader = false; 703 } 883 Button.prototype.initialize.apply( this, arguments ); 884 this.controller.on( 'select:activate select:deactivate', this.toggleBulkEditHandler, this ); 885 this.controller.on( 'selection:action:done', this.back, this ); 886 }, 704 887 705 // Initialize a window-wide uploader. 706 if ( this.options.uploader ) { 707 this.uploader = new wp.media.view.UploaderWindow({ 708 controller: this, 709 uploader: { 710 dropzone: document.body, 711 container: document.body 712 } 713 }).render(); 714 this.uploader.ready(); 715 $('body').append( this.uploader.el ); 888 back: function () { 889 this.controller.deactivateMode( 'select' ).activateMode( 'edit' ); 890 }, 716 891 717 this.options.uploader = false; 892 click: function() { 893 Button.prototype.click.apply( this, arguments ); 894 if ( this.controller.isModeActive( 'select' ) ) { 895 this.back(); 896 } else { 897 this.controller.deactivateMode( 'edit' ).activateMode( 'select' ); 718 898 } 719 720 this.gridRouter = new wp.media.view.MediaFrame.Manage.Router();721 722 // Call 'initialize' directly on the parent class.723 MediaFrame.prototype.initialize.apply( this, arguments );724 725 // Append the frame view directly the supplied container.726 this.$el.appendTo( this.options.container );727 728 this.createStates();729 this.bindRegionModeHandlers();730 this.render();731 this.bindSearchHandler();732 733 wp.media.frames.browse = this;734 899 }, 735 900 736 bindSearchHandler: function() { 737 var search = this.$( '#media-search-input' ), 738 searchView = this.browserView.toolbar.get( 'search' ).$el, 739 listMode = this.$( '.view-list' ), 740 741 input = _.throttle( function (e) { 742 var val = $( e.currentTarget ).val(), 743 url = ''; 744 745 if ( val ) { 746 url += '?search=' + val; 747 this.gridRouter.navigate( this.gridRouter.baseUrl( url ), { replace: true } ); 748 } 749 }, 1000 ); 750 751 // Update the URL when entering search string (at most once per second) 752 search.on( 'input', _.bind( input, this ) ); 753 754 this.gridRouter 755 .on( 'route:search', function () { 756 var href = window.location.href; 757 if ( href.indexOf( 'mode=' ) > -1 ) { 758 href = href.replace( /mode=[^&]+/g, 'mode=list' ); 759 } else { 760 href += href.indexOf( '?' ) > -1 ? '&mode=list' : '?mode=list'; 761 } 762 href = href.replace( 'search=', 's=' ); 763 listMode.prop( 'href', href ); 764 }) 765 .on( 'route:reset', function() { 766 searchView.val( '' ).trigger( 'input' ); 767 }); 901 render: function() { 902 Button.prototype.render.apply( this, arguments ); 903 this.$el.addClass( 'select-mode-toggle-button' ); 904 return this; 768 905 }, 769 906 770 /** 771 * Create the default states for the frame. 772 */ 773 createStates: function() { 774 var options = this.options; 907 toggleBulkEditHandler: function() { 908 var toolbar = this.controller.content.get().toolbar, children; 775 909 776 if ( this.options.states ) { 777 return; 910 children = toolbar.$( '.media-toolbar-secondary > *, .media-toolbar-primary > *' ); 911 912 // TODO: the Frame should be doing all of this. 913 if ( this.controller.isModeActive( 'select' ) ) { 914 this.model.set( { 915 size: 'large', 916 text: l10n.cancelSelection 917 } ); 918 children.not( '.spinner, .media-button' ).hide(); 919 this.$el.show(); 920 toolbar.$( '.delete-selected-button' ).removeClass( 'hidden' ); 921 } else { 922 this.model.set( { 923 size: '', 924 text: l10n.bulkSelect 925 } ); 926 this.controller.content.get().$el.removeClass( 'fixed' ); 927 toolbar.$el.css( 'width', '' ); 928 toolbar.$( '.delete-selected-button' ).addClass( 'hidden' ); 929 children.not( '.media-button' ).show(); 930 this.controller.state().get( 'selection' ).reset(); 778 931 } 932 } 933 }); 779 934 780 // Add the default states. 781 this.states.add([ 782 new Library({ 783 library: wp.media.query( options.library ), 784 multiple: options.multiple, 785 title: options.title, 786 content: 'browse', 787 toolbar: 'select', 788 contentUserSetting: false, 789 filterable: 'all', 790 autoSelect: false 791 }) 792 ]); 793 }, 935 module.exports = SelectModeToggle; 794 936 795 /**796 * Bind region mode activation events to proper handlers.797 */798 bindRegionModeHandlers: function() {799 this.on( 'content:create:browse', this.browseContent, this );800 937 801 // Handle a frame-level event for editing an attachment. 802 this.on( 'edit:attachment', this.openEditAttachmentModal, this ); 938 /***/ }), 939 /* 18 */ 940 /***/ (function(module, exports) { 803 941 804 this.on( 'select:activate', this.bindKeydown, this ); 805 this.on( 'select:deactivate', this.unbindKeydown, this );806 },942 var Button = wp.media.view.Button, 943 l10n = wp.media.view.l10n, 944 DeleteSelected; 807 945 808 handleKeydown: function( e ) { 809 if ( 27 === e.which ) { 810 e.preventDefault(); 811 this.deactivateMode( 'select' ).activateMode( 'edit' ); 946 /** 947 * wp.media.view.DeleteSelectedButton 948 * 949 * A button that handles bulk Delete/Trash logic 950 * 951 * @memberOf wp.media.view 952 * 953 * @class 954 * @augments wp.media.view.Button 955 * @augments wp.media.View 956 * @augments wp.Backbone.View 957 * @augments Backbone.View 958 */ 959 DeleteSelected = Button.extend(/** @lends wp.media.view.DeleteSelectedButton.prototype */{ 960 initialize: function() { 961 Button.prototype.initialize.apply( this, arguments ); 962 if ( this.options.filters ) { 963 this.options.filters.model.on( 'change', this.filterChange, this ); 812 964 } 965 this.controller.on( 'selection:toggle', this.toggleDisabled, this ); 813 966 }, 814 967 815 bindKeydown: function() { 816 this.$body.on( 'keydown.select', _.bind( this.handleKeydown, this ) ); 817 }, 818 819 unbindKeydown: function() { 820 this.$body.off( 'keydown.select' ); 821 }, 822 823 fixPosition: function() { 824 var $browser, $toolbar; 825 if ( ! this.isModeActive( 'select' ) ) { 826 return; 827 } 828 829 $browser = this.$('.attachments-browser'); 830 $toolbar = $browser.find('.media-toolbar'); 831 832 // Offset doesn't appear to take top margin into account, hence +16 833 if ( ( $browser.offset().top + 16 ) < this.$window.scrollTop() + this.$adminBar.height() ) { 834 $browser.addClass( 'fixed' ); 835 $toolbar.css('width', $browser.width() + 'px'); 968 filterChange: function( model ) { 969 if ( 'trash' === model.get( 'status' ) ) { 970 this.model.set( 'text', l10n.untrashSelected ); 971 } else if ( wp.media.view.settings.mediaTrash ) { 972 this.model.set( 'text', l10n.trashSelected ); 836 973 } else { 837 $browser.removeClass( 'fixed' ); 838 $toolbar.css('width', ''); 974 this.model.set( 'text', l10n.deleteSelected ); 839 975 } 840 976 }, 841 977 842 /** 843 * Click handler for the `Add New` button. 844 */ 845 addNewClickHandler: function( event ) { 846 event.preventDefault(); 847 this.trigger( 'toggle:upload:attachment' ); 848 849 if ( this.uploader ) { 850 this.uploader.refresh(); 851 } 978 toggleDisabled: function() { 979 this.model.set( 'disabled', ! this.controller.state().get( 'selection' ).length ); 852 980 }, 853 981 854 /** 855 * Open the Edit Attachment modal. 856 */ 857 openEditAttachmentModal: function( model ) { 858 // Create a new EditAttachment frame, passing along the library and the attachment model. 859 if ( wp.media.frames.edit ) { 860 wp.media.frames.edit.open().trigger( 'refresh', model ); 982 render: function() { 983 Button.prototype.render.apply( this, arguments ); 984 if ( this.controller.isModeActive( 'select' ) ) { 985 this.$el.addClass( 'delete-selected-button' ); 861 986 } else { 862 wp.media.frames.edit = wp.media( { 863 frame: 'edit-attachments', 864 controller: this, 865 library: this.state().get('library'), 866 model: model 867 } ); 987 this.$el.addClass( 'delete-selected-button hidden' ); 868 988 } 869 }, 989 this.toggleDisabled(); 990 return this; 991 } 992 }); 870 993 871 /** 872 * Create an attachments browser view within the content region. 873 * 874 * @param {Object} contentRegion Basic object with a `view` property, which 875 * should be set with the proper region view. 876 * @this wp.media.controller.Region 877 */ 878 browseContent: function( contentRegion ) { 879 var state = this.state(); 994 module.exports = DeleteSelected; 880 995 881 // Browse our library of attachments.882 this.browserView = contentRegion.view = new wp.media.view.AttachmentsBrowser({883 controller: this,884 collection: state.get('library'),885 selection: state.get('selection'),886 model: state,887 sortable: state.get('sortable'),888 search: state.get('searchable'),889 filters: state.get('filterable'),890 date: state.get('date'),891 display: state.get('displaySettings'),892 dragInfo: state.get('dragInfo'),893 sidebar: 'errors',894 996 895 suggestedWidth: state.get('suggestedWidth'), 896 suggestedHeight: state.get('suggestedHeight'), 997 /***/ }), 998 /* 19 */ 999 /***/ (function(module, exports) { 897 1000 898 AttachmentView: state.get('AttachmentView'), 1001 var Button = wp.media.view.Button, 1002 DeleteSelected = wp.media.view.DeleteSelectedButton, 1003 DeleteSelectedPermanently; 899 1004 900 scrollElement: document 901 }); 902 this.browserView.on( 'ready', _.bind( this.bindDeferred, this ) ); 1005 /** 1006 * wp.media.view.DeleteSelectedPermanentlyButton 1007 * 1008 * When MEDIA_TRASH is true, a button that handles bulk Delete Permanently logic 1009 * 1010 * @memberOf wp.media.view 1011 * 1012 * @class 1013 * @augments wp.media.view.DeleteSelectedButton 1014 * @augments wp.media.view.Button 1015 * @augments wp.media.View 1016 * @augments wp.Backbone.View 1017 * @augments Backbone.View 1018 */ 1019 DeleteSelectedPermanently = DeleteSelected.extend(/** @lends wp.media.view.DeleteSelectedPermanentlyButton.prototype */{ 1020 initialize: function() { 1021 DeleteSelected.prototype.initialize.apply( this, arguments ); 1022 this.controller.on( 'select:activate', this.selectActivate, this ); 1023 this.controller.on( 'select:deactivate', this.selectDeactivate, this ); 1024 }, 903 1025 904 this.errors = wp.Uploader.errors;905 this. errors.on( 'add remove reset', this.sidebarVisibility, this);1026 filterChange: function( model ) { 1027 this.canShow = ( 'trash' === model.get( 'status' ) ); 906 1028 }, 907 1029 908 sidebarVisibility: function() { 909 this.browserView.$( '.media-sidebar' ).toggle( !! this.errors.length ); 1030 selectActivate: function() { 1031 this.toggleDisabled(); 1032 this.$el.toggleClass( 'hidden', ! this.canShow ); 910 1033 }, 911 1034 912 bindDeferred: function() { 913 if ( ! this.browserView.dfd ) { 914 return; 915 } 916 this.browserView.dfd.done( _.bind( this.startHistory, this ) ); 1035 selectDeactivate: function() { 1036 this.toggleDisabled(); 1037 this.$el.addClass( 'hidden' ); 917 1038 }, 918 1039 919 startHistory: function() { 920 // Verify pushState support and activate 921 if ( window.history && window.history.pushState ) { 922 if ( Backbone.History.started ) { 923 Backbone.history.stop(); 924 } 925 Backbone.history.start( { 926 root: window._wpMediaGridSettings.adminUrl, 927 pushState: true 928 } ); 929 } 1040 render: function() { 1041 Button.prototype.render.apply( this, arguments ); 1042 this.selectActivate(); 1043 return this; 930 1044 } 931 1045 }); 932 1046 933 module.exports = Manage; 1047 module.exports = DeleteSelectedPermanently; 1048 934 1049 935 },{}]},{},[2]); 1050 /***/ }) 1051 /******/ ]); 1052 No newline at end of file -
src/wp-includes/js/media-models.js
diff --git src/wp-includes/js/media-models.js src/wp-includes/js/media-models.js index caa7fcb590..af64387257 100644
1 (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ 1 /******/ (function(modules) { // webpackBootstrap 2 /******/ // The module cache 3 /******/ var installedModules = {}; 4 /******/ 5 /******/ // The require function 6 /******/ function __webpack_require__(moduleId) { 7 /******/ 8 /******/ // Check if module is in cache 9 /******/ if(installedModules[moduleId]) { 10 /******/ return installedModules[moduleId].exports; 11 /******/ } 12 /******/ // Create a new module (and put it into the cache) 13 /******/ var module = installedModules[moduleId] = { 14 /******/ i: moduleId, 15 /******/ l: false, 16 /******/ exports: {} 17 /******/ }; 18 /******/ 19 /******/ // Execute the module function 20 /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); 21 /******/ 22 /******/ // Flag the module as loaded 23 /******/ module.l = true; 24 /******/ 25 /******/ // Return the exports of the module 26 /******/ return module.exports; 27 /******/ } 28 /******/ 29 /******/ 30 /******/ // expose the modules object (__webpack_modules__) 31 /******/ __webpack_require__.m = modules; 32 /******/ 33 /******/ // expose the module cache 34 /******/ __webpack_require__.c = installedModules; 35 /******/ 36 /******/ // define getter function for harmony exports 37 /******/ __webpack_require__.d = function(exports, name, getter) { 38 /******/ if(!__webpack_require__.o(exports, name)) { 39 /******/ Object.defineProperty(exports, name, { 40 /******/ configurable: false, 41 /******/ enumerable: true, 42 /******/ get: getter 43 /******/ }); 44 /******/ } 45 /******/ }; 46 /******/ 47 /******/ // getDefaultExport function for compatibility with non-harmony modules 48 /******/ __webpack_require__.n = function(module) { 49 /******/ var getter = module && module.__esModule ? 50 /******/ function getDefault() { return module['default']; } : 51 /******/ function getModuleExports() { return module; }; 52 /******/ __webpack_require__.d(getter, 'a', getter); 53 /******/ return getter; 54 /******/ }; 55 /******/ 56 /******/ // Object.prototype.hasOwnProperty.call 57 /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; 58 /******/ 59 /******/ // __webpack_public_path__ 60 /******/ __webpack_require__.p = ""; 61 /******/ 62 /******/ // Load entry module and return exports 63 /******/ return __webpack_require__(__webpack_require__.s = 20); 64 /******/ }) 65 /************************************************************************/ 66 /******/ ({ 67 68 /***/ 20: 69 /***/ (function(module, exports, __webpack_require__) { 70 2 71 var $ = jQuery, 3 72 Attachment, Attachments, l10n, media; 4 73 … … l10n = media.model.l10n = window._wpMediaModelsL10n || {}; 65 134 media.model.settings = l10n.settings || {}; 66 135 delete l10n.settings; 67 136 68 Attachment = media.model.Attachment = require( './models/attachment.js');69 Attachments = media.model.Attachments = require( './models/attachments.js');137 Attachment = media.model.Attachment = __webpack_require__( 21 ); 138 Attachments = media.model.Attachments = __webpack_require__( 22 ); 70 139 71 media.model.Query = require( './models/query.js');72 media.model.PostImage = require( './models/post-image.js');73 media.model.Selection = require( './models/selection.js');140 media.model.Query = __webpack_require__( 23 ); 141 media.model.PostImage = __webpack_require__( 24 ); 142 media.model.Selection = __webpack_require__( 25 ); 74 143 75 144 /** 76 145 * ======================================================================== … … $(window).on('unload', function(){ 238 307 window.wp = null; 239 308 }); 240 309 241 },{"./models/attachment.js":2,"./models/attachments.js":3,"./models/post-image.js":4,"./models/query.js":5,"./models/selection.js":6}],2:[function(require,module,exports){ 310 311 /***/ }), 312 313 /***/ 21: 314 /***/ (function(module, exports) { 315 242 316 var $ = Backbone.$, 243 317 Attachment; 244 318 … … Attachment = Backbone.Model.extend(/** @lends wp.media.model.Attachment.prototyp 409 483 410 484 module.exports = Attachment; 411 485 412 },{}],3:[function(require,module,exports){ 486 487 /***/ }), 488 489 /***/ 22: 490 /***/ (function(module, exports) { 491 413 492 /** 414 493 * wp.media.model.Attachments 415 494 * … … var Attachments = Backbone.Collection.extend(/** @lends wp.media.model.Attachmen 954 1033 955 1034 module.exports = Attachments; 956 1035 957 },{}],4:[function(require,module,exports){958 /**959 * wp.media.model.PostImage960 *961 * An instance of an image that's been embedded into a post.962 *963 * Used in the embedded image attachment display settings modal - @see wp.media.view.MediaFrame.ImageDetails.964 *965 * @memberOf wp.media.model966 *967 * @class968 * @augments Backbone.Model969 *970 * @param {int} [attributes] Initial model attributes.971 * @param {int} [attributes.attachment_id] ID of the attachment.972 **/973 var PostImage = Backbone.Model.extend(/** @lends wp.media.model.PostImage.prototype */{974 975 initialize: function( attributes ) {976 var Attachment = wp.media.model.Attachment;977 this.attachment = false;978 1036 979 if ( attributes.attachment_id ) { 980 this.attachment = Attachment.get( attributes.attachment_id ); 981 if ( this.attachment.get( 'url' ) ) { 982 this.dfd = jQuery.Deferred(); 983 this.dfd.resolve(); 984 } else { 985 this.dfd = this.attachment.fetch(); 986 } 987 this.bindAttachmentListeners(); 988 } 1037 /***/ }), 989 1038 990 // keep url in sync with changes to the type of link 991 this.on( 'change:link', this.updateLinkUrl, this ); 992 this.on( 'change:size', this.updateSize, this ); 1039 /***/ 23: 1040 /***/ (function(module, exports) { 993 1041 994 this.setLinkTypeFromUrl();995 this.setAspectRatio();996 997 this.set( 'originalUrl', attributes.url );998 },999 1000 bindAttachmentListeners: function() {1001 this.listenTo( this.attachment, 'sync', this.setLinkTypeFromUrl );1002 this.listenTo( this.attachment, 'sync', this.setAspectRatio );1003 this.listenTo( this.attachment, 'change', this.updateSize );1004 },1005 1006 changeAttachment: function( attachment, props ) {1007 this.stopListening( this.attachment );1008 this.attachment = attachment;1009 this.bindAttachmentListeners();1010 1011 this.set( 'attachment_id', this.attachment.get( 'id' ) );1012 this.set( 'caption', this.attachment.get( 'caption' ) );1013 this.set( 'alt', this.attachment.get( 'alt' ) );1014 this.set( 'size', props.get( 'size' ) );1015 this.set( 'align', props.get( 'align' ) );1016 this.set( 'link', props.get( 'link' ) );1017 this.updateLinkUrl();1018 this.updateSize();1019 },1020 1021 setLinkTypeFromUrl: function() {1022 var linkUrl = this.get( 'linkUrl' ),1023 type;1024 1025 if ( ! linkUrl ) {1026 this.set( 'link', 'none' );1027 return;1028 }1029 1030 // default to custom if there is a linkUrl1031 type = 'custom';1032 1033 if ( this.attachment ) {1034 if ( this.attachment.get( 'url' ) === linkUrl ) {1035 type = 'file';1036 } else if ( this.attachment.get( 'link' ) === linkUrl ) {1037 type = 'post';1038 }1039 } else {1040 if ( this.get( 'url' ) === linkUrl ) {1041 type = 'file';1042 }1043 }1044 1045 this.set( 'link', type );1046 },1047 1048 updateLinkUrl: function() {1049 var link = this.get( 'link' ),1050 url;1051 1052 switch( link ) {1053 case 'file':1054 if ( this.attachment ) {1055 url = this.attachment.get( 'url' );1056 } else {1057 url = this.get( 'url' );1058 }1059 this.set( 'linkUrl', url );1060 break;1061 case 'post':1062 this.set( 'linkUrl', this.attachment.get( 'link' ) );1063 break;1064 case 'none':1065 this.set( 'linkUrl', '' );1066 break;1067 }1068 },1069 1070 updateSize: function() {1071 var size;1072 1073 if ( ! this.attachment ) {1074 return;1075 }1076 1077 if ( this.get( 'size' ) === 'custom' ) {1078 this.set( 'width', this.get( 'customWidth' ) );1079 this.set( 'height', this.get( 'customHeight' ) );1080 this.set( 'url', this.get( 'originalUrl' ) );1081 return;1082 }1083 1084 size = this.attachment.get( 'sizes' )[ this.get( 'size' ) ];1085 1086 if ( ! size ) {1087 return;1088 }1089 1090 this.set( 'url', size.url );1091 this.set( 'width', size.width );1092 this.set( 'height', size.height );1093 },1094 1095 setAspectRatio: function() {1096 var full;1097 1098 if ( this.attachment && this.attachment.get( 'sizes' ) ) {1099 full = this.attachment.get( 'sizes' ).full;1100 1101 if ( full ) {1102 this.set( 'aspectRatio', full.width / full.height );1103 return;1104 }1105 }1106 1107 this.set( 'aspectRatio', this.get( 'customWidth' ) / this.get( 'customHeight' ) );1108 }1109 });1110 1111 module.exports = PostImage;1112 1113 },{}],5:[function(require,module,exports){1114 1042 var Attachments = wp.media.model.Attachments, 1115 1043 Query; 1116 1044 … … Query = Attachments.extend(/** @lends wp.media.model.Query.prototype */{ 1418 1346 1419 1347 module.exports = Query; 1420 1348 1421 },{}],6:[function(require,module,exports){ 1349 1350 /***/ }), 1351 1352 /***/ 24: 1353 /***/ (function(module, exports) { 1354 1355 /** 1356 * wp.media.model.PostImage 1357 * 1358 * An instance of an image that's been embedded into a post. 1359 * 1360 * Used in the embedded image attachment display settings modal - @see wp.media.view.MediaFrame.ImageDetails. 1361 * 1362 * @memberOf wp.media.model 1363 * 1364 * @class 1365 * @augments Backbone.Model 1366 * 1367 * @param {int} [attributes] Initial model attributes. 1368 * @param {int} [attributes.attachment_id] ID of the attachment. 1369 **/ 1370 var PostImage = Backbone.Model.extend(/** @lends wp.media.model.PostImage.prototype */{ 1371 1372 initialize: function( attributes ) { 1373 var Attachment = wp.media.model.Attachment; 1374 this.attachment = false; 1375 1376 if ( attributes.attachment_id ) { 1377 this.attachment = Attachment.get( attributes.attachment_id ); 1378 if ( this.attachment.get( 'url' ) ) { 1379 this.dfd = jQuery.Deferred(); 1380 this.dfd.resolve(); 1381 } else { 1382 this.dfd = this.attachment.fetch(); 1383 } 1384 this.bindAttachmentListeners(); 1385 } 1386 1387 // keep url in sync with changes to the type of link 1388 this.on( 'change:link', this.updateLinkUrl, this ); 1389 this.on( 'change:size', this.updateSize, this ); 1390 1391 this.setLinkTypeFromUrl(); 1392 this.setAspectRatio(); 1393 1394 this.set( 'originalUrl', attributes.url ); 1395 }, 1396 1397 bindAttachmentListeners: function() { 1398 this.listenTo( this.attachment, 'sync', this.setLinkTypeFromUrl ); 1399 this.listenTo( this.attachment, 'sync', this.setAspectRatio ); 1400 this.listenTo( this.attachment, 'change', this.updateSize ); 1401 }, 1402 1403 changeAttachment: function( attachment, props ) { 1404 this.stopListening( this.attachment ); 1405 this.attachment = attachment; 1406 this.bindAttachmentListeners(); 1407 1408 this.set( 'attachment_id', this.attachment.get( 'id' ) ); 1409 this.set( 'caption', this.attachment.get( 'caption' ) ); 1410 this.set( 'alt', this.attachment.get( 'alt' ) ); 1411 this.set( 'size', props.get( 'size' ) ); 1412 this.set( 'align', props.get( 'align' ) ); 1413 this.set( 'link', props.get( 'link' ) ); 1414 this.updateLinkUrl(); 1415 this.updateSize(); 1416 }, 1417 1418 setLinkTypeFromUrl: function() { 1419 var linkUrl = this.get( 'linkUrl' ), 1420 type; 1421 1422 if ( ! linkUrl ) { 1423 this.set( 'link', 'none' ); 1424 return; 1425 } 1426 1427 // default to custom if there is a linkUrl 1428 type = 'custom'; 1429 1430 if ( this.attachment ) { 1431 if ( this.attachment.get( 'url' ) === linkUrl ) { 1432 type = 'file'; 1433 } else if ( this.attachment.get( 'link' ) === linkUrl ) { 1434 type = 'post'; 1435 } 1436 } else { 1437 if ( this.get( 'url' ) === linkUrl ) { 1438 type = 'file'; 1439 } 1440 } 1441 1442 this.set( 'link', type ); 1443 }, 1444 1445 updateLinkUrl: function() { 1446 var link = this.get( 'link' ), 1447 url; 1448 1449 switch( link ) { 1450 case 'file': 1451 if ( this.attachment ) { 1452 url = this.attachment.get( 'url' ); 1453 } else { 1454 url = this.get( 'url' ); 1455 } 1456 this.set( 'linkUrl', url ); 1457 break; 1458 case 'post': 1459 this.set( 'linkUrl', this.attachment.get( 'link' ) ); 1460 break; 1461 case 'none': 1462 this.set( 'linkUrl', '' ); 1463 break; 1464 } 1465 }, 1466 1467 updateSize: function() { 1468 var size; 1469 1470 if ( ! this.attachment ) { 1471 return; 1472 } 1473 1474 if ( this.get( 'size' ) === 'custom' ) { 1475 this.set( 'width', this.get( 'customWidth' ) ); 1476 this.set( 'height', this.get( 'customHeight' ) ); 1477 this.set( 'url', this.get( 'originalUrl' ) ); 1478 return; 1479 } 1480 1481 size = this.attachment.get( 'sizes' )[ this.get( 'size' ) ]; 1482 1483 if ( ! size ) { 1484 return; 1485 } 1486 1487 this.set( 'url', size.url ); 1488 this.set( 'width', size.width ); 1489 this.set( 'height', size.height ); 1490 }, 1491 1492 setAspectRatio: function() { 1493 var full; 1494 1495 if ( this.attachment && this.attachment.get( 'sizes' ) ) { 1496 full = this.attachment.get( 'sizes' ).full; 1497 1498 if ( full ) { 1499 this.set( 'aspectRatio', full.width / full.height ); 1500 return; 1501 } 1502 } 1503 1504 this.set( 'aspectRatio', this.get( 'customWidth' ) / this.get( 'customHeight' ) ); 1505 } 1506 }); 1507 1508 module.exports = PostImage; 1509 1510 1511 /***/ }), 1512 1513 /***/ 25: 1514 /***/ (function(module, exports) { 1515 1422 1516 var Attachments = wp.media.model.Attachments, 1423 1517 Selection; 1424 1518 … … Selection = Attachments.extend(/** @lends wp.media.model.Selection.prototype */{ 1517 1611 1518 1612 module.exports = Selection; 1519 1613 1520 },{}]},{},[1]); 1614 1615 /***/ }) 1616 1617 /******/ }); 1618 No newline at end of file -
src/wp-includes/js/media-views.js
diff --git src/wp-includes/js/media-views.js src/wp-includes/js/media-views.js index 18e6444691..2e462971e2 100644
1 (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ 2 var Selection = wp.media.model.Selection, 3 Library = wp.media.controller.Library, 4 CollectionAdd; 1 /******/ (function(modules) { // webpackBootstrap 2 /******/ // The module cache 3 /******/ var installedModules = {}; 4 /******/ 5 /******/ // The require function 6 /******/ function __webpack_require__(moduleId) { 7 /******/ 8 /******/ // Check if module is in cache 9 /******/ if(installedModules[moduleId]) { 10 /******/ return installedModules[moduleId].exports; 11 /******/ } 12 /******/ // Create a new module (and put it into the cache) 13 /******/ var module = installedModules[moduleId] = { 14 /******/ i: moduleId, 15 /******/ l: false, 16 /******/ exports: {} 17 /******/ }; 18 /******/ 19 /******/ // Execute the module function 20 /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); 21 /******/ 22 /******/ // Flag the module as loaded 23 /******/ module.l = true; 24 /******/ 25 /******/ // Return the exports of the module 26 /******/ return module.exports; 27 /******/ } 28 /******/ 29 /******/ 30 /******/ // expose the modules object (__webpack_modules__) 31 /******/ __webpack_require__.m = modules; 32 /******/ 33 /******/ // expose the module cache 34 /******/ __webpack_require__.c = installedModules; 35 /******/ 36 /******/ // define getter function for harmony exports 37 /******/ __webpack_require__.d = function(exports, name, getter) { 38 /******/ if(!__webpack_require__.o(exports, name)) { 39 /******/ Object.defineProperty(exports, name, { 40 /******/ configurable: false, 41 /******/ enumerable: true, 42 /******/ get: getter 43 /******/ }); 44 /******/ } 45 /******/ }; 46 /******/ 47 /******/ // getDefaultExport function for compatibility with non-harmony modules 48 /******/ __webpack_require__.n = function(module) { 49 /******/ var getter = module && module.__esModule ? 50 /******/ function getDefault() { return module['default']; } : 51 /******/ function getModuleExports() { return module; }; 52 /******/ __webpack_require__.d(getter, 'a', getter); 53 /******/ return getter; 54 /******/ }; 55 /******/ 56 /******/ // Object.prototype.hasOwnProperty.call 57 /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; 58 /******/ 59 /******/ // __webpack_public_path__ 60 /******/ __webpack_require__.p = ""; 61 /******/ 62 /******/ // Load entry module and return exports 63 /******/ return __webpack_require__(__webpack_require__.s = 26); 64 /******/ }) 65 /************************************************************************/ 66 /******/ (Array(26).concat([ 67 /* 26 */ 68 /***/ (function(module, exports, __webpack_require__) { 5 69 6 /** 7 * wp.media.controller.CollectionAdd 8 * 9 * A state for adding attachments to a collection (e.g. video playlist). 10 * 11 * @memberOf wp.media.controller 12 * 13 * @class 14 * @augments wp.media.controller.Library 15 * @augments wp.media.controller.State 16 * @augments Backbone.Model 17 * 18 * @param {object} [attributes] The attributes hash passed to the state. 19 * @param {string} [attributes.id=library] Unique identifier. 20 * @param {string} attributes.title Title for the state. Displays in the frame's title region. 21 * @param {boolean} [attributes.multiple=add] Whether multi-select is enabled. @todo 'add' doesn't seem do anything special, and gets used as a boolean. 22 * @param {wp.media.model.Attachments} [attributes.library] The attachments collection to browse. 23 * If one is not supplied, a collection of attachments of the specified type will be created. 24 * @param {boolean|string} [attributes.filterable=uploaded] Whether the library is filterable, and if so what filters should be shown. 25 * Accepts 'all', 'uploaded', or 'unattached'. 26 * @param {string} [attributes.menu=gallery] Initial mode for the menu region. 27 * @param {string} [attributes.content=upload] Initial mode for the content region. 28 * Overridden by persistent user setting if 'contentUserSetting' is true. 29 * @param {string} [attributes.router=browse] Initial mode for the router region. 30 * @param {string} [attributes.toolbar=gallery-add] Initial mode for the toolbar region. 31 * @param {boolean} [attributes.searchable=true] Whether the library is searchable. 32 * @param {boolean} [attributes.sortable=true] Whether the Attachments should be sortable. Depends on the orderby property being set to menuOrder on the attachments collection. 33 * @param {boolean} [attributes.autoSelect=true] Whether an uploaded attachment should be automatically added to the selection. 34 * @param {boolean} [attributes.contentUserSetting=true] Whether the content region's mode should be set and persisted per user. 35 * @param {int} [attributes.priority=100] The priority for the state link in the media menu. 36 * @param {boolean} [attributes.syncSelection=false] Whether the Attachments selection should be persisted from the last state. 37 * Defaults to false because for this state, because the library of the Edit Gallery state is the selection. 38 * @param {string} attributes.type The collection's media type. (e.g. 'video'). 39 * @param {string} attributes.collectionType The collection type. (e.g. 'playlist'). 40 */ 41 CollectionAdd = Library.extend(/** @lends wp.media.controller.CollectionAdd.prototype */{ 42 defaults: _.defaults( { 43 // Selection defaults. @see media.model.Selection 44 multiple: 'add', 45 // Attachments browser defaults. @see media.view.AttachmentsBrowser 46 filterable: 'uploaded', 70 var media = wp.media, 71 $ = jQuery, 72 l10n; 47 73 48 priority: 100, 49 syncSelection: false 50 }, Library.prototype.defaults ), 74 media.isTouchDevice = ( 'ontouchend' in document ); 51 75 52 /** 53 * @since 3.9.0 54 */ 55 initialize: function() { 56 var collectionType = this.get('collectionType'); 76 // Link any localized strings. 77 l10n = media.view.l10n = window._wpMediaViewsL10n || {}; 57 78 58 if ( 'video' === this.get( 'type' ) ) { 59 collectionType = 'video-' + collectionType;60 } 79 // Link any settings. 80 media.view.settings = l10n.settings || {}; 81 delete l10n.settings; 61 82 62 this.set( 'id', collectionType + '-library' ); 63 this.set( 'toolbar', collectionType + '-add' ); 64 this.set( 'menu', collectionType ); 83 // Copy the `post` setting over to the model settings. 84 media.model.settings.post = media.view.settings.post; 65 85 66 // If we haven't been provided a `library`, create a `Selection`. 67 if ( ! this.get('library') ) { 68 this.set( 'library', wp.media.query({ type: this.get('type') }) ); 69 } 70 Library.prototype.initialize.apply( this, arguments ); 71 }, 86 // Check if the browser supports CSS 3.0 transitions 87 $.support.transition = (function(){ 88 var style = document.documentElement.style, 89 transitions = { 90 WebkitTransition: 'webkitTransitionEnd', 91 MozTransition: 'transitionend', 92 OTransition: 'oTransitionEnd otransitionend', 93 transition: 'transitionend' 94 }, transition; 72 95 73 /** 74 * @since 3.9.0 75 */ 76 activate: function() { 77 var library = this.get('library'), 78 editLibrary = this.get('editLibrary'), 79 edit = this.frame.state( this.get('collectionType') + '-edit' ).get('library'); 96 transition = _.find( _.keys( transitions ), function( transition ) { 97 return ! _.isUndefined( style[ transition ] ); 98 }); 80 99 81 if ( editLibrary && editLibrary !== edit ) { 82 library.unobserve( editLibrary ); 100 return transition && { 101 end: transitions[ transition ] 102 }; 103 }()); 104 105 /** 106 * A shared event bus used to provide events into 107 * the media workflows that 3rd-party devs can use to hook 108 * in. 109 */ 110 media.events = _.extend( {}, Backbone.Events ); 111 112 /** 113 * Makes it easier to bind events using transitions. 114 * 115 * @param {string} selector 116 * @param {Number} sensitivity 117 * @returns {Promise} 118 */ 119 media.transition = function( selector, sensitivity ) { 120 var deferred = $.Deferred(); 121 122 sensitivity = sensitivity || 2000; 123 124 if ( $.support.transition ) { 125 if ( ! (selector instanceof $) ) { 126 selector = $( selector ); 83 127 } 84 128 85 // Accepts attachments that exist in the original library and 86 // that do not exist in gallery's library. 87 library.validator = function( attachment ) { 88 return !! this.mirroring.get( attachment.cid ) && ! edit.get( attachment.cid ) && Selection.prototype.validator.apply( this, arguments ); 89 }; 129 // Resolve the deferred when the first element finishes animating. 130 selector.first().one( $.support.transition.end, deferred.resolve ); 90 131 91 // Reset the library to ensure that all attachments are re-added 92 // to the collection. Do so silently, as calling `observe` will 93 // trigger the `reset` event. 94 library.reset( library.mirroring.models, { silent: true }); 95 library.observe( edit ); 96 this.set('editLibrary', edit); 132 // Just in case the event doesn't trigger, fire a callback. 133 _.delay( deferred.resolve, sensitivity ); 97 134 98 Library.prototype.activate.apply( this, arguments ); 135 // Otherwise, execute on the spot. 136 } else { 137 deferred.resolve(); 99 138 } 100 });101 139 102 module.exports = CollectionAdd; 140 return deferred.promise(); 141 }; 103 142 104 },{}],2:[function(require,module,exports){ 105 var Library = wp.media.controller.Library, 106 l10n = wp.media.view.l10n, 107 $ = jQuery, 108 CollectionEdit; 143 media.controller.Region = __webpack_require__( 27 ); 144 media.controller.StateMachine = __webpack_require__( 28 ); 145 media.controller.State = __webpack_require__( 29 ); 146 147 media.selectionSync = __webpack_require__( 30 ); 148 media.controller.Library = __webpack_require__( 31 ); 149 media.controller.ImageDetails = __webpack_require__( 32 ); 150 media.controller.GalleryEdit = __webpack_require__( 33 ); 151 media.controller.GalleryAdd = __webpack_require__( 34 ); 152 media.controller.CollectionEdit = __webpack_require__( 35 ); 153 media.controller.CollectionAdd = __webpack_require__( 36 ); 154 media.controller.FeaturedImage = __webpack_require__( 37 ); 155 media.controller.ReplaceImage = __webpack_require__( 38 ); 156 media.controller.EditImage = __webpack_require__( 39 ); 157 media.controller.MediaLibrary = __webpack_require__( 40 ); 158 media.controller.Embed = __webpack_require__( 41 ); 159 media.controller.Cropper = __webpack_require__( 42 ); 160 media.controller.CustomizeImageCropper = __webpack_require__( 43 ); 161 media.controller.SiteIconCropper = __webpack_require__( 44 ); 162 163 media.View = __webpack_require__( 45 ); 164 media.view.Frame = __webpack_require__( 46 ); 165 media.view.MediaFrame = __webpack_require__( 47 ); 166 media.view.MediaFrame.Select = __webpack_require__( 48 ); 167 media.view.MediaFrame.Post = __webpack_require__( 49 ); 168 media.view.MediaFrame.ImageDetails = __webpack_require__( 50 ); 169 media.view.Modal = __webpack_require__( 51 ); 170 media.view.FocusManager = __webpack_require__( 52 ); 171 media.view.UploaderWindow = __webpack_require__( 53 ); 172 media.view.EditorUploader = __webpack_require__( 54 ); 173 media.view.UploaderInline = __webpack_require__( 55 ); 174 media.view.UploaderStatus = __webpack_require__( 56 ); 175 media.view.UploaderStatusError = __webpack_require__( 57 ); 176 media.view.Toolbar = __webpack_require__( 58 ); 177 media.view.Toolbar.Select = __webpack_require__( 59 ); 178 media.view.Toolbar.Embed = __webpack_require__( 60 ); 179 media.view.Button = __webpack_require__( 61 ); 180 media.view.ButtonGroup = __webpack_require__( 62 ); 181 media.view.PriorityList = __webpack_require__( 63 ); 182 media.view.MenuItem = __webpack_require__( 64 ); 183 media.view.Menu = __webpack_require__( 65 ); 184 media.view.RouterItem = __webpack_require__( 66 ); 185 media.view.Router = __webpack_require__( 67 ); 186 media.view.Sidebar = __webpack_require__( 68 ); 187 media.view.Attachment = __webpack_require__( 69 ); 188 media.view.Attachment.Library = __webpack_require__( 70 ); 189 media.view.Attachment.EditLibrary = __webpack_require__( 71 ); 190 media.view.Attachments = __webpack_require__( 72 ); 191 media.view.Search = __webpack_require__( 73 ); 192 media.view.AttachmentFilters = __webpack_require__( 74 ); 193 media.view.DateFilter = __webpack_require__( 75 ); 194 media.view.AttachmentFilters.Uploaded = __webpack_require__( 76 ); 195 media.view.AttachmentFilters.All = __webpack_require__( 77 ); 196 media.view.AttachmentsBrowser = __webpack_require__( 78 ); 197 media.view.Selection = __webpack_require__( 79 ); 198 media.view.Attachment.Selection = __webpack_require__( 80 ); 199 media.view.Attachments.Selection = __webpack_require__( 81 ); 200 media.view.Attachment.EditSelection = __webpack_require__( 82 ); 201 media.view.Settings = __webpack_require__( 83 ); 202 media.view.Settings.AttachmentDisplay = __webpack_require__( 84 ); 203 media.view.Settings.Gallery = __webpack_require__( 85 ); 204 media.view.Settings.Playlist = __webpack_require__( 86 ); 205 media.view.Attachment.Details = __webpack_require__( 87 ); 206 media.view.AttachmentCompat = __webpack_require__( 88 ); 207 media.view.Iframe = __webpack_require__( 89 ); 208 media.view.Embed = __webpack_require__( 90 ); 209 media.view.Label = __webpack_require__( 91 ); 210 media.view.EmbedUrl = __webpack_require__( 92 ); 211 media.view.EmbedLink = __webpack_require__( 93 ); 212 media.view.EmbedImage = __webpack_require__( 94 ); 213 media.view.ImageDetails = __webpack_require__( 95 ); 214 media.view.Cropper = __webpack_require__( 96 ); 215 media.view.SiteIconCropper = __webpack_require__( 97 ); 216 media.view.SiteIconPreview = __webpack_require__( 98 ); 217 media.view.EditImage = __webpack_require__( 99 ); 218 media.view.Spinner = __webpack_require__( 100 ); 219 220 221 /***/ }), 222 /* 27 */ 223 /***/ (function(module, exports) { 109 224 110 225 /** 111 * wp.media.controller. CollectionEdit226 * wp.media.controller.Region 112 227 * 113 * A state for editing a collection, which is used by audio and video playlists, 114 * and can be used for other collections. 228 * A region is a persistent application layout area. 229 * 230 * A region assumes one mode at any time, and can be switched to another. 231 * 232 * When mode changes, events are triggered on the region's parent view. 233 * The parent view will listen to specific events and fill the region with an 234 * appropriate view depending on mode. For example, a frame listens for the 235 * 'browse' mode t be activated on the 'content' view and then fills the region 236 * with an AttachmentsBrowser view. 115 237 * 116 238 * @memberOf wp.media.controller 117 239 * 118 240 * @class 119 * @augments wp.media.controller.Library120 * @augments wp.media.controller.State121 * @augments Backbone.Model122 241 * 123 * @param {object} [attributes] The attributes hash passed to the state. 124 * @param {string} attributes.title Title for the state. Displays in the media menu and the frame's title region. 125 * @param {wp.media.model.Attachments} [attributes.library] The attachments collection to edit. 126 * If one is not supplied, an empty media.model.Selection collection is created. 127 * @param {boolean} [attributes.multiple=false] Whether multi-select is enabled. 128 * @param {string} [attributes.content=browse] Initial mode for the content region. 129 * @param {string} attributes.menu Initial mode for the menu region. @todo this needs a better explanation. 130 * @param {boolean} [attributes.searchable=false] Whether the library is searchable. 131 * @param {boolean} [attributes.sortable=true] Whether the Attachments should be sortable. Depends on the orderby property being set to menuOrder on the attachments collection. 132 * @param {boolean} [attributes.date=true] Whether to show the date filter in the browser's toolbar. 133 * @param {boolean} [attributes.describe=true] Whether to offer UI to describe the attachments - e.g. captioning images in a gallery. 134 * @param {boolean} [attributes.dragInfo=true] Whether to show instructional text about the attachments being sortable. 135 * @param {boolean} [attributes.dragInfoText] Instructional text about the attachments being sortable. 136 * @param {int} [attributes.idealColumnWidth=170] The ideal column width in pixels for attachments. 137 * @param {boolean} [attributes.editing=false] Whether the gallery is being created, or editing an existing instance. 138 * @param {int} [attributes.priority=60] The priority for the state link in the media menu. 139 * @param {boolean} [attributes.syncSelection=false] Whether the Attachments selection should be persisted from the last state. 140 * Defaults to false for this state, because the library passed in *is* the selection. 141 * @param {view} [attributes.SettingsView] The view to edit the collection instance settings (e.g. Playlist settings with "Show tracklist" checkbox). 142 * @param {view} [attributes.AttachmentView] The single `Attachment` view to be used in the `Attachments`. 143 * If none supplied, defaults to wp.media.view.Attachment.EditLibrary. 144 * @param {string} attributes.type The collection's media type. (e.g. 'video'). 145 * @param {string} attributes.collectionType The collection type. (e.g. 'playlist'). 242 * @param {object} options Options hash for the region. 243 * @param {string} options.id Unique identifier for the region. 244 * @param {Backbone.View} options.view A parent view the region exists within. 245 * @param {string} options.selector jQuery selector for the region within the parent view. 146 246 */ 147 CollectionEdit = Library.extend(/** @lends wp.media.controller.CollectionEdit.prototype */{ 148 defaults: { 149 multiple: false, 150 sortable: true, 151 date: false, 152 searchable: false, 153 content: 'browse', 154 describe: true, 155 dragInfo: true, 156 idealColumnWidth: 170, 157 editing: false, 158 priority: 60, 159 SettingsView: false, 160 syncSelection: false 161 }, 247 var Region = function( options ) { 248 _.extend( this, _.pick( options || {}, 'id', 'view', 'selector' ) ); 249 }; 250 251 // Use Backbone's self-propagating `extend` inheritance method. 252 Region.extend = Backbone.Model.extend; 162 253 254 _.extend( Region.prototype,/** @lends wp.media.controller.Region.prototype */{ 163 255 /** 164 * @since 3.9.0 256 * Activate a mode. 257 * 258 * @since 3.5.0 259 * 260 * @param {string} mode 261 * 262 * @fires Region#activate 263 * @fires Region#deactivate 264 * 265 * @returns {wp.media.controller.Region} Returns itself to allow chaining. 165 266 */ 166 initialize: function() { 167 var collectionType = this.get('collectionType'); 168 169 if ( 'video' === this.get( 'type' ) ) { 170 collectionType = 'video-' + collectionType; 171 } 172 173 this.set( 'id', collectionType + '-edit' ); 174 this.set( 'toolbar', collectionType + '-edit' ); 175 176 // If we haven't been provided a `library`, create a `Selection`. 177 if ( ! this.get('library') ) { 178 this.set( 'library', new wp.media.model.Selection() ); 267 mode: function( mode ) { 268 if ( ! mode ) { 269 return this._mode; 179 270 } 180 // The single `Attachment` view to be used in the `Attachments` view.181 if ( ! this.get('AttachmentView')) {182 this.set( 'AttachmentView', wp.media.view.Attachment.EditLibrary );271 // Bail if we're trying to change to the current mode. 272 if ( mode === this._mode ) { 273 return this; 183 274 } 184 Library.prototype.initialize.apply( this, arguments );185 },186 187 /**188 * @since 3.9.0189 */190 activate: function() {191 var library = this.get('library');192 275 193 // Limit the library to images only. 194 library.props.set( 'type', this.get( 'type' ) ); 195 196 // Watch for uploaded attachments. 197 this.get('library').observe( wp.Uploader.queue ); 276 /** 277 * Region mode deactivation event. 278 * 279 * @event wp.media.controller.Region#deactivate 280 */ 281 this.trigger('deactivate'); 198 282 199 this.frame.on( 'content:render:browse', this.renderSettings, this ); 283 this._mode = mode; 284 this.render( mode ); 200 285 201 Library.prototype.activate.apply( this, arguments ); 286 /** 287 * Region mode activation event. 288 * 289 * @event wp.media.controller.Region#activate 290 */ 291 this.trigger('activate'); 292 return this; 202 293 }, 203 204 294 /** 205 * @since 3.9.0 295 * Render a mode. 296 * 297 * @since 3.5.0 298 * 299 * @param {string} mode 300 * 301 * @fires Region#create 302 * @fires Region#render 303 * 304 * @returns {wp.media.controller.Region} Returns itself to allow chaining 206 305 */ 207 deactivate: function() { 208 // Stop watching for uploaded attachments. 209 this.get('library').unobserve( wp.Uploader.queue ); 306 render: function( mode ) { 307 // If the mode isn't active, activate it. 308 if ( mode && mode !== this._mode ) { 309 return this.mode( mode ); 310 } 210 311 211 this.frame.off( 'content:render:browse', this.renderSettings, this ); 312 var set = { view: null }, 313 view; 212 314 213 Library.prototype.deactivate.apply( this, arguments ); 315 /** 316 * Create region view event. 317 * 318 * Region view creation takes place in an event callback on the frame. 319 * 320 * @event wp.media.controller.Region#create 321 * @type {object} 322 * @property {object} view 323 */ 324 this.trigger( 'create', set ); 325 view = set.view; 326 327 /** 328 * Render region view event. 329 * 330 * Region view creation takes place in an event callback on the frame. 331 * 332 * @event wp.media.controller.Region#render 333 * @type {object} 334 */ 335 this.trigger( 'render', view ); 336 if ( view ) { 337 this.set( view ); 338 } 339 return this; 214 340 }, 215 341 216 342 /** 217 * Render the collection embed settings view in the browser sidebar. 218 * 219 * @todo This is against the pattern elsewhere in media. Typically the frame 220 * is responsible for adding region mode callbacks. Explain. 343 * Get the region's view. 221 344 * 222 * @since 3. 9.0345 * @since 3.5.0 223 346 * 224 * @ param {wp.media.view.attachmentsBrowser} The attachments browser view.347 * @returns {wp.media.View} 225 348 */ 226 renderSettings: function( attachmentsBrowserView ) { 227 var library = this.get('library'), 228 collectionType = this.get('collectionType'), 229 dragInfoText = this.get('dragInfoText'), 230 SettingsView = this.get('SettingsView'), 231 obj = {}; 349 get: function() { 350 return this.view.views.first( this.selector ); 351 }, 232 352 233 if ( ! library || ! attachmentsBrowserView ) { 234 return; 353 /** 354 * Set the region's view as a subview of the frame. 355 * 356 * @since 3.5.0 357 * 358 * @param {Array|Object} views 359 * @param {Object} [options={}] 360 * @returns {wp.Backbone.Subviews} Subviews is returned to allow chaining 361 */ 362 set: function( views, options ) { 363 if ( options ) { 364 options.add = false; 235 365 } 366 return this.view.views.set( this.selector, views, options ); 367 }, 236 368 237 library[ collectionType ] = library[ collectionType ] || new Backbone.Model(); 238 239 obj[ collectionType ] = new SettingsView({ 240 controller: this, 241 model: library[ collectionType ], 242 priority: 40 243 }); 244 245 attachmentsBrowserView.sidebar.set( obj ); 369 /** 370 * Trigger regional view events on the frame. 371 * 372 * @since 3.5.0 373 * 374 * @param {string} event 375 * @returns {undefined|wp.media.controller.Region} Returns itself to allow chaining. 376 */ 377 trigger: function( event ) { 378 var base, args; 246 379 247 if ( dragInfoText ) { 248 attachmentsBrowserView.toolbar.set( 'dragInfo', new wp.media.View({ 249 el: $( '<div class="instructions">' + dragInfoText + '</div>' )[0], 250 priority: -40 251 }) ); 380 if ( ! this._mode ) { 381 return; 252 382 } 253 383 254 // Add the 'Reverse order' button to the toolbar. 255 attachmentsBrowserView.toolbar.set( 'reverse', { 256 text: l10n.reverseOrder, 257 priority: 80, 384 args = _.toArray( arguments ); 385 base = this.id + ':' + event; 258 386 259 click: function() { 260 library.reset( library.toArray().reverse() ); 261 } 262 }); 387 // Trigger `{this.id}:{event}:{this._mode}` event on the frame. 388 args[0] = base + ':' + this._mode; 389 this.view.trigger.apply( this.view, args ); 390 391 // Trigger `{this.id}:{event}` event on the frame. 392 args[0] = base; 393 this.view.trigger.apply( this.view, args ); 394 return this; 263 395 } 264 396 }); 265 397 266 module.exports = CollectionEdit;398 module.exports = Region; 267 399 268 },{}],3:[function(require,module,exports){ 269 var l10n = wp.media.view.l10n, 270 Cropper; 400 401 /***/ }), 402 /* 28 */ 403 /***/ (function(module, exports) { 271 404 272 405 /** 273 * wp.media.controller. Cropper406 * wp.media.controller.StateMachine 274 407 * 275 * A state for cropping an image. 408 * A state machine keeps track of state. It is in one state at a time, 409 * and can change from one state to another. 410 * 411 * States are stored as models in a Backbone collection. 276 412 * 277 413 * @memberOf wp.media.controller 278 414 * 415 * @since 3.5.0 416 * 279 417 * @class 280 * @augments wp.media.controller.State281 418 * @augments Backbone.Model 419 * @mixin 420 * @mixes Backbone.Events 421 * 422 * @param {Array} states 282 423 */ 283 Cropper = wp.media.controller.State.extend(/** @lends wp.media.controller.Cropper.prototype */{ 284 defaults: { 285 id: 'cropper', 286 title: l10n.cropImage, 287 // Region mode defaults. 288 toolbar: 'crop', 289 content: 'crop', 290 router: false, 291 canSkipCrop: false, 292 293 // Default doCrop Ajax arguments to allow the Customizer (for example) to inject state. 294 doCropArgs: {} 295 }, 424 var StateMachine = function( states ) { 425 // @todo This is dead code. The states collection gets created in media.view.Frame._createStates. 426 this.states = new Backbone.Collection( states ); 427 }; 296 428 297 activate: function() { 298 this.frame.on( 'content:create:crop', this.createCropContent, this ); 299 this.frame.on( 'close', this.removeCropper, this ); 300 this.set('selection', new Backbone.Collection(this.frame._selection.single)); 301 }, 429 // Use Backbone's self-propagating `extend` inheritance method. 430 StateMachine.extend = Backbone.Model.extend; 302 431 303 deactivate: function() { 304 this.frame.toolbar.mode('browse'); 305 }, 432 _.extend( StateMachine.prototype, Backbone.Events,/** @lends wp.media.controller.StateMachine.prototype */{ 433 /** 434 * Fetch a state. 435 * 436 * If no `id` is provided, returns the active state. 437 * 438 * Implicitly creates states. 439 * 440 * Ensure that the `states` collection exists so the `StateMachine` 441 * can be used as a mixin. 442 * 443 * @since 3.5.0 444 * 445 * @param {string} id 446 * @returns {wp.media.controller.State} Returns a State model 447 * from the StateMachine collection 448 */ 449 state: function( id ) { 450 this.states = this.states || new Backbone.Collection(); 306 451 307 createCropContent: function() { 308 this.cropperView = new wp.media.view.Cropper({ 309 controller: this, 310 attachment: this.get('selection').first() 311 }); 312 this.cropperView.on('image-loaded', this.createCropToolbar, this); 313 this.frame.content.set(this.cropperView); 452 // Default to the active state. 453 id = id || this._state; 314 454 455 if ( id && ! this.states.get( id ) ) { 456 this.states.add({ id: id }); 457 } 458 return this.states.get( id ); 315 459 }, 316 removeCropper: function() {317 this.imgSelect.cancelSelection();318 this.imgSelect.setOptions({remove: true});319 this.imgSelect.update();320 this.cropperView.remove();321 },322 createCropToolbar: function() {323 var canSkipCrop, toolbarOptions;324 460 325 canSkipCrop = this.get('canSkipCrop') || false; 461 /** 462 * Sets the active state. 463 * 464 * Bail if we're trying to select the current state, if we haven't 465 * created the `states` collection, or are trying to select a state 466 * that does not exist. 467 * 468 * @since 3.5.0 469 * 470 * @param {string} id 471 * 472 * @fires wp.media.controller.State#deactivate 473 * @fires wp.media.controller.State#activate 474 * 475 * @returns {wp.media.controller.StateMachine} Returns itself to allow chaining 476 */ 477 setState: function( id ) { 478 var previous = this.state(); 326 479 327 toolbarOptions = { 328 controller: this.frame, 329 items: { 330 insert: { 331 style: 'primary', 332 text: l10n.cropImage, 333 priority: 80, 334 requires: { library: false, selection: false }, 335 336 click: function() { 337 var controller = this.controller, 338 selection; 339 340 selection = controller.state().get('selection').first(); 341 selection.set({cropDetails: controller.state().imgSelect.getSelection()}); 342 343 this.$el.text(l10n.cropping); 344 this.$el.attr('disabled', true); 345 346 controller.state().doCrop( selection ).done( function( croppedImage ) { 347 controller.trigger('cropped', croppedImage ); 348 controller.close(); 349 }).fail( function() { 350 controller.trigger('content:error:crop'); 351 }); 352 } 353 } 354 } 355 }; 480 if ( ( previous && id === previous.id ) || ! this.states || ! this.states.get( id ) ) { 481 return this; 482 } 356 483 357 if ( canSkipCrop ) { 358 _.extend( toolbarOptions.items, { 359 skip: { 360 style: 'secondary', 361 text: l10n.skipCropping, 362 priority: 70, 363 requires: { library: false, selection: false }, 364 click: function() { 365 var selection = this.controller.state().get('selection').first(); 366 this.controller.state().cropperView.remove(); 367 this.controller.trigger('skippedcrop', selection); 368 this.controller.close(); 369 } 370 } 371 }); 484 if ( previous ) { 485 previous.trigger('deactivate'); 486 this._lastState = previous.id; 372 487 } 373 488 374 this.frame.toolbar.set( new wp.media.view.Toolbar(toolbarOptions) ); 489 this._state = id; 490 this.state().trigger('activate'); 491 492 return this; 375 493 }, 376 494 377 doCrop: function( attachment ) { 378 return wp.ajax.post( 'custom-header-crop', _.extend( 379 {}, 380 this.defaults.doCropArgs, 381 { 382 nonce: attachment.get( 'nonces' ).edit, 383 id: attachment.get( 'id' ), 384 cropDetails: attachment.get( 'cropDetails' ) 385 } 386 ) ); 495 /** 496 * Returns the previous active state. 497 * 498 * Call the `state()` method with no parameters to retrieve the current 499 * active state. 500 * 501 * @since 3.5.0 502 * 503 * @returns {wp.media.controller.State} Returns a State model 504 * from the StateMachine collection 505 */ 506 lastState: function() { 507 if ( this._lastState ) { 508 return this.state( this._lastState ); 509 } 387 510 } 388 511 }); 389 512 390 module.exports = Cropper; 391 392 },{}],4:[function(require,module,exports){ 393 var Controller = wp.media.controller, 394 CustomizeImageCropper; 395 396 /** 397 * wp.media.controller.CustomizeImageCropper 398 * 399 * @memberOf wp.media.controller 400 * 401 * A state for cropping an image. 402 * 403 * @class 404 * @augments wp.media.controller.Cropper 405 * @augments wp.media.controller.State 406 * @augments Backbone.Model 407 */ 408 CustomizeImageCropper = Controller.Cropper.extend(/** @lends wp.media.controller.CustomizeImageCropper.prototype */{ 409 doCrop: function( attachment ) { 410 var cropDetails = attachment.get( 'cropDetails' ), 411 control = this.get( 'control' ), 412 ratio = cropDetails.width / cropDetails.height; 413 414 // Use crop measurements when flexible in both directions. 415 if ( control.params.flex_width && control.params.flex_height ) { 416 cropDetails.dst_width = cropDetails.width; 417 cropDetails.dst_height = cropDetails.height; 418 419 // Constrain flexible side based on image ratio and size of the fixed side. 420 } else { 421 cropDetails.dst_width = control.params.flex_width ? control.params.height * ratio : control.params.width; 422 cropDetails.dst_height = control.params.flex_height ? control.params.width / ratio : control.params.height; 423 } 424 425 return wp.ajax.post( 'crop-image', { 426 wp_customize: 'on', 427 nonce: attachment.get( 'nonces' ).edit, 428 id: attachment.get( 'id' ), 429 context: control.id, 430 cropDetails: cropDetails 431 } ); 432 } 513 // Map all event binding and triggering on a StateMachine to its `states` collection. 514 _.each([ 'on', 'off', 'trigger' ], function( method ) { 515 /** 516 * @function on 517 * @memberOf wp.media.controller.StateMachine 518 * @instance 519 * @returns {wp.media.controller.StateMachine} Returns itself to allow chaining. 520 */ 521 /** 522 * @function off 523 * @memberOf wp.media.controller.StateMachine 524 * @instance 525 * @returns {wp.media.controller.StateMachine} Returns itself to allow chaining. 526 */ 527 /** 528 * @function trigger 529 * @memberOf wp.media.controller.StateMachine 530 * @instance 531 * @returns {wp.media.controller.StateMachine} Returns itself to allow chaining. 532 */ 533 StateMachine.prototype[ method ] = function() { 534 // Ensure that the `states` collection exists so the `StateMachine` 535 // can be used as a mixin. 536 this.states = this.states || new Backbone.Collection(); 537 // Forward the method to the `states` collection. 538 this.states[ method ].apply( this.states, arguments ); 539 return this; 540 }; 433 541 }); 434 542 435 module.exports = CustomizeImageCropper;543 module.exports = StateMachine; 436 544 437 },{}],5:[function(require,module,exports){ 438 var l10n = wp.media.view.l10n, 439 EditImage; 545 546 /***/ }), 547 /* 29 */ 548 /***/ (function(module, exports) { 440 549 441 550 /** 442 * wp.media.controller. EditImage551 * wp.media.controller.State 443 552 * 444 * A state for editing (cropping, etc.) an image. 553 * A state is a step in a workflow that when set will trigger the controllers 554 * for the regions to be updated as specified in the frame. 555 * 556 * A state has an event-driven lifecycle: 557 * 558 * 'ready' triggers when a state is added to a state machine's collection. 559 * 'activate' triggers when a state is activated by a state machine. 560 * 'deactivate' triggers when a state is deactivated by a state machine. 561 * 'reset' is not triggered automatically. It should be invoked by the 562 * proper controller to reset the state to its default. 445 563 * 446 564 * @memberOf wp.media.controller 447 565 * 448 566 * @class 449 * @augments wp.media.controller.State450 567 * @augments Backbone.Model 451 *452 * @param {object} attributes The attributes hash passed to the state.453 * @param {wp.media.model.Attachment} attributes.model The attachment.454 * @param {string} [attributes.id=edit-image] Unique identifier.455 * @param {string} [attributes.title=Edit Image] Title for the state. Displays in the media menu and the frame's title region.456 * @param {string} [attributes.content=edit-image] Initial mode for the content region.457 * @param {string} [attributes.toolbar=edit-image] Initial mode for the toolbar region.458 * @param {string} [attributes.menu=false] Initial mode for the menu region.459 * @param {string} [attributes.url] Unused. @todo Consider removal.460 568 */ 461 EditImage = wp.media.controller.State.extend(/** @lends wp.media.controller.EditImage.prototype */{ 462 defaults: { 463 id: 'edit-image', 464 title: l10n.editImage, 465 menu: false, 466 toolbar: 'edit-image', 467 content: 'edit-image', 468 url: '' 569 var State = Backbone.Model.extend(/** @lends wp.media.controller.State.prototype */{ 570 /** 571 * Constructor. 572 * 573 * @since 3.5.0 574 */ 575 constructor: function() { 576 this.on( 'activate', this._preActivate, this ); 577 this.on( 'activate', this.activate, this ); 578 this.on( 'activate', this._postActivate, this ); 579 this.on( 'deactivate', this._deactivate, this ); 580 this.on( 'deactivate', this.deactivate, this ); 581 this.on( 'reset', this.reset, this ); 582 this.on( 'ready', this._ready, this ); 583 this.on( 'ready', this.ready, this ); 584 /** 585 * Call parent constructor with passed arguments 586 */ 587 Backbone.Model.apply( this, arguments ); 588 this.on( 'change:menu', this._updateMenu, this ); 469 589 }, 590 /** 591 * Ready event callback. 592 * 593 * @abstract 594 * @since 3.5.0 595 */ 596 ready: function() {}, 470 597 471 598 /** 472 * @since 3.9.0 599 * Activate event callback. 600 * 601 * @abstract 602 * @since 3.5.0 473 603 */ 474 activate: function() { 475 this.frame.on( 'toolbar:render:edit-image', _.bind( this.toolbar, this ) ); 476 }, 604 activate: function() {}, 477 605 478 606 /** 479 * @since 3.9.0 607 * Deactivate event callback. 608 * 609 * @abstract 610 * @since 3.5.0 480 611 */ 481 deactivate: function() { 482 this.frame.off( 'toolbar:render:edit-image' ); 483 }, 612 deactivate: function() {}, 484 613 485 614 /** 486 * @since 3.9.0 615 * Reset event callback. 616 * 617 * @abstract 618 * @since 3.5.0 487 619 */ 488 toolbar: function() { 489 var frame = this.frame, 490 lastState = frame.lastState(), 491 previous = lastState && lastState.id; 620 reset: function() {}, 492 621 493 frame.toolbar.set( new wp.media.view.Toolbar({ 494 controller: frame, 495 items: { 496 back: { 497 style: 'primary', 498 text: l10n.back, 499 priority: 20, 500 click: function() { 501 if ( previous ) { 502 frame.setState( previous ); 503 } else { 504 frame.close(); 505 } 506 } 507 } 508 } 509 }) ); 510 } 511 }); 622 /** 623 * @access private 624 * @since 3.5.0 625 */ 626 _ready: function() { 627 this._updateMenu(); 628 }, 512 629 513 module.exports = EditImage; 630 /** 631 * @access private 632 * @since 3.5.0 633 */ 634 _preActivate: function() { 635 this.active = true; 636 }, 514 637 515 },{}],6:[function(require,module,exports){ 516 var l10n = wp.media.view.l10n, 517 $ = Backbone.$, 518 Embed; 638 /** 639 * @access private 640 * @since 3.5.0 641 */ 642 _postActivate: function() { 643 this.on( 'change:menu', this._menu, this ); 644 this.on( 'change:titleMode', this._title, this ); 645 this.on( 'change:content', this._content, this ); 646 this.on( 'change:toolbar', this._toolbar, this ); 519 647 520 /** 521 * wp.media.controller.Embed 522 * 523 * A state for embedding media from a URL. 524 * 525 * @memberOf wp.media.controller 526 * 527 * @class 528 * @augments wp.media.controller.State 529 * @augments Backbone.Model 530 * 531 * @param {object} attributes The attributes hash passed to the state. 532 * @param {string} [attributes.id=embed] Unique identifier. 533 * @param {string} [attributes.title=Insert From URL] Title for the state. Displays in the media menu and the frame's title region. 534 * @param {string} [attributes.content=embed] Initial mode for the content region. 535 * @param {string} [attributes.menu=default] Initial mode for the menu region. 536 * @param {string} [attributes.toolbar=main-embed] Initial mode for the toolbar region. 537 * @param {string} [attributes.menu=false] Initial mode for the menu region. 538 * @param {int} [attributes.priority=120] The priority for the state link in the media menu. 539 * @param {string} [attributes.type=link] The type of embed. Currently only link is supported. 540 * @param {string} [attributes.url] The embed URL. 541 * @param {object} [attributes.metadata={}] Properties of the embed, which will override attributes.url if set. 542 */ 543 Embed = wp.media.controller.State.extend(/** @lends wp.media.controller.Embed.prototype */{ 544 defaults: { 545 id: 'embed', 546 title: l10n.insertFromUrlTitle, 547 content: 'embed', 548 menu: 'default', 549 toolbar: 'main-embed', 550 priority: 120, 551 type: 'link', 552 url: '', 553 metadata: {} 648 this.frame.on( 'title:render:default', this._renderTitle, this ); 649 650 this._title(); 651 this._menu(); 652 this._toolbar(); 653 this._content(); 654 this._router(); 554 655 }, 555 656 556 // The amount of time used when debouncing the scan. 557 sensitivity: 400, 657 /** 658 * @access private 659 * @since 3.5.0 660 */ 661 _deactivate: function() { 662 this.active = false; 558 663 559 initialize: function(options) { 560 this.metadata = options.metadata; 561 this.debouncedScan = _.debounce( _.bind( this.scan, this ), this.sensitivity ); 562 this.props = new Backbone.Model( this.metadata || { url: '' }); 563 this.props.on( 'change:url', this.debouncedScan, this ); 564 this.props.on( 'change:url', this.refresh, this ); 565 this.on( 'scan', this.scanImage, this ); 664 this.frame.off( 'title:render:default', this._renderTitle, this ); 665 666 this.off( 'change:menu', this._menu, this ); 667 this.off( 'change:titleMode', this._title, this ); 668 this.off( 'change:content', this._content, this ); 669 this.off( 'change:toolbar', this._toolbar, this ); 566 670 }, 567 671 568 672 /** 569 * Trigger a scan of the embedded URL's content for metadata required to embed. 570 * 571 * @fires wp.media.controller.Embed#scan 673 * @access private 674 * @since 3.5.0 572 675 */ 573 scan: function() { 574 var scanners, 575 embed = this, 576 attributes = { 577 type: 'link', 578 scanners: [] 579 }; 676 _title: function() { 677 this.frame.title.render( this.get('titleMode') || 'default' ); 678 }, 580 679 581 // Scan is triggered with the list of `attributes` to set on the 582 // state, useful for the 'type' attribute and 'scanners' attribute, 583 // an array of promise objects for asynchronous scan operations. 584 if ( this.props.get('url') ) { 585 this.trigger( 'scan', attributes ); 586 } 680 /** 681 * @access private 682 * @since 3.5.0 683 */ 684 _renderTitle: function( view ) { 685 view.$el.text( this.get('title') || '' ); 686 }, 587 687 588 if ( attributes.scanners.length ) { 589 scanners = attributes.scanners = $.when.apply( $, attributes.scanners ); 590 scanners.always( function() { 591 if ( embed.get('scanners') === scanners ) { 592 embed.set( 'loading', false ); 593 } 594 }); 595 } else { 596 attributes.scanners = null; 688 /** 689 * @access private 690 * @since 3.5.0 691 */ 692 _router: function() { 693 var router = this.frame.router, 694 mode = this.get('router'), 695 view; 696 697 this.frame.$el.toggleClass( 'hide-router', ! mode ); 698 if ( ! mode ) { 699 return; 597 700 } 598 701 599 attributes.loading = !! attributes.scanners; 600 this.set( attributes ); 702 this.frame.router.render( mode ); 703 704 view = router.get(); 705 if ( view && view.select ) { 706 view.select( this.frame.content.mode() ); 707 } 601 708 }, 709 602 710 /** 603 * Try scanning the embed as an image to discover its dimensions. 604 * 605 * @param {Object} attributes 711 * @access private 712 * @since 3.5.0 606 713 */ 607 scanImage: function( attributes ) { 608 var frame = this.frame, 609 state = this, 610 url = this.props.get('url'), 611 image = new Image(), 612 deferred = $.Deferred(); 714 _menu: function() { 715 var menu = this.frame.menu, 716 mode = this.get('menu'), 717 view; 613 718 614 attributes.scanners.push( deferred.promise() ); 719 this.frame.$el.toggleClass( 'hide-menu', ! mode ); 720 if ( ! mode ) { 721 return; 722 } 615 723 616 // Try to load the image and find its width/height. 617 image.onload = function() { 618 deferred.resolve(); 724 menu.mode( mode ); 619 725 620 if ( state !== frame.state() || url !== state.props.get('url') ) { 621 return; 622 } 726 view = menu.get(); 727 if ( view && view.select ) { 728 view.select( this.id ); 729 } 730 }, 623 731 624 state.set({ 625 type: 'image' 626 }); 732 /** 733 * @access private 734 * @since 3.5.0 735 */ 736 _updateMenu: function() { 737 var previous = this.previous('menu'), 738 menu = this.get('menu'); 627 739 628 state.props.set({ 629 width: image.width, 630 height: image.height 631 }); 632 }; 740 if ( previous ) { 741 this.frame.off( 'menu:render:' + previous, this._renderMenu, this ); 742 } 633 743 634 image.onerror = deferred.reject; 635 image.src = url; 744 if ( menu ) { 745 this.frame.on( 'menu:render:' + menu, this._renderMenu, this ); 746 } 636 747 }, 637 748 638 refresh: function() { 639 this.frame.toolbar.get().refresh(); 640 }, 749 /** 750 * Create a view in the media menu for the state. 751 * 752 * @access private 753 * @since 3.5.0 754 * 755 * @param {media.view.Menu} view The menu view. 756 */ 757 _renderMenu: function( view ) { 758 var menuItem = this.get('menuItem'), 759 title = this.get('title'), 760 priority = this.get('priority'); 641 761 642 reset: function() {643 this.props.clear().set({ url: '' });762 if ( ! menuItem && title ) { 763 menuItem = { text: title }; 644 764 645 if ( this.active ) { 646 this.refresh(); 765 if ( priority ) { 766 menuItem.priority = priority; 767 } 768 } 769 770 if ( ! menuItem ) { 771 return; 647 772 } 773 774 view.set( this.id, menuItem ); 648 775 } 649 776 }); 650 777 651 module.exports = Embed; 778 _.each(['toolbar','content'], function( region ) { 779 /** 780 * @access private 781 */ 782 State.prototype[ '_' + region ] = function() { 783 var mode = this.get( region ); 784 if ( mode ) { 785 this.frame[ region ].render( mode ); 786 } 787 }; 788 }); 789 790 module.exports = State; 652 791 653 },{}],7:[function(require,module,exports){ 654 var Attachment = wp.media.model.Attachment, 655 Library = wp.media.controller.Library, 656 l10n = wp.media.view.l10n, 657 FeaturedImage; 792 793 /***/ }), 794 /* 30 */ 795 /***/ (function(module, exports) { 658 796 659 797 /** 660 * wp.media. controller.FeaturedImage798 * wp.media.selectionSync 661 799 * 662 * A state for selecting a featured image for a post.800 * Sync an attachments selection in a state with another state. 663 801 * 664 * @memberOf wp.media.controller 802 * Allows for selecting multiple images in the Add Media workflow, and then 803 * switching to the Insert Gallery workflow while preserving the attachments selection. 665 804 * 666 * @class 667 * @augments wp.media.controller.Library 668 * @augments wp.media.controller.State 669 * @augments Backbone.Model 805 * @memberOf wp.media 670 806 * 671 * @param {object} [attributes] The attributes hash passed to the state. 672 * @param {string} [attributes.id=featured-image] Unique identifier. 673 * @param {string} [attributes.title=Set Featured Image] Title for the state. Displays in the media menu and the frame's title region. 674 * @param {wp.media.model.Attachments} [attributes.library] The attachments collection to browse. 675 * If one is not supplied, a collection of all images will be created. 676 * @param {boolean} [attributes.multiple=false] Whether multi-select is enabled. 677 * @param {string} [attributes.content=upload] Initial mode for the content region. 678 * Overridden by persistent user setting if 'contentUserSetting' is true. 679 * @param {string} [attributes.menu=default] Initial mode for the menu region. 680 * @param {string} [attributes.router=browse] Initial mode for the router region. 681 * @param {string} [attributes.toolbar=featured-image] Initial mode for the toolbar region. 682 * @param {int} [attributes.priority=60] The priority for the state link in the media menu. 683 * @param {boolean} [attributes.searchable=true] Whether the library is searchable. 684 * @param {boolean|string} [attributes.filterable=false] Whether the library is filterable, and if so what filters should be shown. 685 * Accepts 'all', 'uploaded', or 'unattached'. 686 * @param {boolean} [attributes.sortable=true] Whether the Attachments should be sortable. Depends on the orderby property being set to menuOrder on the attachments collection. 687 * @param {boolean} [attributes.autoSelect=true] Whether an uploaded attachment should be automatically added to the selection. 688 * @param {boolean} [attributes.describe=false] Whether to offer UI to describe attachments - e.g. captioning images in a gallery. 689 * @param {boolean} [attributes.contentUserSetting=true] Whether the content region's mode should be set and persisted per user. 690 * @param {boolean} [attributes.syncSelection=true] Whether the Attachments selection should be persisted from the last state. 807 * @mixin 691 808 */ 692 FeaturedImage = Library.extend(/** @lends wp.media.controller.FeaturedImage.prototype */{ 693 defaults: _.defaults({ 694 id: 'featured-image', 695 title: l10n.setFeaturedImageTitle, 696 multiple: false, 697 filterable: 'uploaded', 698 toolbar: 'featured-image', 699 priority: 60, 700 syncSelection: true 701 }, Library.prototype.defaults ), 702 809 var selectionSync = { 703 810 /** 704 811 * @since 3.5.0 705 812 */ 706 initialize: function() { 707 var library, comparator; 813 syncSelection: function() { 814 var selection = this.get('selection'), 815 manager = this.frame._selection; 708 816 709 // If we haven't been provided a `library`, create a `Selection`. 710 if ( ! this.get('library') ) { 711 this.set( 'library', wp.media.query({ type: 'image' }) ); 817 if ( ! this.get('syncSelection') || ! manager || ! selection ) { 818 return; 712 819 } 713 820 714 Library.prototype.initialize.apply( this, arguments ); 715 716 library = this.get('library'); 717 comparator = library.comparator; 718 719 // Overload the library's comparator to push items that are not in 720 // the mirrored query to the front of the aggregate collection. 721 library.comparator = function( a, b ) { 722 var aInQuery = !! this.mirroring.get( a.cid ), 723 bInQuery = !! this.mirroring.get( b.cid ); 724 725 if ( ! aInQuery && bInQuery ) { 726 return -1; 727 } else if ( aInQuery && ! bInQuery ) { 728 return 1; 729 } else { 730 return comparator.apply( this, arguments ); 731 } 732 }; 733 734 // Add all items in the selection to the library, so any featured 735 // images that are not initially loaded still appear. 736 library.observe( this.get('selection') ); 737 }, 738 739 /** 740 * @since 3.5.0 741 */ 742 activate: function() { 743 this.updateSelection(); 744 this.frame.on( 'open', this.updateSelection, this ); 745 746 Library.prototype.activate.apply( this, arguments ); 747 }, 748 749 /** 750 * @since 3.5.0 751 */ 752 deactivate: function() { 753 this.frame.off( 'open', this.updateSelection, this ); 821 // If the selection supports multiple items, validate the stored 822 // attachments based on the new selection's conditions. Record 823 // the attachments that are not included; we'll maintain a 824 // reference to those. Other attachments are considered in flux. 825 if ( selection.multiple ) { 826 selection.reset( [], { silent: true }); 827 selection.validateAll( manager.attachments ); 828 manager.difference = _.difference( manager.attachments.models, selection.models ); 829 } 754 830 755 Library.prototype.deactivate.apply( this, arguments ); 831 // Sync the selection's single item with the master. 832 selection.single( manager.single ); 756 833 }, 757 834 758 835 /** 836 * Record the currently active attachments, which is a combination 837 * of the selection's attachments and the set of selected 838 * attachments that this specific selection considered invalid. 839 * Reset the difference and record the single attachment. 840 * 759 841 * @since 3.5.0 760 842 */ 761 updateSelection: function() {843 recordSelection: function() { 762 844 var selection = this.get('selection'), 763 id = wp.media.view.settings.post.featuredImageId, 764 attachment; 845 manager = this.frame._selection; 765 846 766 if ( '' !== id && -1 !== id ) { 767 attachment = Attachment.get( id ); 768 attachment.fetch(); 847 if ( ! this.get('syncSelection') || ! manager || ! selection ) { 848 return; 769 849 } 770 850 771 selection.reset( attachment ? [ attachment ] : [] ); 851 if ( selection.multiple ) { 852 manager.attachments.reset( selection.toArray().concat( manager.difference ) ); 853 manager.difference = []; 854 } else { 855 manager.attachments.add( selection.toArray() ); 856 } 857 858 manager.single = selection._single; 772 859 } 773 } );860 }; 774 861 775 module.exports = FeaturedImage;862 module.exports = selectionSync; 776 863 777 },{}],8:[function(require,module,exports){ 778 var Selection = wp.media.model.Selection, 779 Library = wp.media.controller.Library, 780 l10n = wp.media.view.l10n, 781 GalleryAdd; 864 865 /***/ }), 866 /* 31 */ 867 /***/ (function(module, exports) { 868 869 var l10n = wp.media.view.l10n, 870 getUserSetting = window.getUserSetting, 871 setUserSetting = window.setUserSetting, 872 Library; 782 873 783 874 /** 784 * wp.media.controller. GalleryAdd875 * wp.media.controller.Library 785 876 * 786 * A state for selecting more images to add to a gallery.877 * A state for choosing an attachment or group of attachments from the media library. 787 878 * 788 879 * @memberOf wp.media.controller 789 880 * 790 881 * @class 791 * @augments wp.media.controller.Library792 882 * @augments wp.media.controller.State 793 883 * @augments Backbone.Model 884 * @mixes media.selectionSync 794 885 * 795 * @param {object} [attributes] The attributes hash passed to the state. 796 * @param {string} [attributes.id=gallery-library] Unique identifier. 797 * @param {string} [attributes.title=Add to Gallery] Title for the state. Displays in the frame's title region. 798 * @param {boolean} [attributes.multiple=add] Whether multi-select is enabled. @todo 'add' doesn't seem do anything special, and gets used as a boolean. 799 * @param {wp.media.model.Attachments} [attributes.library] The attachments collection to browse. 800 * If one is not supplied, a collection of all images will be created. 801 * @param {boolean|string} [attributes.filterable=uploaded] Whether the library is filterable, and if so what filters should be shown. 802 * Accepts 'all', 'uploaded', or 'unattached'. 803 * @param {string} [attributes.menu=gallery] Initial mode for the menu region. 804 * @param {string} [attributes.content=upload] Initial mode for the content region. 805 * Overridden by persistent user setting if 'contentUserSetting' is true. 806 * @param {string} [attributes.router=browse] Initial mode for the router region. 807 * @param {string} [attributes.toolbar=gallery-add] Initial mode for the toolbar region. 808 * @param {boolean} [attributes.searchable=true] Whether the library is searchable. 809 * @param {boolean} [attributes.sortable=true] Whether the Attachments should be sortable. Depends on the orderby property being set to menuOrder on the attachments collection. 810 * @param {boolean} [attributes.autoSelect=true] Whether an uploaded attachment should be automatically added to the selection. 811 * @param {boolean} [attributes.contentUserSetting=true] Whether the content region's mode should be set and persisted per user. 812 * @param {int} [attributes.priority=100] The priority for the state link in the media menu. 813 * @param {boolean} [attributes.syncSelection=false] Whether the Attachments selection should be persisted from the last state. 814 * Defaults to false because for this state, because the library of the Edit Gallery state is the selection. 886 * @param {object} [attributes] The attributes hash passed to the state. 887 * @param {string} [attributes.id=library] Unique identifier. 888 * @param {string} [attributes.title=Media library] Title for the state. Displays in the media menu and the frame's title region. 889 * @param {wp.media.model.Attachments} [attributes.library] The attachments collection to browse. 890 * If one is not supplied, a collection of all attachments will be created. 891 * @param {wp.media.model.Selection|object} [attributes.selection] A collection to contain attachment selections within the state. 892 * If the 'selection' attribute is a plain JS object, 893 * a Selection will be created using its values as the selection instance's `props` model. 894 * Otherwise, it will copy the library's `props` model. 895 * @param {boolean} [attributes.multiple=false] Whether multi-select is enabled. 896 * @param {string} [attributes.content=upload] Initial mode for the content region. 897 * Overridden by persistent user setting if 'contentUserSetting' is true. 898 * @param {string} [attributes.menu=default] Initial mode for the menu region. 899 * @param {string} [attributes.router=browse] Initial mode for the router region. 900 * @param {string} [attributes.toolbar=select] Initial mode for the toolbar region. 901 * @param {boolean} [attributes.searchable=true] Whether the library is searchable. 902 * @param {boolean|string} [attributes.filterable=false] Whether the library is filterable, and if so what filters should be shown. 903 * Accepts 'all', 'uploaded', or 'unattached'. 904 * @param {boolean} [attributes.sortable=true] Whether the Attachments should be sortable. Depends on the orderby property being set to menuOrder on the attachments collection. 905 * @param {boolean} [attributes.autoSelect=true] Whether an uploaded attachment should be automatically added to the selection. 906 * @param {boolean} [attributes.describe=false] Whether to offer UI to describe attachments - e.g. captioning images in a gallery. 907 * @param {boolean} [attributes.contentUserSetting=true] Whether the content region's mode should be set and persisted per user. 908 * @param {boolean} [attributes.syncSelection=true] Whether the Attachments selection should be persisted from the last state. 815 909 */ 816 GalleryAdd = Library.extend(/** @lends wp.media.controller.GalleryAdd.prototype */{ 817 defaults: _.defaults({ 818 id: 'gallery-library', 819 title: l10n.addToGalleryTitle, 820 multiple: 'add', 821 filterable: 'uploaded', 822 menu: 'gallery', 823 toolbar: 'gallery-add', 824 priority: 100, 825 syncSelection: false 826 }, Library.prototype.defaults ), 910 Library = wp.media.controller.State.extend(/** @lends wp.media.controller.Library.prototype */{ 911 defaults: { 912 id: 'library', 913 title: l10n.mediaLibraryTitle, 914 multiple: false, 915 content: 'upload', 916 menu: 'default', 917 router: 'browse', 918 toolbar: 'select', 919 searchable: true, 920 filterable: false, 921 sortable: true, 922 autoSelect: true, 923 describe: false, 924 contentUserSetting: true, 925 syncSelection: true 926 }, 827 927 828 928 /** 929 * If a library isn't provided, query all media items. 930 * If a selection instance isn't provided, create one. 931 * 829 932 * @since 3.5.0 830 933 */ 831 934 initialize: function() { 832 // If a library wasn't supplied, create a library of images. 935 var selection = this.get('selection'), 936 props; 937 833 938 if ( ! this.get('library') ) { 834 this.set( 'library', wp.media.query( { type: 'image' }) );939 this.set( 'library', wp.media.query() ); 835 940 } 836 941 837 Library.prototype.initialize.apply( this, arguments ); 942 if ( ! ( selection instanceof wp.media.model.Selection ) ) { 943 props = selection; 944 945 if ( ! props ) { 946 props = this.get('library').props.toJSON(); 947 props = _.omit( props, 'orderby', 'query' ); 948 } 949 950 this.set( 'selection', new wp.media.model.Selection( null, { 951 multiple: this.get('multiple'), 952 props: props 953 }) ); 954 } 955 956 this.resetDisplays(); 838 957 }, 839 958 840 959 /** 841 960 * @since 3.5.0 842 961 */ 843 962 activate: function() { 844 var library = this.get('library'), 845 edit = this.frame.state('gallery-edit').get('library'); 963 this.syncSelection(); 846 964 847 if ( this.editLibrary && this.editLibrary !== edit ) { 848 library.unobserve( this.editLibrary ); 849 } 965 wp.Uploader.queue.on( 'add', this.uploading, this ); 850 966 851 // Accepts attachments that exist in the original library and 852 // that do not exist in gallery's library. 853 library.validator = function( attachment ) { 854 return !! this.mirroring.get( attachment.cid ) && ! edit.get( attachment.cid ) && Selection.prototype.validator.apply( this, arguments ); 855 }; 967 this.get('selection').on( 'add remove reset', this.refreshContent, this ); 856 968 857 // Reset the library to ensure that all attachments are re-added 858 // to the collection. Do so silently, as calling `observe` will 859 // trigger the `reset` event. 860 library.reset( library.mirroring.models, { silent: true }); 861 library.observe( edit ); 862 this.editLibrary = edit; 969 if ( this.get( 'router' ) && this.get('contentUserSetting') ) { 970 this.frame.on( 'content:activate', this.saveContentMode, this ); 971 this.set( 'content', getUserSetting( 'libraryContent', this.get('content') ) ); 972 } 973 }, 863 974 864 Library.prototype.activate.apply( this, arguments ); 865 } 866 }); 975 /** 976 * @since 3.5.0 977 */ 978 deactivate: function() { 979 this.recordSelection(); 867 980 868 module.exports = GalleryAdd;981 this.frame.off( 'content:activate', this.saveContentMode, this ); 869 982 870 },{}],9:[function(require,module,exports){ 871 var Library = wp.media.controller.Library, 872 l10n = wp.media.view.l10n, 873 GalleryEdit; 983 // Unbind all event handlers that use this state as the context 984 // from the selection. 985 this.get('selection').off( null, null, this ); 874 986 875 /** 876 * wp.media.controller.GalleryEdit 877 * 878 * A state for editing a gallery's images and settings. 879 * 880 * @memberOf wp.media.controller 881 * 882 * @class 883 * @augments wp.media.controller.Library 884 * @augments wp.media.controller.State 885 * @augments Backbone.Model 886 * 887 * @param {object} [attributes] The attributes hash passed to the state. 888 * @param {string} [attributes.id=gallery-edit] Unique identifier. 889 * @param {string} [attributes.title=Edit Gallery] Title for the state. Displays in the frame's title region. 890 * @param {wp.media.model.Attachments} [attributes.library] The collection of attachments in the gallery. 891 * If one is not supplied, an empty media.model.Selection collection is created. 892 * @param {boolean} [attributes.multiple=false] Whether multi-select is enabled. 893 * @param {boolean} [attributes.searchable=false] Whether the library is searchable. 894 * @param {boolean} [attributes.sortable=true] Whether the Attachments should be sortable. Depends on the orderby property being set to menuOrder on the attachments collection. 895 * @param {boolean} [attributes.date=true] Whether to show the date filter in the browser's toolbar. 896 * @param {string|false} [attributes.content=browse] Initial mode for the content region. 897 * @param {string|false} [attributes.toolbar=image-details] Initial mode for the toolbar region. 898 * @param {boolean} [attributes.describe=true] Whether to offer UI to describe attachments - e.g. captioning images in a gallery. 899 * @param {boolean} [attributes.displaySettings=true] Whether to show the attachment display settings interface. 900 * @param {boolean} [attributes.dragInfo=true] Whether to show instructional text about the attachments being sortable. 901 * @param {int} [attributes.idealColumnWidth=170] The ideal column width in pixels for attachments. 902 * @param {boolean} [attributes.editing=false] Whether the gallery is being created, or editing an existing instance. 903 * @param {int} [attributes.priority=60] The priority for the state link in the media menu. 904 * @param {boolean} [attributes.syncSelection=false] Whether the Attachments selection should be persisted from the last state. 905 * Defaults to false for this state, because the library passed in *is* the selection. 906 * @param {view} [attributes.AttachmentView] The single `Attachment` view to be used in the `Attachments`. 907 * If none supplied, defaults to wp.media.view.Attachment.EditLibrary. 908 */ 909 GalleryEdit = Library.extend(/** @lends wp.media.controller.GalleryEdit.prototype */{ 910 defaults: { 911 id: 'gallery-edit', 912 title: l10n.editGalleryTitle, 913 multiple: false, 914 searchable: false, 915 sortable: true, 916 date: false, 917 display: false, 918 content: 'browse', 919 toolbar: 'gallery-edit', 920 describe: true, 921 displaySettings: true, 922 dragInfo: true, 923 idealColumnWidth: 170, 924 editing: false, 925 priority: 60, 926 syncSelection: false 987 wp.Uploader.queue.off( null, null, this ); 927 988 }, 928 989 929 990 /** 991 * Reset the library to its initial state. 992 * 930 993 * @since 3.5.0 931 994 */ 932 initialize: function() { 933 // If we haven't been provided a `library`, create a `Selection`. 934 if ( ! this.get('library') ) { 935 this.set( 'library', new wp.media.model.Selection() ); 995 reset: function() { 996 this.get('selection').reset(); 997 this.resetDisplays(); 998 this.refreshContent(); 999 }, 1000 1001 /** 1002 * Reset the attachment display settings defaults to the site options. 1003 * 1004 * If site options don't define them, fall back to a persistent user setting. 1005 * 1006 * @since 3.5.0 1007 */ 1008 resetDisplays: function() { 1009 var defaultProps = wp.media.view.settings.defaultProps; 1010 this._displays = []; 1011 this._defaultDisplaySettings = { 1012 align: getUserSetting( 'align', defaultProps.align ) || 'none', 1013 size: getUserSetting( 'imgsize', defaultProps.size ) || 'medium', 1014 link: getUserSetting( 'urlbutton', defaultProps.link ) || 'none' 1015 }; 1016 }, 1017 1018 /** 1019 * Create a model to represent display settings (alignment, etc.) for an attachment. 1020 * 1021 * @since 3.5.0 1022 * 1023 * @param {wp.media.model.Attachment} attachment 1024 * @returns {Backbone.Model} 1025 */ 1026 display: function( attachment ) { 1027 var displays = this._displays; 1028 1029 if ( ! displays[ attachment.cid ] ) { 1030 displays[ attachment.cid ] = new Backbone.Model( this.defaultDisplaySettings( attachment ) ); 936 1031 } 1032 return displays[ attachment.cid ]; 1033 }, 937 1034 938 // The single `Attachment` view to be used in the `Attachments` view. 939 if ( ! this.get('AttachmentView') ) { 940 this.set( 'AttachmentView', wp.media.view.Attachment.EditLibrary ); 1035 /** 1036 * Given an attachment, create attachment display settings properties. 1037 * 1038 * @since 3.6.0 1039 * 1040 * @param {wp.media.model.Attachment} attachment 1041 * @returns {Object} 1042 */ 1043 defaultDisplaySettings: function( attachment ) { 1044 var settings = _.clone( this._defaultDisplaySettings ); 1045 1046 if ( settings.canEmbed = this.canEmbed( attachment ) ) { 1047 settings.link = 'embed'; 1048 } else if ( ! this.isImageAttachment( attachment ) && settings.link === 'none' ) { 1049 settings.link = 'file'; 941 1050 } 942 1051 943 Library.prototype.initialize.apply( this, arguments );1052 return settings; 944 1053 }, 945 1054 946 1055 /** 947 * @since 3.5.0 1056 * Whether an attachment is image. 1057 * 1058 * @since 4.4.1 1059 * 1060 * @param {wp.media.model.Attachment} attachment 1061 * @returns {Boolean} 948 1062 */ 949 activate: function() { 950 var library = this.get('library'); 1063 isImageAttachment: function( attachment ) { 1064 // If uploading, we know the filename but not the mime type. 1065 if ( attachment.get('uploading') ) { 1066 return /\.(jpe?g|png|gif)$/i.test( attachment.get('filename') ); 1067 } 951 1068 952 // Limit the library to images only.953 library.props.set( 'type', 'image' );1069 return attachment.get('type') === 'image'; 1070 }, 954 1071 955 // Watch for uploaded attachments. 956 this.get('library').observe( wp.Uploader.queue ); 1072 /** 1073 * Whether an attachment can be embedded (audio or video). 1074 * 1075 * @since 3.6.0 1076 * 1077 * @param {wp.media.model.Attachment} attachment 1078 * @returns {Boolean} 1079 */ 1080 canEmbed: function( attachment ) { 1081 // If uploading, we know the filename but not the mime type. 1082 if ( ! attachment.get('uploading') ) { 1083 var type = attachment.get('type'); 1084 if ( type !== 'audio' && type !== 'video' ) { 1085 return false; 1086 } 1087 } 957 1088 958 this.frame.on( 'content:render:browse', this.gallerySettings, this ); 1089 return _.contains( wp.media.view.settings.embedExts, attachment.get('filename').split('.').pop() ); 1090 }, 959 1091 960 Library.prototype.activate.apply( this, arguments ); 1092 1093 /** 1094 * If the state is active, no items are selected, and the current 1095 * content mode is not an option in the state's router (provided 1096 * the state has a router), reset the content mode to the default. 1097 * 1098 * @since 3.5.0 1099 */ 1100 refreshContent: function() { 1101 var selection = this.get('selection'), 1102 frame = this.frame, 1103 router = frame.router.get(), 1104 mode = frame.content.mode(); 1105 1106 if ( this.active && ! selection.length && router && ! router.get( mode ) ) { 1107 this.frame.content.render( this.get('content') ); 1108 } 961 1109 }, 962 1110 963 1111 /** 1112 * Callback handler when an attachment is uploaded. 1113 * 1114 * Switch to the Media Library if uploaded from the 'Upload Files' tab. 1115 * 1116 * Adds any uploading attachments to the selection. 1117 * 1118 * If the state only supports one attachment to be selected and multiple 1119 * attachments are uploaded, the last attachment in the upload queue will 1120 * be selected. 1121 * 964 1122 * @since 3.5.0 1123 * 1124 * @param {wp.media.model.Attachment} attachment 965 1125 */ 966 deactivate: function() { 967 // Stop watching for uploaded attachments. 968 this.get('library').unobserve( wp.Uploader.queue ); 1126 uploading: function( attachment ) { 1127 var content = this.frame.content; 969 1128 970 this.frame.off( 'content:render:browse', this.gallerySettings, this ); 1129 if ( 'upload' === content.mode() ) { 1130 this.frame.content.mode('browse'); 1131 } 971 1132 972 Library.prototype.deactivate.apply( this, arguments ); 1133 if ( this.get( 'autoSelect' ) ) { 1134 this.get('selection').add( attachment ); 1135 this.frame.trigger( 'library:selection:add' ); 1136 } 973 1137 }, 974 1138 975 1139 /** 976 * @since 3.5.01140 * Persist the mode of the content region as a user setting. 977 1141 * 978 * @ param browser1142 * @since 3.5.0 979 1143 */ 980 gallerySettings: function( browser) {981 if ( ! this.get('displaySettings') ) {1144 saveContentMode: function() { 1145 if ( 'browse' !== this.get('router') ) { 982 1146 return; 983 1147 } 984 1148 985 var library = this.get('library'); 1149 var mode = this.frame.content.mode(), 1150 view = this.frame.router.get(); 986 1151 987 if ( ! library || ! browser) {988 return;1152 if ( view && view.get( mode ) ) { 1153 setUserSetting( 'libraryContent', mode ); 989 1154 } 1155 } 1156 }); 990 1157 991 library.gallery = library.gallery || new Backbone.Model(); 1158 // Make selectionSync available on any Media Library state. 1159 _.extend( Library.prototype, wp.media.selectionSync ); 992 1160 993 browser.sidebar.set({ 994 gallery: new wp.media.view.Settings.Gallery({ 995 controller: this, 996 model: library.gallery, 997 priority: 40 998 }) 999 }); 1161 module.exports = Library; 1000 1162 1001 browser.toolbar.set( 'reverse', {1002 text: l10n.reverseOrder,1003 priority: 80,1004 1005 click: function() {1006 library.reset( library.toArray().reverse() );1007 }1008 });1009 }1010 });1011 1163 1012 module.exports = GalleryEdit; 1164 /***/ }), 1165 /* 32 */ 1166 /***/ (function(module, exports) { 1013 1167 1014 },{}],10:[function(require,module,exports){1015 1168 var State = wp.media.controller.State, 1016 1169 Library = wp.media.controller.Library, 1017 1170 l10n = wp.media.view.l10n, … … ImageDetails = State.extend(/** @lends wp.media.controller.ImageDetails.prototyp 1075 1228 1076 1229 module.exports = ImageDetails; 1077 1230 1078 },{}],11:[function(require,module,exports){ 1079 var l10n = wp.media.view.l10n, 1080 getUserSetting = window.getUserSetting, 1081 setUserSetting = window.setUserSetting, 1082 Library; 1231 1232 /***/ }), 1233 /* 33 */ 1234 /***/ (function(module, exports) { 1235 1236 var Library = wp.media.controller.Library, 1237 l10n = wp.media.view.l10n, 1238 GalleryEdit; 1083 1239 1084 1240 /** 1085 * wp.media.controller. Library1241 * wp.media.controller.GalleryEdit 1086 1242 * 1087 * A state for choosing an attachment or group of attachments from the media library.1243 * A state for editing a gallery's images and settings. 1088 1244 * 1089 1245 * @memberOf wp.media.controller 1090 1246 * 1091 1247 * @class 1248 * @augments wp.media.controller.Library 1092 1249 * @augments wp.media.controller.State 1093 1250 * @augments Backbone.Model 1094 * @mixes media.selectionSync1095 1251 * 1096 * @param {object} [attributes] The attributes hash passed to the state. 1097 * @param {string} [attributes.id=library] Unique identifier. 1098 * @param {string} [attributes.title=Media library] Title for the state. Displays in the media menu and the frame's title region. 1099 * @param {wp.media.model.Attachments} [attributes.library] The attachments collection to browse. 1100 * If one is not supplied, a collection of all attachments will be created. 1101 * @param {wp.media.model.Selection|object} [attributes.selection] A collection to contain attachment selections within the state. 1102 * If the 'selection' attribute is a plain JS object, 1103 * a Selection will be created using its values as the selection instance's `props` model. 1104 * Otherwise, it will copy the library's `props` model. 1105 * @param {boolean} [attributes.multiple=false] Whether multi-select is enabled. 1106 * @param {string} [attributes.content=upload] Initial mode for the content region. 1107 * Overridden by persistent user setting if 'contentUserSetting' is true. 1108 * @param {string} [attributes.menu=default] Initial mode for the menu region. 1109 * @param {string} [attributes.router=browse] Initial mode for the router region. 1110 * @param {string} [attributes.toolbar=select] Initial mode for the toolbar region. 1111 * @param {boolean} [attributes.searchable=true] Whether the library is searchable. 1112 * @param {boolean|string} [attributes.filterable=false] Whether the library is filterable, and if so what filters should be shown. 1113 * Accepts 'all', 'uploaded', or 'unattached'. 1114 * @param {boolean} [attributes.sortable=true] Whether the Attachments should be sortable. Depends on the orderby property being set to menuOrder on the attachments collection. 1115 * @param {boolean} [attributes.autoSelect=true] Whether an uploaded attachment should be automatically added to the selection. 1116 * @param {boolean} [attributes.describe=false] Whether to offer UI to describe attachments - e.g. captioning images in a gallery. 1117 * @param {boolean} [attributes.contentUserSetting=true] Whether the content region's mode should be set and persisted per user. 1118 * @param {boolean} [attributes.syncSelection=true] Whether the Attachments selection should be persisted from the last state. 1252 * @param {object} [attributes] The attributes hash passed to the state. 1253 * @param {string} [attributes.id=gallery-edit] Unique identifier. 1254 * @param {string} [attributes.title=Edit Gallery] Title for the state. Displays in the frame's title region. 1255 * @param {wp.media.model.Attachments} [attributes.library] The collection of attachments in the gallery. 1256 * If one is not supplied, an empty media.model.Selection collection is created. 1257 * @param {boolean} [attributes.multiple=false] Whether multi-select is enabled. 1258 * @param {boolean} [attributes.searchable=false] Whether the library is searchable. 1259 * @param {boolean} [attributes.sortable=true] Whether the Attachments should be sortable. Depends on the orderby property being set to menuOrder on the attachments collection. 1260 * @param {boolean} [attributes.date=true] Whether to show the date filter in the browser's toolbar. 1261 * @param {string|false} [attributes.content=browse] Initial mode for the content region. 1262 * @param {string|false} [attributes.toolbar=image-details] Initial mode for the toolbar region. 1263 * @param {boolean} [attributes.describe=true] Whether to offer UI to describe attachments - e.g. captioning images in a gallery. 1264 * @param {boolean} [attributes.displaySettings=true] Whether to show the attachment display settings interface. 1265 * @param {boolean} [attributes.dragInfo=true] Whether to show instructional text about the attachments being sortable. 1266 * @param {int} [attributes.idealColumnWidth=170] The ideal column width in pixels for attachments. 1267 * @param {boolean} [attributes.editing=false] Whether the gallery is being created, or editing an existing instance. 1268 * @param {int} [attributes.priority=60] The priority for the state link in the media menu. 1269 * @param {boolean} [attributes.syncSelection=false] Whether the Attachments selection should be persisted from the last state. 1270 * Defaults to false for this state, because the library passed in *is* the selection. 1271 * @param {view} [attributes.AttachmentView] The single `Attachment` view to be used in the `Attachments`. 1272 * If none supplied, defaults to wp.media.view.Attachment.EditLibrary. 1119 1273 */ 1120 Library = wp.media.controller.State.extend(/** @lends wp.media.controller.Library.prototype */{1274 GalleryEdit = Library.extend(/** @lends wp.media.controller.GalleryEdit.prototype */{ 1121 1275 defaults: { 1122 id: 'library', 1123 title: l10n.mediaLibraryTitle, 1124 multiple: false, 1125 content: 'upload', 1126 menu: 'default', 1127 router: 'browse', 1128 toolbar: 'select', 1129 searchable: true, 1130 filterable: false, 1131 sortable: true, 1132 autoSelect: true, 1133 describe: false, 1134 contentUserSetting: true, 1135 syncSelection: true 1276 id: 'gallery-edit', 1277 title: l10n.editGalleryTitle, 1278 multiple: false, 1279 searchable: false, 1280 sortable: true, 1281 date: false, 1282 display: false, 1283 content: 'browse', 1284 toolbar: 'gallery-edit', 1285 describe: true, 1286 displaySettings: true, 1287 dragInfo: true, 1288 idealColumnWidth: 170, 1289 editing: false, 1290 priority: 60, 1291 syncSelection: false 1136 1292 }, 1137 1293 1138 1294 /** 1139 * If a library isn't provided, query all media items.1140 * If a selection instance isn't provided, create one.1141 *1142 1295 * @since 3.5.0 1143 1296 */ 1144 1297 initialize: function() { 1145 var selection = this.get('selection'), 1146 props; 1147 1298 // If we haven't been provided a `library`, create a `Selection`. 1148 1299 if ( ! this.get('library') ) { 1149 this.set( 'library', wp.media.query() );1300 this.set( 'library', new wp.media.model.Selection() ); 1150 1301 } 1151 1302 1152 if ( ! ( selection instanceof wp.media.model.Selection ) ) { 1153 props = selection; 1154 1155 if ( ! props ) { 1156 props = this.get('library').props.toJSON(); 1157 props = _.omit( props, 'orderby', 'query' ); 1158 } 1159 1160 this.set( 'selection', new wp.media.model.Selection( null, { 1161 multiple: this.get('multiple'), 1162 props: props 1163 }) ); 1303 // The single `Attachment` view to be used in the `Attachments` view. 1304 if ( ! this.get('AttachmentView') ) { 1305 this.set( 'AttachmentView', wp.media.view.Attachment.EditLibrary ); 1164 1306 } 1165 1307 1166 this.resetDisplays();1308 Library.prototype.initialize.apply( this, arguments ); 1167 1309 }, 1168 1310 1169 1311 /** 1170 1312 * @since 3.5.0 1171 1313 */ 1172 1314 activate: function() { 1173 this.syncSelection();1315 var library = this.get('library'); 1174 1316 1175 wp.Uploader.queue.on( 'add', this.uploading, this ); 1317 // Limit the library to images only. 1318 library.props.set( 'type', 'image' ); 1176 1319 1177 this.get('selection').on( 'add remove reset', this.refreshContent, this ); 1320 // Watch for uploaded attachments. 1321 this.get('library').observe( wp.Uploader.queue ); 1178 1322 1179 if ( this.get( 'router' ) && this.get('contentUserSetting') ) { 1180 this.frame.on( 'content:activate', this.saveContentMode, this ); 1181 this.set( 'content', getUserSetting( 'libraryContent', this.get('content') ) ); 1182 } 1323 this.frame.on( 'content:render:browse', this.gallerySettings, this ); 1324 1325 Library.prototype.activate.apply( this, arguments ); 1183 1326 }, 1184 1327 1185 1328 /** 1186 1329 * @since 3.5.0 1187 1330 */ 1188 1331 deactivate: function() { 1189 this.recordSelection(); 1190 1191 this.frame.off( 'content:activate', this.saveContentMode, this ); 1332 // Stop watching for uploaded attachments. 1333 this.get('library').unobserve( wp.Uploader.queue ); 1192 1334 1193 // Unbind all event handlers that use this state as the context 1194 // from the selection. 1195 this.get('selection').off( null, null, this ); 1335 this.frame.off( 'content:render:browse', this.gallerySettings, this ); 1196 1336 1197 wp.Uploader.queue.off( null, null, this );1337 Library.prototype.deactivate.apply( this, arguments ); 1198 1338 }, 1199 1339 1200 1340 /** 1201 * Reset the library to its initial state.1202 *1203 1341 * @since 3.5.0 1204 */1205 reset: function() {1206 this.get('selection').reset();1207 this.resetDisplays();1208 this.refreshContent();1209 },1210 1211 /**1212 * Reset the attachment display settings defaults to the site options.1213 *1214 * If site options don't define them, fall back to a persistent user setting.1215 1342 * 1216 * @ since 3.5.01343 * @param browser 1217 1344 */ 1218 resetDisplays: function() { 1219 var defaultProps = wp.media.view.settings.defaultProps; 1220 this._displays = []; 1221 this._defaultDisplaySettings = { 1222 align: getUserSetting( 'align', defaultProps.align ) || 'none', 1223 size: getUserSetting( 'imgsize', defaultProps.size ) || 'medium', 1224 link: getUserSetting( 'urlbutton', defaultProps.link ) || 'none' 1225 }; 1226 }, 1345 gallerySettings: function( browser ) { 1346 if ( ! this.get('displaySettings') ) { 1347 return; 1348 } 1227 1349 1228 /** 1229 * Create a model to represent display settings (alignment, etc.) for an attachment. 1230 * 1231 * @since 3.5.0 1232 * 1233 * @param {wp.media.model.Attachment} attachment 1234 * @returns {Backbone.Model} 1235 */ 1236 display: function( attachment ) { 1237 var displays = this._displays; 1350 var library = this.get('library'); 1238 1351 1239 if ( ! displays[ attachment.cid ]) {1240 displays[ attachment.cid ] = new Backbone.Model( this.defaultDisplaySettings( attachment ) );1352 if ( ! library || ! browser ) { 1353 return; 1241 1354 } 1242 return displays[ attachment.cid ];1243 },1244 1355 1245 /** 1246 * Given an attachment, create attachment display settings properties. 1247 * 1248 * @since 3.6.0 1249 * 1250 * @param {wp.media.model.Attachment} attachment 1251 * @returns {Object} 1252 */ 1253 defaultDisplaySettings: function( attachment ) { 1254 var settings = _.clone( this._defaultDisplaySettings ); 1356 library.gallery = library.gallery || new Backbone.Model(); 1255 1357 1256 if ( settings.canEmbed = this.canEmbed( attachment ) ) { 1257 settings.link = 'embed'; 1258 } else if ( ! this.isImageAttachment( attachment ) && settings.link === 'none' ) { 1259 settings.link = 'file'; 1260 } 1358 browser.sidebar.set({ 1359 gallery: new wp.media.view.Settings.Gallery({ 1360 controller: this, 1361 model: library.gallery, 1362 priority: 40 1363 }) 1364 }); 1261 1365 1262 return settings; 1263 }, 1366 browser.toolbar.set( 'reverse', { 1367 text: l10n.reverseOrder, 1368 priority: 80, 1264 1369 1265 /** 1266 * Whether an attachment is image. 1267 * 1268 * @since 4.4.1 1269 * 1270 * @param {wp.media.model.Attachment} attachment 1271 * @returns {Boolean} 1272 */ 1273 isImageAttachment: function( attachment ) { 1274 // If uploading, we know the filename but not the mime type. 1275 if ( attachment.get('uploading') ) { 1276 return /\.(jpe?g|png|gif)$/i.test( attachment.get('filename') ); 1277 } 1370 click: function() { 1371 library.reset( library.toArray().reverse() ); 1372 } 1373 }); 1374 } 1375 }); 1278 1376 1279 return attachment.get('type') === 'image'; 1280 }, 1377 module.exports = GalleryEdit; 1281 1378 1282 /**1283 * Whether an attachment can be embedded (audio or video).1284 *1285 * @since 3.6.01286 *1287 * @param {wp.media.model.Attachment} attachment1288 * @returns {Boolean}1289 */1290 canEmbed: function( attachment ) {1291 // If uploading, we know the filename but not the mime type.1292 if ( ! attachment.get('uploading') ) {1293 var type = attachment.get('type');1294 if ( type !== 'audio' && type !== 'video' ) {1295 return false;1296 }1297 }1298 1299 return _.contains( wp.media.view.settings.embedExts, attachment.get('filename').split('.').pop() );1300 },1301 1379 1380 /***/ }), 1381 /* 34 */ 1382 /***/ (function(module, exports) { 1302 1383 1303 /** 1304 * If the state is active, no items are selected, and the current 1305 * content mode is not an option in the state's router (provided 1306 * the state has a router), reset the content mode to the default. 1307 * 1308 * @since 3.5.0 1309 */ 1310 refreshContent: function() { 1311 var selection = this.get('selection'), 1312 frame = this.frame, 1313 router = frame.router.get(), 1314 mode = frame.content.mode(); 1384 var Selection = wp.media.model.Selection, 1385 Library = wp.media.controller.Library, 1386 l10n = wp.media.view.l10n, 1387 GalleryAdd; 1315 1388 1316 if ( this.active && ! selection.length && router && ! router.get( mode ) ) { 1317 this.frame.content.render( this.get('content') ); 1318 } 1319 }, 1389 /** 1390 * wp.media.controller.GalleryAdd 1391 * 1392 * A state for selecting more images to add to a gallery. 1393 * 1394 * @memberOf wp.media.controller 1395 * 1396 * @class 1397 * @augments wp.media.controller.Library 1398 * @augments wp.media.controller.State 1399 * @augments Backbone.Model 1400 * 1401 * @param {object} [attributes] The attributes hash passed to the state. 1402 * @param {string} [attributes.id=gallery-library] Unique identifier. 1403 * @param {string} [attributes.title=Add to Gallery] Title for the state. Displays in the frame's title region. 1404 * @param {boolean} [attributes.multiple=add] Whether multi-select is enabled. @todo 'add' doesn't seem do anything special, and gets used as a boolean. 1405 * @param {wp.media.model.Attachments} [attributes.library] The attachments collection to browse. 1406 * If one is not supplied, a collection of all images will be created. 1407 * @param {boolean|string} [attributes.filterable=uploaded] Whether the library is filterable, and if so what filters should be shown. 1408 * Accepts 'all', 'uploaded', or 'unattached'. 1409 * @param {string} [attributes.menu=gallery] Initial mode for the menu region. 1410 * @param {string} [attributes.content=upload] Initial mode for the content region. 1411 * Overridden by persistent user setting if 'contentUserSetting' is true. 1412 * @param {string} [attributes.router=browse] Initial mode for the router region. 1413 * @param {string} [attributes.toolbar=gallery-add] Initial mode for the toolbar region. 1414 * @param {boolean} [attributes.searchable=true] Whether the library is searchable. 1415 * @param {boolean} [attributes.sortable=true] Whether the Attachments should be sortable. Depends on the orderby property being set to menuOrder on the attachments collection. 1416 * @param {boolean} [attributes.autoSelect=true] Whether an uploaded attachment should be automatically added to the selection. 1417 * @param {boolean} [attributes.contentUserSetting=true] Whether the content region's mode should be set and persisted per user. 1418 * @param {int} [attributes.priority=100] The priority for the state link in the media menu. 1419 * @param {boolean} [attributes.syncSelection=false] Whether the Attachments selection should be persisted from the last state. 1420 * Defaults to false because for this state, because the library of the Edit Gallery state is the selection. 1421 */ 1422 GalleryAdd = Library.extend(/** @lends wp.media.controller.GalleryAdd.prototype */{ 1423 defaults: _.defaults({ 1424 id: 'gallery-library', 1425 title: l10n.addToGalleryTitle, 1426 multiple: 'add', 1427 filterable: 'uploaded', 1428 menu: 'gallery', 1429 toolbar: 'gallery-add', 1430 priority: 100, 1431 syncSelection: false 1432 }, Library.prototype.defaults ), 1320 1433 1321 1434 /** 1322 * Callback handler when an attachment is uploaded.1323 *1324 * Switch to the Media Library if uploaded from the 'Upload Files' tab.1325 *1326 * Adds any uploading attachments to the selection.1327 *1328 * If the state only supports one attachment to be selected and multiple1329 * attachments are uploaded, the last attachment in the upload queue will1330 * be selected.1331 *1332 1435 * @since 3.5.0 1333 *1334 * @param {wp.media.model.Attachment} attachment1335 1436 */ 1336 uploading: function( attachment ) { 1337 var content = this.frame.content; 1338 1339 if ( 'upload' === content.mode() ) { 1340 this.frame.content.mode('browse'); 1437 initialize: function() { 1438 // If a library wasn't supplied, create a library of images. 1439 if ( ! this.get('library') ) { 1440 this.set( 'library', wp.media.query({ type: 'image' }) ); 1341 1441 } 1342 1442 1343 if ( this.get( 'autoSelect' ) ) { 1344 this.get('selection').add( attachment ); 1345 this.frame.trigger( 'library:selection:add' ); 1346 } 1443 Library.prototype.initialize.apply( this, arguments ); 1347 1444 }, 1348 1445 1349 1446 /** 1350 * Persist the mode of the content region as a user setting.1351 *1352 1447 * @since 3.5.0 1353 1448 */ 1354 saveContentMode: function() { 1355 if ( 'browse' !== this.get('router') ) { 1356 return; 1449 activate: function() { 1450 var library = this.get('library'), 1451 edit = this.frame.state('gallery-edit').get('library'); 1452 1453 if ( this.editLibrary && this.editLibrary !== edit ) { 1454 library.unobserve( this.editLibrary ); 1357 1455 } 1358 1456 1359 var mode = this.frame.content.mode(), 1360 view = this.frame.router.get(); 1457 // Accepts attachments that exist in the original library and 1458 // that do not exist in gallery's library. 1459 library.validator = function( attachment ) { 1460 return !! this.mirroring.get( attachment.cid ) && ! edit.get( attachment.cid ) && Selection.prototype.validator.apply( this, arguments ); 1461 }; 1361 1462 1362 if ( view && view.get( mode ) ) { 1363 setUserSetting( 'libraryContent', mode ); 1364 } 1463 // Reset the library to ensure that all attachments are re-added 1464 // to the collection. Do so silently, as calling `observe` will 1465 // trigger the `reset` event. 1466 library.reset( library.mirroring.models, { silent: true }); 1467 library.observe( edit ); 1468 this.editLibrary = edit; 1469 1470 Library.prototype.activate.apply( this, arguments ); 1365 1471 } 1366 1472 }); 1367 1473 1368 // Make selectionSync available on any Media Library state. 1369 _.extend( Library.prototype, wp.media.selectionSync ); 1474 module.exports = GalleryAdd; 1370 1475 1371 module.exports = Library;1372 1476 1373 },{}],12:[function(require,module,exports){ 1477 /***/ }), 1478 /* 35 */ 1479 /***/ (function(module, exports) { 1480 1481 var Library = wp.media.controller.Library, 1482 l10n = wp.media.view.l10n, 1483 $ = jQuery, 1484 CollectionEdit; 1485 1374 1486 /** 1375 * wp.media.controller.MediaLibrary 1487 * wp.media.controller.CollectionEdit 1488 * 1489 * A state for editing a collection, which is used by audio and video playlists, 1490 * and can be used for other collections. 1376 1491 * 1377 1492 * @memberOf wp.media.controller 1378 1493 * … … module.exports = Library; 1380 1495 * @augments wp.media.controller.Library 1381 1496 * @augments wp.media.controller.State 1382 1497 * @augments Backbone.Model 1498 * 1499 * @param {object} [attributes] The attributes hash passed to the state. 1500 * @param {string} attributes.title Title for the state. Displays in the media menu and the frame's title region. 1501 * @param {wp.media.model.Attachments} [attributes.library] The attachments collection to edit. 1502 * If one is not supplied, an empty media.model.Selection collection is created. 1503 * @param {boolean} [attributes.multiple=false] Whether multi-select is enabled. 1504 * @param {string} [attributes.content=browse] Initial mode for the content region. 1505 * @param {string} attributes.menu Initial mode for the menu region. @todo this needs a better explanation. 1506 * @param {boolean} [attributes.searchable=false] Whether the library is searchable. 1507 * @param {boolean} [attributes.sortable=true] Whether the Attachments should be sortable. Depends on the orderby property being set to menuOrder on the attachments collection. 1508 * @param {boolean} [attributes.date=true] Whether to show the date filter in the browser's toolbar. 1509 * @param {boolean} [attributes.describe=true] Whether to offer UI to describe the attachments - e.g. captioning images in a gallery. 1510 * @param {boolean} [attributes.dragInfo=true] Whether to show instructional text about the attachments being sortable. 1511 * @param {boolean} [attributes.dragInfoText] Instructional text about the attachments being sortable. 1512 * @param {int} [attributes.idealColumnWidth=170] The ideal column width in pixels for attachments. 1513 * @param {boolean} [attributes.editing=false] Whether the gallery is being created, or editing an existing instance. 1514 * @param {int} [attributes.priority=60] The priority for the state link in the media menu. 1515 * @param {boolean} [attributes.syncSelection=false] Whether the Attachments selection should be persisted from the last state. 1516 * Defaults to false for this state, because the library passed in *is* the selection. 1517 * @param {view} [attributes.SettingsView] The view to edit the collection instance settings (e.g. Playlist settings with "Show tracklist" checkbox). 1518 * @param {view} [attributes.AttachmentView] The single `Attachment` view to be used in the `Attachments`. 1519 * If none supplied, defaults to wp.media.view.Attachment.EditLibrary. 1520 * @param {string} attributes.type The collection's media type. (e.g. 'video'). 1521 * @param {string} attributes.collectionType The collection type. (e.g. 'playlist'). 1383 1522 */ 1384 var Library = wp.media.controller.Library, 1385 MediaLibrary; 1386 1387 MediaLibrary = Library.extend(/** @lends wp.media.controller.MediaLibrary.prototype */{ 1388 defaults: _.defaults({ 1389 // Attachments browser defaults. @see media.view.AttachmentsBrowser 1390 filterable: 'uploaded', 1391 1392 displaySettings: false, 1393 priority: 80, 1394 syncSelection: false 1395 }, Library.prototype.defaults ), 1523 CollectionEdit = Library.extend(/** @lends wp.media.controller.CollectionEdit.prototype */{ 1524 defaults: { 1525 multiple: false, 1526 sortable: true, 1527 date: false, 1528 searchable: false, 1529 content: 'browse', 1530 describe: true, 1531 dragInfo: true, 1532 idealColumnWidth: 170, 1533 editing: false, 1534 priority: 60, 1535 SettingsView: false, 1536 syncSelection: false 1537 }, 1396 1538 1397 1539 /** 1398 1540 * @since 3.9.0 1399 *1400 * @param options1401 1541 */ 1402 initialize: function( options ) { 1403 this.media = options.media; 1404 this.type = options.type; 1405 this.set( 'library', wp.media.query({ type: this.type }) ); 1542 initialize: function() { 1543 var collectionType = this.get('collectionType'); 1544 1545 if ( 'video' === this.get( 'type' ) ) { 1546 collectionType = 'video-' + collectionType; 1547 } 1548 1549 this.set( 'id', collectionType + '-edit' ); 1550 this.set( 'toolbar', collectionType + '-edit' ); 1406 1551 1552 // If we haven't been provided a `library`, create a `Selection`. 1553 if ( ! this.get('library') ) { 1554 this.set( 'library', new wp.media.model.Selection() ); 1555 } 1556 // The single `Attachment` view to be used in the `Attachments` view. 1557 if ( ! this.get('AttachmentView') ) { 1558 this.set( 'AttachmentView', wp.media.view.Attachment.EditLibrary ); 1559 } 1407 1560 Library.prototype.initialize.apply( this, arguments ); 1408 1561 }, 1409 1562 … … MediaLibrary = Library.extend(/** @lends wp.media.controller.MediaLibrary.protot 1411 1564 * @since 3.9.0 1412 1565 */ 1413 1566 activate: function() { 1414 // @todo this should use this.frame. 1415 if ( wp.media.frame.lastMime ) { 1416 this.set( 'library', wp.media.query({ type: wp.media.frame.lastMime }) ); 1417 delete wp.media.frame.lastMime; 1418 } 1567 var library = this.get('library'); 1568 1569 // Limit the library to images only. 1570 library.props.set( 'type', this.get( 'type' ) ); 1571 1572 // Watch for uploaded attachments. 1573 this.get('library').observe( wp.Uploader.queue ); 1574 1575 this.frame.on( 'content:render:browse', this.renderSettings, this ); 1576 1419 1577 Library.prototype.activate.apply( this, arguments ); 1420 } 1421 }); 1578 }, 1422 1579 1423 module.exports = MediaLibrary; 1580 /** 1581 * @since 3.9.0 1582 */ 1583 deactivate: function() { 1584 // Stop watching for uploaded attachments. 1585 this.get('library').unobserve( wp.Uploader.queue ); 1424 1586 1425 },{}],13:[function(require,module,exports){ 1426 /** 1427 * wp.media.controller.Region 1428 * 1429 * A region is a persistent application layout area. 1430 * 1431 * A region assumes one mode at any time, and can be switched to another. 1432 * 1433 * When mode changes, events are triggered on the region's parent view. 1434 * The parent view will listen to specific events and fill the region with an 1435 * appropriate view depending on mode. For example, a frame listens for the 1436 * 'browse' mode t be activated on the 'content' view and then fills the region 1437 * with an AttachmentsBrowser view. 1438 * 1439 * @memberOf wp.media.controller 1440 * 1441 * @class 1442 * 1443 * @param {object} options Options hash for the region. 1444 * @param {string} options.id Unique identifier for the region. 1445 * @param {Backbone.View} options.view A parent view the region exists within. 1446 * @param {string} options.selector jQuery selector for the region within the parent view. 1447 */ 1448 var Region = function( options ) { 1449 _.extend( this, _.pick( options || {}, 'id', 'view', 'selector' ) ); 1450 }; 1451 1452 // Use Backbone's self-propagating `extend` inheritance method. 1453 Region.extend = Backbone.Model.extend; 1454 1455 _.extend( Region.prototype,/** @lends wp.media.controller.Region.prototype */{ 1456 /** 1457 * Activate a mode. 1458 * 1459 * @since 3.5.0 1460 * 1461 * @param {string} mode 1462 * 1463 * @fires Region#activate 1464 * @fires Region#deactivate 1465 * 1466 * @returns {wp.media.controller.Region} Returns itself to allow chaining. 1467 */ 1468 mode: function( mode ) { 1469 if ( ! mode ) { 1470 return this._mode; 1471 } 1472 // Bail if we're trying to change to the current mode. 1473 if ( mode === this._mode ) { 1474 return this; 1475 } 1476 1477 /** 1478 * Region mode deactivation event. 1479 * 1480 * @event wp.media.controller.Region#deactivate 1481 */ 1482 this.trigger('deactivate'); 1483 1484 this._mode = mode; 1485 this.render( mode ); 1587 this.frame.off( 'content:render:browse', this.renderSettings, this ); 1486 1588 1487 /** 1488 * Region mode activation event. 1489 * 1490 * @event wp.media.controller.Region#activate 1491 */ 1492 this.trigger('activate'); 1493 return this; 1589 Library.prototype.deactivate.apply( this, arguments ); 1494 1590 }, 1591 1495 1592 /** 1496 * Render a mode. 1497 * 1498 * @since 3.5.0 1593 * Render the collection embed settings view in the browser sidebar. 1499 1594 * 1500 * @param {string} mode 1595 * @todo This is against the pattern elsewhere in media. Typically the frame 1596 * is responsible for adding region mode callbacks. Explain. 1501 1597 * 1502 * @fires Region#create 1503 * @fires Region#render 1598 * @since 3.9.0 1504 1599 * 1505 * @ returns {wp.media.controller.Region} Returns itself to allow chaining1600 * @param {wp.media.view.attachmentsBrowser} The attachments browser view. 1506 1601 */ 1507 render: function( mode ) { 1508 // If the mode isn't active, activate it. 1509 if ( mode && mode !== this._mode ) { 1510 return this.mode( mode ); 1511 } 1512 1513 var set = { view: null }, 1514 view; 1515 1516 /** 1517 * Create region view event. 1518 * 1519 * Region view creation takes place in an event callback on the frame. 1520 * 1521 * @event wp.media.controller.Region#create 1522 * @type {object} 1523 * @property {object} view 1524 */ 1525 this.trigger( 'create', set ); 1526 view = set.view; 1602 renderSettings: function( attachmentsBrowserView ) { 1603 var library = this.get('library'), 1604 collectionType = this.get('collectionType'), 1605 dragInfoText = this.get('dragInfoText'), 1606 SettingsView = this.get('SettingsView'), 1607 obj = {}; 1527 1608 1528 /** 1529 * Render region view event. 1530 * 1531 * Region view creation takes place in an event callback on the frame. 1532 * 1533 * @event wp.media.controller.Region#render 1534 * @type {object} 1535 */ 1536 this.trigger( 'render', view ); 1537 if ( view ) { 1538 this.set( view ); 1609 if ( ! library || ! attachmentsBrowserView ) { 1610 return; 1539 1611 } 1540 return this;1541 },1542 1612 1543 /** 1544 * Get the region's view. 1545 * 1546 * @since 3.5.0 1547 * 1548 * @returns {wp.media.View} 1549 */ 1550 get: function() { 1551 return this.view.views.first( this.selector ); 1552 }, 1613 library[ collectionType ] = library[ collectionType ] || new Backbone.Model(); 1553 1614 1554 /** 1555 * Set the region's view as a subview of the frame. 1556 * 1557 * @since 3.5.0 1558 * 1559 * @param {Array|Object} views 1560 * @param {Object} [options={}] 1561 * @returns {wp.Backbone.Subviews} Subviews is returned to allow chaining 1562 */ 1563 set: function( views, options ) { 1564 if ( options ) { 1565 options.add = false; 1566 } 1567 return this.view.views.set( this.selector, views, options ); 1568 }, 1615 obj[ collectionType ] = new SettingsView({ 1616 controller: this, 1617 model: library[ collectionType ], 1618 priority: 40 1619 }); 1569 1620 1570 /** 1571 * Trigger regional view events on the frame. 1572 * 1573 * @since 3.5.0 1574 * 1575 * @param {string} event 1576 * @returns {undefined|wp.media.controller.Region} Returns itself to allow chaining. 1577 */ 1578 trigger: function( event ) { 1579 var base, args; 1621 attachmentsBrowserView.sidebar.set( obj ); 1580 1622 1581 if ( ! this._mode ) { 1582 return; 1623 if ( dragInfoText ) { 1624 attachmentsBrowserView.toolbar.set( 'dragInfo', new wp.media.View({ 1625 el: $( '<div class="instructions">' + dragInfoText + '</div>' )[0], 1626 priority: -40 1627 }) ); 1583 1628 } 1584 1629 1585 args = _.toArray( arguments ); 1586 base = this.id + ':' + event; 1587 1588 // Trigger `{this.id}:{event}:{this._mode}` event on the frame. 1589 args[0] = base + ':' + this._mode; 1590 this.view.trigger.apply( this.view, args ); 1630 // Add the 'Reverse order' button to the toolbar. 1631 attachmentsBrowserView.toolbar.set( 'reverse', { 1632 text: l10n.reverseOrder, 1633 priority: 80, 1591 1634 1592 // Trigger `{this.id}:{event}` event on the frame.1593 args[0] = base;1594 this.view.trigger.apply( this.view, args );1595 return this;1635 click: function() { 1636 library.reset( library.toArray().reverse() ); 1637 } 1638 }); 1596 1639 } 1597 1640 }); 1598 1641 1599 module.exports = Region;1642 module.exports = CollectionEdit; 1600 1643 1601 },{}],14:[function(require,module,exports){ 1602 var Library = wp.media.controller.Library, 1603 l10n = wp.media.view.l10n, 1604 ReplaceImage; 1644 1645 /***/ }), 1646 /* 36 */ 1647 /***/ (function(module, exports) { 1648 1649 var Selection = wp.media.model.Selection, 1650 Library = wp.media.controller.Library, 1651 CollectionAdd; 1605 1652 1606 1653 /** 1607 * wp.media.controller. ReplaceImage1654 * wp.media.controller.CollectionAdd 1608 1655 * 1609 * A state for replacing an image.1656 * A state for adding attachments to a collection (e.g. video playlist). 1610 1657 * 1611 1658 * @memberOf wp.media.controller 1612 1659 * … … var Library = wp.media.controller.Library, 1616 1663 * @augments Backbone.Model 1617 1664 * 1618 1665 * @param {object} [attributes] The attributes hash passed to the state. 1619 * @param {string} [attributes.id=replace-image] Unique identifier. 1620 * @param {string} [attributes.title=Replace Image] Title for the state. Displays in the media menu and the frame's title region. 1666 * @param {string} [attributes.id=library] Unique identifier. 1667 * @param {string} attributes.title Title for the state. Displays in the frame's title region. 1668 * @param {boolean} [attributes.multiple=add] Whether multi-select is enabled. @todo 'add' doesn't seem do anything special, and gets used as a boolean. 1621 1669 * @param {wp.media.model.Attachments} [attributes.library] The attachments collection to browse. 1622 * If one is not supplied, a collection of all images will be created. 1623 * @param {boolean} [attributes.multiple=false] Whether multi-select is enabled. 1670 * If one is not supplied, a collection of attachments of the specified type will be created. 1671 * @param {boolean|string} [attributes.filterable=uploaded] Whether the library is filterable, and if so what filters should be shown. 1672 * Accepts 'all', 'uploaded', or 'unattached'. 1673 * @param {string} [attributes.menu=gallery] Initial mode for the menu region. 1624 1674 * @param {string} [attributes.content=upload] Initial mode for the content region. 1625 1675 * Overridden by persistent user setting if 'contentUserSetting' is true. 1626 * @param {string} [attributes.menu=default] Initial mode for the menu region.1627 1676 * @param {string} [attributes.router=browse] Initial mode for the router region. 1628 * @param {string} [attributes.toolbar=replace] Initial mode for the toolbar region. 1629 * @param {int} [attributes.priority=60] The priority for the state link in the media menu. 1677 * @param {string} [attributes.toolbar=gallery-add] Initial mode for the toolbar region. 1630 1678 * @param {boolean} [attributes.searchable=true] Whether the library is searchable. 1631 * @param {boolean|string} [attributes.filterable=uploaded] Whether the library is filterable, and if so what filters should be shown.1632 * Accepts 'all', 'uploaded', or 'unattached'.1633 1679 * @param {boolean} [attributes.sortable=true] Whether the Attachments should be sortable. Depends on the orderby property being set to menuOrder on the attachments collection. 1634 1680 * @param {boolean} [attributes.autoSelect=true] Whether an uploaded attachment should be automatically added to the selection. 1635 * @param {boolean} [attributes.describe=false] Whether to offer UI to describe attachments - e.g. captioning images in a gallery.1636 1681 * @param {boolean} [attributes.contentUserSetting=true] Whether the content region's mode should be set and persisted per user. 1637 * @param {boolean} [attributes.syncSelection=true] Whether the Attachments selection should be persisted from the last state. 1682 * @param {int} [attributes.priority=100] The priority for the state link in the media menu. 1683 * @param {boolean} [attributes.syncSelection=false] Whether the Attachments selection should be persisted from the last state. 1684 * Defaults to false because for this state, because the library of the Edit Gallery state is the selection. 1685 * @param {string} attributes.type The collection's media type. (e.g. 'video'). 1686 * @param {string} attributes.collectionType The collection type. (e.g. 'playlist'). 1638 1687 */ 1639 ReplaceImage = Library.extend(/** @lends wp.media.controller.ReplaceImage.prototype */{1640 defaults: _.defaults( {1641 id: 'replace-image',1642 title: l10n.replaceImageTitle,1643 multiple: false,1688 CollectionAdd = Library.extend(/** @lends wp.media.controller.CollectionAdd.prototype */{ 1689 defaults: _.defaults( { 1690 // Selection defaults. @see media.model.Selection 1691 multiple: 'add', 1692 // Attachments browser defaults. @see media.view.AttachmentsBrowser 1644 1693 filterable: 'uploaded', 1645 toolbar: 'replace', 1646 menu: false, 1647 priority: 60, 1648 syncSelection: true 1694 1695 priority: 100, 1696 syncSelection: false 1649 1697 }, Library.prototype.defaults ), 1650 1698 1651 1699 /** 1652 1700 * @since 3.9.0 1653 *1654 * @param options1655 1701 */ 1656 initialize: function( options ) { 1657 var library, comparator; 1702 initialize: function() { 1703 var collectionType = this.get('collectionType'); 1704 1705 if ( 'video' === this.get( 'type' ) ) { 1706 collectionType = 'video-' + collectionType; 1707 } 1708 1709 this.set( 'id', collectionType + '-library' ); 1710 this.set( 'toolbar', collectionType + '-add' ); 1711 this.set( 'menu', collectionType ); 1658 1712 1659 this.image = options.image;1660 1713 // If we haven't been provided a `library`, create a `Selection`. 1661 1714 if ( ! this.get('library') ) { 1662 this.set( 'library', wp.media.query({ type: 'image'}) );1715 this.set( 'library', wp.media.query({ type: this.get('type') }) ); 1663 1716 } 1664 1665 1717 Library.prototype.initialize.apply( this, arguments ); 1718 }, 1719 1720 /** 1721 * @since 3.9.0 1722 */ 1723 activate: function() { 1724 var library = this.get('library'), 1725 editLibrary = this.get('editLibrary'), 1726 edit = this.frame.state( this.get('collectionType') + '-edit' ).get('library'); 1727 1728 if ( editLibrary && editLibrary !== edit ) { 1729 library.unobserve( editLibrary ); 1730 } 1731 1732 // Accepts attachments that exist in the original library and 1733 // that do not exist in gallery's library. 1734 library.validator = function( attachment ) { 1735 return !! this.mirroring.get( attachment.cid ) && ! edit.get( attachment.cid ) && Selection.prototype.validator.apply( this, arguments ); 1736 }; 1737 1738 // Reset the library to ensure that all attachments are re-added 1739 // to the collection. Do so silently, as calling `observe` will 1740 // trigger the `reset` event. 1741 library.reset( library.mirroring.models, { silent: true }); 1742 library.observe( edit ); 1743 this.set('editLibrary', edit); 1744 1745 Library.prototype.activate.apply( this, arguments ); 1746 } 1747 }); 1748 1749 module.exports = CollectionAdd; 1750 1751 1752 /***/ }), 1753 /* 37 */ 1754 /***/ (function(module, exports) { 1755 1756 var Attachment = wp.media.model.Attachment, 1757 Library = wp.media.controller.Library, 1758 l10n = wp.media.view.l10n, 1759 FeaturedImage; 1760 1761 /** 1762 * wp.media.controller.FeaturedImage 1763 * 1764 * A state for selecting a featured image for a post. 1765 * 1766 * @memberOf wp.media.controller 1767 * 1768 * @class 1769 * @augments wp.media.controller.Library 1770 * @augments wp.media.controller.State 1771 * @augments Backbone.Model 1772 * 1773 * @param {object} [attributes] The attributes hash passed to the state. 1774 * @param {string} [attributes.id=featured-image] Unique identifier. 1775 * @param {string} [attributes.title=Set Featured Image] Title for the state. Displays in the media menu and the frame's title region. 1776 * @param {wp.media.model.Attachments} [attributes.library] The attachments collection to browse. 1777 * If one is not supplied, a collection of all images will be created. 1778 * @param {boolean} [attributes.multiple=false] Whether multi-select is enabled. 1779 * @param {string} [attributes.content=upload] Initial mode for the content region. 1780 * Overridden by persistent user setting if 'contentUserSetting' is true. 1781 * @param {string} [attributes.menu=default] Initial mode for the menu region. 1782 * @param {string} [attributes.router=browse] Initial mode for the router region. 1783 * @param {string} [attributes.toolbar=featured-image] Initial mode for the toolbar region. 1784 * @param {int} [attributes.priority=60] The priority for the state link in the media menu. 1785 * @param {boolean} [attributes.searchable=true] Whether the library is searchable. 1786 * @param {boolean|string} [attributes.filterable=false] Whether the library is filterable, and if so what filters should be shown. 1787 * Accepts 'all', 'uploaded', or 'unattached'. 1788 * @param {boolean} [attributes.sortable=true] Whether the Attachments should be sortable. Depends on the orderby property being set to menuOrder on the attachments collection. 1789 * @param {boolean} [attributes.autoSelect=true] Whether an uploaded attachment should be automatically added to the selection. 1790 * @param {boolean} [attributes.describe=false] Whether to offer UI to describe attachments - e.g. captioning images in a gallery. 1791 * @param {boolean} [attributes.contentUserSetting=true] Whether the content region's mode should be set and persisted per user. 1792 * @param {boolean} [attributes.syncSelection=true] Whether the Attachments selection should be persisted from the last state. 1793 */ 1794 FeaturedImage = Library.extend(/** @lends wp.media.controller.FeaturedImage.prototype */{ 1795 defaults: _.defaults({ 1796 id: 'featured-image', 1797 title: l10n.setFeaturedImageTitle, 1798 multiple: false, 1799 filterable: 'uploaded', 1800 toolbar: 'featured-image', 1801 priority: 60, 1802 syncSelection: true 1803 }, Library.prototype.defaults ), 1804 1805 /** 1806 * @since 3.5.0 1807 */ 1808 initialize: function() { 1809 var library, comparator; 1810 1811 // If we haven't been provided a `library`, create a `Selection`. 1812 if ( ! this.get('library') ) { 1813 this.set( 'library', wp.media.query({ type: 'image' }) ); 1814 } 1815 1816 Library.prototype.initialize.apply( this, arguments ); 1817 1818 library = this.get('library'); 1819 comparator = library.comparator; 1666 1820 1667 library = this.get('library');1668 comparator = library.comparator;1669 1670 1821 // Overload the library's comparator to push items that are not in 1671 1822 // the mirrored query to the front of the aggregate collection. 1672 1823 library.comparator = function( a, b ) { … … ReplaceImage = Library.extend(/** @lends wp.media.controller.ReplaceImage.protot 1688 1839 }, 1689 1840 1690 1841 /** 1691 * @since 3. 9.01842 * @since 3.5.0 1692 1843 */ 1693 1844 activate: function() { 1694 1845 this.updateSelection(); 1846 this.frame.on( 'open', this.updateSelection, this ); 1847 1695 1848 Library.prototype.activate.apply( this, arguments ); 1696 1849 }, 1697 1850 1698 1851 /** 1699 * @since 3.9.0 1852 * @since 3.5.0 1853 */ 1854 deactivate: function() { 1855 this.frame.off( 'open', this.updateSelection, this ); 1856 1857 Library.prototype.deactivate.apply( this, arguments ); 1858 }, 1859 1860 /** 1861 * @since 3.5.0 1700 1862 */ 1701 1863 updateSelection: function() { 1702 1864 var selection = this.get('selection'), 1703 attachment = this.image.attachment; 1865 id = wp.media.view.settings.post.featuredImageId, 1866 attachment; 1867 1868 if ( '' !== id && -1 !== id ) { 1869 attachment = Attachment.get( id ); 1870 attachment.fetch(); 1871 } 1704 1872 1705 1873 selection.reset( attachment ? [ attachment ] : [] ); 1706 1874 } 1707 1875 }); 1708 1876 1709 module.exports = ReplaceImage;1877 module.exports = FeaturedImage; 1710 1878 1711 },{}],15:[function(require,module,exports){ 1712 var Controller = wp.media.controller, 1713 SiteIconCropper; 1879 1880 /***/ }), 1881 /* 38 */ 1882 /***/ (function(module, exports) { 1883 1884 var Library = wp.media.controller.Library, 1885 l10n = wp.media.view.l10n, 1886 ReplaceImage; 1714 1887 1715 1888 /** 1716 * wp.media.controller. SiteIconCropper1889 * wp.media.controller.ReplaceImage 1717 1890 * 1718 * A state for cropping a Site Icon.1891 * A state for replacing an image. 1719 1892 * 1720 1893 * @memberOf wp.media.controller 1721 1894 * 1722 1895 * @class 1723 * @augments wp.media.controller. Cropper1896 * @augments wp.media.controller.Library 1724 1897 * @augments wp.media.controller.State 1725 1898 * @augments Backbone.Model 1899 * 1900 * @param {object} [attributes] The attributes hash passed to the state. 1901 * @param {string} [attributes.id=replace-image] Unique identifier. 1902 * @param {string} [attributes.title=Replace Image] Title for the state. Displays in the media menu and the frame's title region. 1903 * @param {wp.media.model.Attachments} [attributes.library] The attachments collection to browse. 1904 * If one is not supplied, a collection of all images will be created. 1905 * @param {boolean} [attributes.multiple=false] Whether multi-select is enabled. 1906 * @param {string} [attributes.content=upload] Initial mode for the content region. 1907 * Overridden by persistent user setting if 'contentUserSetting' is true. 1908 * @param {string} [attributes.menu=default] Initial mode for the menu region. 1909 * @param {string} [attributes.router=browse] Initial mode for the router region. 1910 * @param {string} [attributes.toolbar=replace] Initial mode for the toolbar region. 1911 * @param {int} [attributes.priority=60] The priority for the state link in the media menu. 1912 * @param {boolean} [attributes.searchable=true] Whether the library is searchable. 1913 * @param {boolean|string} [attributes.filterable=uploaded] Whether the library is filterable, and if so what filters should be shown. 1914 * Accepts 'all', 'uploaded', or 'unattached'. 1915 * @param {boolean} [attributes.sortable=true] Whether the Attachments should be sortable. Depends on the orderby property being set to menuOrder on the attachments collection. 1916 * @param {boolean} [attributes.autoSelect=true] Whether an uploaded attachment should be automatically added to the selection. 1917 * @param {boolean} [attributes.describe=false] Whether to offer UI to describe attachments - e.g. captioning images in a gallery. 1918 * @param {boolean} [attributes.contentUserSetting=true] Whether the content region's mode should be set and persisted per user. 1919 * @param {boolean} [attributes.syncSelection=true] Whether the Attachments selection should be persisted from the last state. 1726 1920 */ 1727 SiteIconCropper = Controller.Cropper.extend(/** @lends wp.media.controller.SiteIconCropper.prototype */{ 1728 activate: function() { 1729 this.frame.on( 'content:create:crop', this.createCropContent, this ); 1730 this.frame.on( 'close', this.removeCropper, this ); 1731 this.set('selection', new Backbone.Collection(this.frame._selection.single)); 1732 }, 1921 ReplaceImage = Library.extend(/** @lends wp.media.controller.ReplaceImage.prototype */{ 1922 defaults: _.defaults({ 1923 id: 'replace-image', 1924 title: l10n.replaceImageTitle, 1925 multiple: false, 1926 filterable: 'uploaded', 1927 toolbar: 'replace', 1928 menu: false, 1929 priority: 60, 1930 syncSelection: true 1931 }, Library.prototype.defaults ), 1733 1932 1734 createCropContent: function() { 1735 this.cropperView = new wp.media.view.SiteIconCropper({ 1736 controller: this, 1737 attachment: this.get('selection').first() 1738 }); 1739 this.cropperView.on('image-loaded', this.createCropToolbar, this); 1740 this.frame.content.set(this.cropperView); 1933 /** 1934 * @since 3.9.0 1935 * 1936 * @param options 1937 */ 1938 initialize: function( options ) { 1939 var library, comparator; 1940 1941 this.image = options.image; 1942 // If we haven't been provided a `library`, create a `Selection`. 1943 if ( ! this.get('library') ) { 1944 this.set( 'library', wp.media.query({ type: 'image' }) ); 1945 } 1946 1947 Library.prototype.initialize.apply( this, arguments ); 1948 1949 library = this.get('library'); 1950 comparator = library.comparator; 1951 1952 // Overload the library's comparator to push items that are not in 1953 // the mirrored query to the front of the aggregate collection. 1954 library.comparator = function( a, b ) { 1955 var aInQuery = !! this.mirroring.get( a.cid ), 1956 bInQuery = !! this.mirroring.get( b.cid ); 1957 1958 if ( ! aInQuery && bInQuery ) { 1959 return -1; 1960 } else if ( aInQuery && ! bInQuery ) { 1961 return 1; 1962 } else { 1963 return comparator.apply( this, arguments ); 1964 } 1965 }; 1741 1966 1967 // Add all items in the selection to the library, so any featured 1968 // images that are not initially loaded still appear. 1969 library.observe( this.get('selection') ); 1742 1970 }, 1743 1971 1744 doCrop: function( attachment ) { 1745 var cropDetails = attachment.get( 'cropDetails' ), 1746 control = this.get( 'control' ); 1972 /** 1973 * @since 3.9.0 1974 */ 1975 activate: function() { 1976 this.updateSelection(); 1977 Library.prototype.activate.apply( this, arguments ); 1978 }, 1747 1979 1748 cropDetails.dst_width = control.params.width; 1749 cropDetails.dst_height = control.params.height; 1980 /** 1981 * @since 3.9.0 1982 */ 1983 updateSelection: function() { 1984 var selection = this.get('selection'), 1985 attachment = this.image.attachment; 1750 1986 1751 return wp.ajax.post( 'crop-image', { 1752 nonce: attachment.get( 'nonces' ).edit, 1753 id: attachment.get( 'id' ), 1754 context: 'site-icon', 1755 cropDetails: cropDetails 1756 } ); 1987 selection.reset( attachment ? [ attachment ] : [] ); 1757 1988 } 1758 1989 }); 1759 1990 1760 module.exports = SiteIconCropper; 1991 module.exports = ReplaceImage; 1992 1993 1994 /***/ }), 1995 /* 39 */ 1996 /***/ (function(module, exports) { 1997 1998 var l10n = wp.media.view.l10n, 1999 EditImage; 1761 2000 1762 },{}],16:[function(require,module,exports){1763 2001 /** 1764 * wp.media.controller.StateMachine 1765 * 1766 * A state machine keeps track of state. It is in one state at a time, 1767 * and can change from one state to another. 2002 * wp.media.controller.EditImage 1768 2003 * 1769 * States are stored as models in a Backbone collection.2004 * A state for editing (cropping, etc.) an image. 1770 2005 * 1771 2006 * @memberOf wp.media.controller 1772 2007 * 1773 * @since 3.5.01774 *1775 2008 * @class 2009 * @augments wp.media.controller.State 1776 2010 * @augments Backbone.Model 1777 * @mixin1778 * @mixes Backbone.Events1779 2011 * 1780 * @param {Array} states 2012 * @param {object} attributes The attributes hash passed to the state. 2013 * @param {wp.media.model.Attachment} attributes.model The attachment. 2014 * @param {string} [attributes.id=edit-image] Unique identifier. 2015 * @param {string} [attributes.title=Edit Image] Title for the state. Displays in the media menu and the frame's title region. 2016 * @param {string} [attributes.content=edit-image] Initial mode for the content region. 2017 * @param {string} [attributes.toolbar=edit-image] Initial mode for the toolbar region. 2018 * @param {string} [attributes.menu=false] Initial mode for the menu region. 2019 * @param {string} [attributes.url] Unused. @todo Consider removal. 1781 2020 */ 1782 var StateMachine = function( states ) { 1783 // @todo This is dead code. The states collection gets created in media.view.Frame._createStates. 1784 this.states = new Backbone.Collection( states ); 1785 }; 1786 1787 // Use Backbone's self-propagating `extend` inheritance method. 1788 StateMachine.extend = Backbone.Model.extend; 2021 EditImage = wp.media.controller.State.extend(/** @lends wp.media.controller.EditImage.prototype */{ 2022 defaults: { 2023 id: 'edit-image', 2024 title: l10n.editImage, 2025 menu: false, 2026 toolbar: 'edit-image', 2027 content: 'edit-image', 2028 url: '' 2029 }, 1789 2030 1790 _.extend( StateMachine.prototype, Backbone.Events,/** @lends wp.media.controller.StateMachine.prototype */{1791 2031 /** 1792 * Fetch a state. 1793 * 1794 * If no `id` is provided, returns the active state. 1795 * 1796 * Implicitly creates states. 1797 * 1798 * Ensure that the `states` collection exists so the `StateMachine` 1799 * can be used as a mixin. 1800 * 1801 * @since 3.5.0 1802 * 1803 * @param {string} id 1804 * @returns {wp.media.controller.State} Returns a State model 1805 * from the StateMachine collection 2032 * @since 3.9.0 1806 2033 */ 1807 state: function( id ) { 1808 this.states = this.states || new Backbone.Collection(); 1809 1810 // Default to the active state. 1811 id = id || this._state; 1812 1813 if ( id && ! this.states.get( id ) ) { 1814 this.states.add({ id: id }); 1815 } 1816 return this.states.get( id ); 2034 activate: function() { 2035 this.frame.on( 'toolbar:render:edit-image', _.bind( this.toolbar, this ) ); 1817 2036 }, 1818 2037 1819 2038 /** 1820 * Sets the active state. 1821 * 1822 * Bail if we're trying to select the current state, if we haven't 1823 * created the `states` collection, or are trying to select a state 1824 * that does not exist. 1825 * 1826 * @since 3.5.0 1827 * 1828 * @param {string} id 1829 * 1830 * @fires wp.media.controller.State#deactivate 1831 * @fires wp.media.controller.State#activate 1832 * 1833 * @returns {wp.media.controller.StateMachine} Returns itself to allow chaining 2039 * @since 3.9.0 1834 2040 */ 1835 setState: function( id ) { 1836 var previous = this.state(); 1837 1838 if ( ( previous && id === previous.id ) || ! this.states || ! this.states.get( id ) ) { 1839 return this; 1840 } 1841 1842 if ( previous ) { 1843 previous.trigger('deactivate'); 1844 this._lastState = previous.id; 1845 } 1846 1847 this._state = id; 1848 this.state().trigger('activate'); 1849 1850 return this; 2041 deactivate: function() { 2042 this.frame.off( 'toolbar:render:edit-image' ); 1851 2043 }, 1852 2044 1853 2045 /** 1854 * Returns the previous active state. 1855 * 1856 * Call the `state()` method with no parameters to retrieve the current 1857 * active state. 1858 * 1859 * @since 3.5.0 1860 * 1861 * @returns {wp.media.controller.State} Returns a State model 1862 * from the StateMachine collection 2046 * @since 3.9.0 1863 2047 */ 1864 lastState: function() { 1865 if ( this._lastState ) { 1866 return this.state( this._lastState ); 1867 } 2048 toolbar: function() { 2049 var frame = this.frame, 2050 lastState = frame.lastState(), 2051 previous = lastState && lastState.id; 2052 2053 frame.toolbar.set( new wp.media.view.Toolbar({ 2054 controller: frame, 2055 items: { 2056 back: { 2057 style: 'primary', 2058 text: l10n.back, 2059 priority: 20, 2060 click: function() { 2061 if ( previous ) { 2062 frame.setState( previous ); 2063 } else { 2064 frame.close(); 2065 } 2066 } 2067 } 2068 } 2069 }) ); 1868 2070 } 1869 2071 }); 1870 2072 1871 // Map all event binding and triggering on a StateMachine to its `states` collection. 1872 _.each([ 'on', 'off', 'trigger' ], function( method ) { 1873 /** 1874 * @function on 1875 * @memberOf wp.media.controller.StateMachine 1876 * @instance 1877 * @returns {wp.media.controller.StateMachine} Returns itself to allow chaining. 1878 */ 1879 /** 1880 * @function off 1881 * @memberOf wp.media.controller.StateMachine 1882 * @instance 1883 * @returns {wp.media.controller.StateMachine} Returns itself to allow chaining. 1884 */ 1885 /** 1886 * @function trigger 1887 * @memberOf wp.media.controller.StateMachine 1888 * @instance 1889 * @returns {wp.media.controller.StateMachine} Returns itself to allow chaining. 1890 */ 1891 StateMachine.prototype[ method ] = function() { 1892 // Ensure that the `states` collection exists so the `StateMachine` 1893 // can be used as a mixin. 1894 this.states = this.states || new Backbone.Collection(); 1895 // Forward the method to the `states` collection. 1896 this.states[ method ].apply( this.states, arguments ); 1897 return this; 1898 }; 1899 }); 2073 module.exports = EditImage; 1900 2074 1901 module.exports = StateMachine;1902 2075 1903 },{}],17:[function(require,module,exports){ 2076 /***/ }), 2077 /* 40 */ 2078 /***/ (function(module, exports) { 2079 1904 2080 /** 1905 * wp.media.controller.State 1906 * 1907 * A state is a step in a workflow that when set will trigger the controllers 1908 * for the regions to be updated as specified in the frame. 1909 * 1910 * A state has an event-driven lifecycle: 1911 * 1912 * 'ready' triggers when a state is added to a state machine's collection. 1913 * 'activate' triggers when a state is activated by a state machine. 1914 * 'deactivate' triggers when a state is deactivated by a state machine. 1915 * 'reset' is not triggered automatically. It should be invoked by the 1916 * proper controller to reset the state to its default. 2081 * wp.media.controller.MediaLibrary 1917 2082 * 1918 2083 * @memberOf wp.media.controller 1919 2084 * 1920 2085 * @class 2086 * @augments wp.media.controller.Library 2087 * @augments wp.media.controller.State 1921 2088 * @augments Backbone.Model 1922 2089 */ 1923 var State = Backbone.Model.extend(/** @lends wp.media.controller.State.prototype */{ 1924 /** 1925 * Constructor. 1926 * 1927 * @since 3.5.0 1928 */ 1929 constructor: function() { 1930 this.on( 'activate', this._preActivate, this ); 1931 this.on( 'activate', this.activate, this ); 1932 this.on( 'activate', this._postActivate, this ); 1933 this.on( 'deactivate', this._deactivate, this ); 1934 this.on( 'deactivate', this.deactivate, this ); 1935 this.on( 'reset', this.reset, this ); 1936 this.on( 'ready', this._ready, this ); 1937 this.on( 'ready', this.ready, this ); 1938 /** 1939 * Call parent constructor with passed arguments 1940 */ 1941 Backbone.Model.apply( this, arguments ); 1942 this.on( 'change:menu', this._updateMenu, this ); 1943 }, 1944 /** 1945 * Ready event callback. 1946 * 1947 * @abstract 1948 * @since 3.5.0 1949 */ 1950 ready: function() {}, 2090 var Library = wp.media.controller.Library, 2091 MediaLibrary; 1951 2092 1952 /** 1953 * Activate event callback. 1954 * 1955 * @abstract 1956 * @since 3.5.0 1957 */ 1958 activate: function() {}, 2093 MediaLibrary = Library.extend(/** @lends wp.media.controller.MediaLibrary.prototype */{ 2094 defaults: _.defaults({ 2095 // Attachments browser defaults. @see media.view.AttachmentsBrowser 2096 filterable: 'uploaded', 1959 2097 1960 /** 1961 * Deactivate event callback. 1962 * 1963 * @abstract 1964 * @since 3.5.0 1965 */ 1966 deactivate: function() {}, 2098 displaySettings: false, 2099 priority: 80, 2100 syncSelection: false 2101 }, Library.prototype.defaults ), 1967 2102 1968 2103 /** 1969 * Reset event callback.2104 * @since 3.9.0 1970 2105 * 1971 * @abstract 1972 * @since 3.5.0 1973 */ 1974 reset: function() {}, 1975 1976 /** 1977 * @access private 1978 * @since 3.5.0 2106 * @param options 1979 2107 */ 1980 _ready: function() { 1981 this._updateMenu(); 1982 }, 2108 initialize: function( options ) { 2109 this.media = options.media; 2110 this.type = options.type; 2111 this.set( 'library', wp.media.query({ type: this.type }) ); 1983 2112 1984 /** 1985 * @access private 1986 * @since 3.5.0 1987 */ 1988 _preActivate: function() { 1989 this.active = true; 2113 Library.prototype.initialize.apply( this, arguments ); 1990 2114 }, 1991 2115 1992 2116 /** 1993 * @access private 1994 * @since 3.5.0 2117 * @since 3.9.0 1995 2118 */ 1996 _postActivate: function() { 1997 this.on( 'change:menu', this._menu, this ); 1998 this.on( 'change:titleMode', this._title, this ); 1999 this.on( 'change:content', this._content, this ); 2000 this.on( 'change:toolbar', this._toolbar, this ); 2119 activate: function() { 2120 // @todo this should use this.frame. 2121 if ( wp.media.frame.lastMime ) { 2122 this.set( 'library', wp.media.query({ type: wp.media.frame.lastMime }) ); 2123 delete wp.media.frame.lastMime; 2124 } 2125 Library.prototype.activate.apply( this, arguments ); 2126 } 2127 }); 2001 2128 2002 this.frame.on( 'title:render:default', this._renderTitle, this );2129 module.exports = MediaLibrary; 2003 2130 2004 this._title();2005 this._menu();2006 this._toolbar();2007 this._content();2008 this._router();2009 },2010 2131 2011 /** 2012 * @access private 2013 * @since 3.5.0 2014 */ 2015 _deactivate: function() { 2016 this.active = false; 2132 /***/ }), 2133 /* 41 */ 2134 /***/ (function(module, exports) { 2017 2135 2018 this.frame.off( 'title:render:default', this._renderTitle, this ); 2136 var l10n = wp.media.view.l10n, 2137 $ = Backbone.$, 2138 Embed; 2019 2139 2020 this.off( 'change:menu', this._menu, this ); 2021 this.off( 'change:titleMode', this._title, this ); 2022 this.off( 'change:content', this._content, this ); 2023 this.off( 'change:toolbar', this._toolbar, this ); 2140 /** 2141 * wp.media.controller.Embed 2142 * 2143 * A state for embedding media from a URL. 2144 * 2145 * @memberOf wp.media.controller 2146 * 2147 * @class 2148 * @augments wp.media.controller.State 2149 * @augments Backbone.Model 2150 * 2151 * @param {object} attributes The attributes hash passed to the state. 2152 * @param {string} [attributes.id=embed] Unique identifier. 2153 * @param {string} [attributes.title=Insert From URL] Title for the state. Displays in the media menu and the frame's title region. 2154 * @param {string} [attributes.content=embed] Initial mode for the content region. 2155 * @param {string} [attributes.menu=default] Initial mode for the menu region. 2156 * @param {string} [attributes.toolbar=main-embed] Initial mode for the toolbar region. 2157 * @param {string} [attributes.menu=false] Initial mode for the menu region. 2158 * @param {int} [attributes.priority=120] The priority for the state link in the media menu. 2159 * @param {string} [attributes.type=link] The type of embed. Currently only link is supported. 2160 * @param {string} [attributes.url] The embed URL. 2161 * @param {object} [attributes.metadata={}] Properties of the embed, which will override attributes.url if set. 2162 */ 2163 Embed = wp.media.controller.State.extend(/** @lends wp.media.controller.Embed.prototype */{ 2164 defaults: { 2165 id: 'embed', 2166 title: l10n.insertFromUrlTitle, 2167 content: 'embed', 2168 menu: 'default', 2169 toolbar: 'main-embed', 2170 priority: 120, 2171 type: 'link', 2172 url: '', 2173 metadata: {} 2024 2174 }, 2025 2175 2026 /** 2027 * @access private 2028 * @since 3.5.0 2029 */ 2030 _title: function() { 2031 this.frame.title.render( this.get('titleMode') || 'default' ); 2032 }, 2176 // The amount of time used when debouncing the scan. 2177 sensitivity: 400, 2033 2178 2034 /** 2035 * @access private 2036 * @since 3.5.0 2037 */ 2038 _renderTitle: function( view ) { 2039 view.$el.text( this.get('title') || '' ); 2179 initialize: function(options) { 2180 this.metadata = options.metadata; 2181 this.debouncedScan = _.debounce( _.bind( this.scan, this ), this.sensitivity ); 2182 this.props = new Backbone.Model( this.metadata || { url: '' }); 2183 this.props.on( 'change:url', this.debouncedScan, this ); 2184 this.props.on( 'change:url', this.refresh, this ); 2185 this.on( 'scan', this.scanImage, this ); 2040 2186 }, 2041 2187 2042 2188 /** 2043 * @access private 2044 * @since 3.5.0 2189 * Trigger a scan of the embedded URL's content for metadata required to embed. 2190 * 2191 * @fires wp.media.controller.Embed#scan 2045 2192 */ 2046 _router: function() { 2047 var router = this.frame.router, 2048 mode = this.get('router'), 2049 view; 2193 scan: function() { 2194 var scanners, 2195 embed = this, 2196 attributes = { 2197 type: 'link', 2198 scanners: [] 2199 }; 2050 2200 2051 this.frame.$el.toggleClass( 'hide-router', ! mode ); 2052 if ( ! mode ) { 2053 return; 2201 // Scan is triggered with the list of `attributes` to set on the 2202 // state, useful for the 'type' attribute and 'scanners' attribute, 2203 // an array of promise objects for asynchronous scan operations. 2204 if ( this.props.get('url') ) { 2205 this.trigger( 'scan', attributes ); 2054 2206 } 2055 2207 2056 this.frame.router.render( mode ); 2057 2058 view = router.get(); 2059 if ( view && view.select ) { 2060 view.select( this.frame.content.mode() ); 2208 if ( attributes.scanners.length ) { 2209 scanners = attributes.scanners = $.when.apply( $, attributes.scanners ); 2210 scanners.always( function() { 2211 if ( embed.get('scanners') === scanners ) { 2212 embed.set( 'loading', false ); 2213 } 2214 }); 2215 } else { 2216 attributes.scanners = null; 2061 2217 } 2062 },2063 2218 2219 attributes.loading = !! attributes.scanners; 2220 this.set( attributes ); 2221 }, 2064 2222 /** 2065 * @access private 2066 * @since 3.5.0 2223 * Try scanning the embed as an image to discover its dimensions. 2224 * 2225 * @param {Object} attributes 2067 2226 */ 2068 _menu: function() { 2069 var menu = this.frame.menu, 2070 mode = this.get('menu'), 2071 view; 2227 scanImage: function( attributes ) { 2228 var frame = this.frame, 2229 state = this, 2230 url = this.props.get('url'), 2231 image = new Image(), 2232 deferred = $.Deferred(); 2072 2233 2073 this.frame.$el.toggleClass( 'hide-menu', ! mode ); 2074 if ( ! mode ) { 2075 return; 2076 } 2234 attributes.scanners.push( deferred.promise() ); 2077 2235 2078 menu.mode( mode ); 2236 // Try to load the image and find its width/height. 2237 image.onload = function() { 2238 deferred.resolve(); 2079 2239 2080 view = menu.get(); 2081 if ( view && view.select ) { 2082 view.select( this.id ); 2083 } 2084 }, 2240 if ( state !== frame.state() || url !== state.props.get('url') ) { 2241 return; 2242 } 2085 2243 2086 /** 2087 * @access private 2088 * @since 3.5.0 2089 */ 2090 _updateMenu: function() { 2091 var previous = this.previous('menu'), 2092 menu = this.get('menu'); 2244 state.set({ 2245 type: 'image' 2246 }); 2093 2247 2094 if ( previous ) { 2095 this.frame.off( 'menu:render:' + previous, this._renderMenu, this ); 2096 } 2248 state.props.set({ 2249 width: image.width, 2250 height: image.height 2251 }); 2252 }; 2097 2253 2098 if ( menu ) { 2099 this.frame.on( 'menu:render:' + menu, this._renderMenu, this ); 2100 } 2254 image.onerror = deferred.reject; 2255 image.src = url; 2101 2256 }, 2102 2257 2103 /** 2104 * Create a view in the media menu for the state. 2105 * 2106 * @access private 2107 * @since 3.5.0 2108 * 2109 * @param {media.view.Menu} view The menu view. 2110 */ 2111 _renderMenu: function( view ) { 2112 var menuItem = this.get('menuItem'), 2113 title = this.get('title'), 2114 priority = this.get('priority'); 2115 2116 if ( ! menuItem && title ) { 2117 menuItem = { text: title }; 2258 refresh: function() { 2259 this.frame.toolbar.get().refresh(); 2260 }, 2118 2261 2119 if ( priority ) { 2120 menuItem.priority = priority; 2121 } 2122 } 2262 reset: function() { 2263 this.props.clear().set({ url: '' }); 2123 2264 2124 if ( ! menuItem) {2125 return;2265 if ( this.active ) { 2266 this.refresh(); 2126 2267 } 2127 2128 view.set( this.id, menuItem );2129 2268 } 2130 2269 }); 2131 2270 2132 _.each(['toolbar','content'], function( region ) { 2133 /** 2134 * @access private 2135 */ 2136 State.prototype[ '_' + region ] = function() { 2137 var mode = this.get( region ); 2138 if ( mode ) { 2139 this.frame[ region ].render( mode ); 2140 } 2141 }; 2142 }); 2271 module.exports = Embed; 2143 2272 2144 module.exports = State;2145 2273 2146 },{}],18:[function(require,module,exports){ 2274 /***/ }), 2275 /* 42 */ 2276 /***/ (function(module, exports) { 2277 2278 var l10n = wp.media.view.l10n, 2279 Cropper; 2280 2147 2281 /** 2148 * wp.media.selectionSync 2149 * 2150 * Sync an attachments selection in a state with another state. 2282 * wp.media.controller.Cropper 2151 2283 * 2152 * Allows for selecting multiple images in the Add Media workflow, and then 2153 * switching to the Insert Gallery workflow while preserving the attachments selection. 2284 * A state for cropping an image. 2154 2285 * 2155 * @memberOf wp.media 2286 * @memberOf wp.media.controller 2156 2287 * 2157 * @mixin 2288 * @class 2289 * @augments wp.media.controller.State 2290 * @augments Backbone.Model 2158 2291 */ 2159 var selectionSync = { 2160 /** 2161 * @since 3.5.0 2162 */ 2163 syncSelection: function() { 2164 var selection = this.get('selection'), 2165 manager = this.frame._selection; 2166 2167 if ( ! this.get('syncSelection') || ! manager || ! selection ) { 2168 return; 2169 } 2292 Cropper = wp.media.controller.State.extend(/** @lends wp.media.controller.Cropper.prototype */{ 2293 defaults: { 2294 id: 'cropper', 2295 title: l10n.cropImage, 2296 // Region mode defaults. 2297 toolbar: 'crop', 2298 content: 'crop', 2299 router: false, 2300 canSkipCrop: false, 2170 2301 2171 // If the selection supports multiple items, validate the stored 2172 // attachments based on the new selection's conditions. Record 2173 // the attachments that are not included; we'll maintain a 2174 // reference to those. Other attachments are considered in flux. 2175 if ( selection.multiple ) { 2176 selection.reset( [], { silent: true }); 2177 selection.validateAll( manager.attachments ); 2178 manager.difference = _.difference( manager.attachments.models, selection.models ); 2179 } 2302 // Default doCrop Ajax arguments to allow the Customizer (for example) to inject state. 2303 doCropArgs: {} 2304 }, 2180 2305 2181 // Sync the selection's single item with the master. 2182 selection.single( manager.single ); 2306 activate: function() { 2307 this.frame.on( 'content:create:crop', this.createCropContent, this ); 2308 this.frame.on( 'close', this.removeCropper, this ); 2309 this.set('selection', new Backbone.Collection(this.frame._selection.single)); 2183 2310 }, 2184 2311 2185 /** 2186 * Record the currently active attachments, which is a combination 2187 * of the selection's attachments and the set of selected 2188 * attachments that this specific selection considered invalid. 2189 * Reset the difference and record the single attachment. 2190 * 2191 * @since 3.5.0 2192 */ 2193 recordSelection: function() { 2194 var selection = this.get('selection'), 2195 manager = this.frame._selection; 2312 deactivate: function() { 2313 this.frame.toolbar.mode('browse'); 2314 }, 2196 2315 2197 if ( ! this.get('syncSelection') || ! manager || ! selection ) { 2198 return; 2199 } 2316 createCropContent: function() { 2317 this.cropperView = new wp.media.view.Cropper({ 2318 controller: this, 2319 attachment: this.get('selection').first() 2320 }); 2321 this.cropperView.on('image-loaded', this.createCropToolbar, this); 2322 this.frame.content.set(this.cropperView); 2200 2323 2201 if ( selection.multiple ) { 2202 manager.attachments.reset( selection.toArray().concat( manager.difference ) ); 2203 manager.difference = []; 2204 } else { 2205 manager.attachments.add( selection.toArray() ); 2206 } 2324 }, 2325 removeCropper: function() { 2326 this.imgSelect.cancelSelection(); 2327 this.imgSelect.setOptions({remove: true}); 2328 this.imgSelect.update(); 2329 this.cropperView.remove(); 2330 }, 2331 createCropToolbar: function() { 2332 var canSkipCrop, toolbarOptions; 2207 2333 2208 manager.single = selection._single; 2209 } 2210 }; 2334 canSkipCrop = this.get('canSkipCrop') || false; 2211 2335 2212 module.exports = selectionSync; 2336 toolbarOptions = { 2337 controller: this.frame, 2338 items: { 2339 insert: { 2340 style: 'primary', 2341 text: l10n.cropImage, 2342 priority: 80, 2343 requires: { library: false, selection: false }, 2213 2344 2214 },{}],19:[function(require,module,exports){ 2215 var media = wp.media, 2216 $ = jQuery, 2217 l10n; 2345 click: function() { 2346 var controller = this.controller, 2347 selection; 2218 2348 2219 media.isTouchDevice = ( 'ontouchend' in document ); 2349 selection = controller.state().get('selection').first(); 2350 selection.set({cropDetails: controller.state().imgSelect.getSelection()}); 2220 2351 2221 // Link any localized strings. 2222 l10n = media.view.l10n = window._wpMediaViewsL10n || {};2352 this.$el.text(l10n.cropping); 2353 this.$el.attr('disabled', true); 2223 2354 2224 // Link any settings. 2225 media.view.settings = l10n.settings || {}; 2226 delete l10n.settings; 2355 controller.state().doCrop( selection ).done( function( croppedImage ) { 2356 controller.trigger('cropped', croppedImage ); 2357 controller.close(); 2358 }).fail( function() { 2359 controller.trigger('content:error:crop'); 2360 }); 2361 } 2362 } 2363 } 2364 }; 2227 2365 2228 // Copy the `post` setting over to the model settings. 2229 media.model.settings.post = media.view.settings.post; 2366 if ( canSkipCrop ) { 2367 _.extend( toolbarOptions.items, { 2368 skip: { 2369 style: 'secondary', 2370 text: l10n.skipCropping, 2371 priority: 70, 2372 requires: { library: false, selection: false }, 2373 click: function() { 2374 var selection = this.controller.state().get('selection').first(); 2375 this.controller.state().cropperView.remove(); 2376 this.controller.trigger('skippedcrop', selection); 2377 this.controller.close(); 2378 } 2379 } 2380 }); 2381 } 2230 2382 2231 // Check if the browser supports CSS 3.0 transitions 2232 $.support.transition = (function(){ 2233 var style = document.documentElement.style, 2234 transitions = { 2235 WebkitTransition: 'webkitTransitionEnd', 2236 MozTransition: 'transitionend', 2237 OTransition: 'oTransitionEnd otransitionend', 2238 transition: 'transitionend' 2239 }, transition; 2383 this.frame.toolbar.set( new wp.media.view.Toolbar(toolbarOptions) ); 2384 }, 2240 2385 2241 transition = _.find( _.keys( transitions ), function( transition ) { 2242 return ! _.isUndefined( style[ transition ] ); 2243 }); 2386 doCrop: function( attachment ) { 2387 return wp.ajax.post( 'custom-header-crop', _.extend( 2388 {}, 2389 this.defaults.doCropArgs, 2390 { 2391 nonce: attachment.get( 'nonces' ).edit, 2392 id: attachment.get( 'id' ), 2393 cropDetails: attachment.get( 'cropDetails' ) 2394 } 2395 ) ); 2396 } 2397 }); 2244 2398 2245 return transition && { 2246 end: transitions[ transition ] 2247 }; 2248 }()); 2399 module.exports = Cropper; 2249 2400 2250 /** 2251 * A shared event bus used to provide events into 2252 * the media workflows that 3rd-party devs can use to hook 2253 * in. 2254 */ 2255 media.events = _.extend( {}, Backbone.Events ); 2401 2402 /***/ }), 2403 /* 43 */ 2404 /***/ (function(module, exports) { 2405 2406 var Controller = wp.media.controller, 2407 CustomizeImageCropper; 2256 2408 2257 2409 /** 2258 * Makes it easier to bind events using transitions.2410 * wp.media.controller.CustomizeImageCropper 2259 2411 * 2260 * @param {string} selector 2261 * @param {Number} sensitivity 2262 * @returns {Promise} 2412 * @memberOf wp.media.controller 2413 * 2414 * A state for cropping an image. 2415 * 2416 * @class 2417 * @augments wp.media.controller.Cropper 2418 * @augments wp.media.controller.State 2419 * @augments Backbone.Model 2263 2420 */ 2264 media.transition = function( selector, sensitivity ) { 2265 var deferred = $.Deferred(); 2421 CustomizeImageCropper = Controller.Cropper.extend(/** @lends wp.media.controller.CustomizeImageCropper.prototype */{ 2422 doCrop: function( attachment ) { 2423 var cropDetails = attachment.get( 'cropDetails' ), 2424 control = this.get( 'control' ), 2425 ratio = cropDetails.width / cropDetails.height; 2266 2426 2267 sensitivity = sensitivity || 2000; 2427 // Use crop measurements when flexible in both directions. 2428 if ( control.params.flex_width && control.params.flex_height ) { 2429 cropDetails.dst_width = cropDetails.width; 2430 cropDetails.dst_height = cropDetails.height; 2268 2431 2269 if ( $.support.transition ) { 2270 if ( ! (selector instanceof $) ) { 2271 selector = $( selector ); 2432 // Constrain flexible side based on image ratio and size of the fixed side. 2433 } else { 2434 cropDetails.dst_width = control.params.flex_width ? control.params.height * ratio : control.params.width; 2435 cropDetails.dst_height = control.params.flex_height ? control.params.width / ratio : control.params.height; 2272 2436 } 2273 2437 2274 // Resolve the deferred when the first element finishes animating. 2275 selector.first().one( $.support.transition.end, deferred.resolve ); 2438 return wp.ajax.post( 'crop-image', { 2439 wp_customize: 'on', 2440 nonce: attachment.get( 'nonces' ).edit, 2441 id: attachment.get( 'id' ), 2442 context: control.id, 2443 cropDetails: cropDetails 2444 } ); 2445 } 2446 }); 2276 2447 2277 // Just in case the event doesn't trigger, fire a callback. 2278 _.delay( deferred.resolve, sensitivity ); 2448 module.exports = CustomizeImageCropper; 2279 2449 2280 // Otherwise, execute on the spot.2281 } else {2282 deferred.resolve();2283 }2284 2450 2285 return deferred.promise(); 2286 }; 2451 /***/ }), 2452 /* 44 */ 2453 /***/ (function(module, exports) { 2287 2454 2288 media.controller.Region = require( './controllers/region.js' ); 2289 media.controller.StateMachine = require( './controllers/state-machine.js' ); 2290 media.controller.State = require( './controllers/state.js' ); 2291 2292 media.selectionSync = require( './utils/selection-sync.js' ); 2293 media.controller.Library = require( './controllers/library.js' ); 2294 media.controller.ImageDetails = require( './controllers/image-details.js' ); 2295 media.controller.GalleryEdit = require( './controllers/gallery-edit.js' ); 2296 media.controller.GalleryAdd = require( './controllers/gallery-add.js' ); 2297 media.controller.CollectionEdit = require( './controllers/collection-edit.js' ); 2298 media.controller.CollectionAdd = require( './controllers/collection-add.js' ); 2299 media.controller.FeaturedImage = require( './controllers/featured-image.js' ); 2300 media.controller.ReplaceImage = require( './controllers/replace-image.js' ); 2301 media.controller.EditImage = require( './controllers/edit-image.js' ); 2302 media.controller.MediaLibrary = require( './controllers/media-library.js' ); 2303 media.controller.Embed = require( './controllers/embed.js' ); 2304 media.controller.Cropper = require( './controllers/cropper.js' ); 2305 media.controller.CustomizeImageCropper = require( './controllers/customize-image-cropper.js' ); 2306 media.controller.SiteIconCropper = require( './controllers/site-icon-cropper.js' ); 2307 2308 media.View = require( './views/view.js' ); 2309 media.view.Frame = require( './views/frame.js' ); 2310 media.view.MediaFrame = require( './views/media-frame.js' ); 2311 media.view.MediaFrame.Select = require( './views/frame/select.js' ); 2312 media.view.MediaFrame.Post = require( './views/frame/post.js' ); 2313 media.view.MediaFrame.ImageDetails = require( './views/frame/image-details.js' ); 2314 media.view.Modal = require( './views/modal.js' ); 2315 media.view.FocusManager = require( './views/focus-manager.js' ); 2316 media.view.UploaderWindow = require( './views/uploader/window.js' ); 2317 media.view.EditorUploader = require( './views/uploader/editor.js' ); 2318 media.view.UploaderInline = require( './views/uploader/inline.js' ); 2319 media.view.UploaderStatus = require( './views/uploader/status.js' ); 2320 media.view.UploaderStatusError = require( './views/uploader/status-error.js' ); 2321 media.view.Toolbar = require( './views/toolbar.js' ); 2322 media.view.Toolbar.Select = require( './views/toolbar/select.js' ); 2323 media.view.Toolbar.Embed = require( './views/toolbar/embed.js' ); 2324 media.view.Button = require( './views/button.js' ); 2325 media.view.ButtonGroup = require( './views/button-group.js' ); 2326 media.view.PriorityList = require( './views/priority-list.js' ); 2327 media.view.MenuItem = require( './views/menu-item.js' ); 2328 media.view.Menu = require( './views/menu.js' ); 2329 media.view.RouterItem = require( './views/router-item.js' ); 2330 media.view.Router = require( './views/router.js' ); 2331 media.view.Sidebar = require( './views/sidebar.js' ); 2332 media.view.Attachment = require( './views/attachment.js' ); 2333 media.view.Attachment.Library = require( './views/attachment/library.js' ); 2334 media.view.Attachment.EditLibrary = require( './views/attachment/edit-library.js' ); 2335 media.view.Attachments = require( './views/attachments.js' ); 2336 media.view.Search = require( './views/search.js' ); 2337 media.view.AttachmentFilters = require( './views/attachment-filters.js' ); 2338 media.view.DateFilter = require( './views/attachment-filters/date.js' ); 2339 media.view.AttachmentFilters.Uploaded = require( './views/attachment-filters/uploaded.js' ); 2340 media.view.AttachmentFilters.All = require( './views/attachment-filters/all.js' ); 2341 media.view.AttachmentsBrowser = require( './views/attachments/browser.js' ); 2342 media.view.Selection = require( './views/selection.js' ); 2343 media.view.Attachment.Selection = require( './views/attachment/selection.js' ); 2344 media.view.Attachments.Selection = require( './views/attachments/selection.js' ); 2345 media.view.Attachment.EditSelection = require( './views/attachment/edit-selection.js' ); 2346 media.view.Settings = require( './views/settings.js' ); 2347 media.view.Settings.AttachmentDisplay = require( './views/settings/attachment-display.js' ); 2348 media.view.Settings.Gallery = require( './views/settings/gallery.js' ); 2349 media.view.Settings.Playlist = require( './views/settings/playlist.js' ); 2350 media.view.Attachment.Details = require( './views/attachment/details.js' ); 2351 media.view.AttachmentCompat = require( './views/attachment-compat.js' ); 2352 media.view.Iframe = require( './views/iframe.js' ); 2353 media.view.Embed = require( './views/embed.js' ); 2354 media.view.Label = require( './views/label.js' ); 2355 media.view.EmbedUrl = require( './views/embed/url.js' ); 2356 media.view.EmbedLink = require( './views/embed/link.js' ); 2357 media.view.EmbedImage = require( './views/embed/image.js' ); 2358 media.view.ImageDetails = require( './views/image-details.js' ); 2359 media.view.Cropper = require( './views/cropper.js' ); 2360 media.view.SiteIconCropper = require( './views/site-icon-cropper.js' ); 2361 media.view.SiteIconPreview = require( './views/site-icon-preview.js' ); 2362 media.view.EditImage = require( './views/edit-image.js' ); 2363 media.view.Spinner = require( './views/spinner.js' ); 2364 2365 },{"./controllers/collection-add.js":1,"./controllers/collection-edit.js":2,"./controllers/cropper.js":3,"./controllers/customize-image-cropper.js":4,"./controllers/edit-image.js":5,"./controllers/embed.js":6,"./controllers/featured-image.js":7,"./controllers/gallery-add.js":8,"./controllers/gallery-edit.js":9,"./controllers/image-details.js":10,"./controllers/library.js":11,"./controllers/media-library.js":12,"./controllers/region.js":13,"./controllers/replace-image.js":14,"./controllers/site-icon-cropper.js":15,"./controllers/state-machine.js":16,"./controllers/state.js":17,"./utils/selection-sync.js":18,"./views/attachment-compat.js":20,"./views/attachment-filters.js":21,"./views/attachment-filters/all.js":22,"./views/attachment-filters/date.js":23,"./views/attachment-filters/uploaded.js":24,"./views/attachment.js":25,"./views/attachment/details.js":26,"./views/attachment/edit-library.js":27,"./views/attachment/edit-selection.js":28,"./views/attachment/library.js":29,"./views/attachment/selection.js":30,"./views/attachments.js":31,"./views/attachments/browser.js":32,"./views/attachments/selection.js":33,"./views/button-group.js":34,"./views/button.js":35,"./views/cropper.js":36,"./views/edit-image.js":37,"./views/embed.js":38,"./views/embed/image.js":39,"./views/embed/link.js":40,"./views/embed/url.js":41,"./views/focus-manager.js":42,"./views/frame.js":43,"./views/frame/image-details.js":44,"./views/frame/post.js":45,"./views/frame/select.js":46,"./views/iframe.js":47,"./views/image-details.js":48,"./views/label.js":49,"./views/media-frame.js":50,"./views/menu-item.js":51,"./views/menu.js":52,"./views/modal.js":53,"./views/priority-list.js":54,"./views/router-item.js":55,"./views/router.js":56,"./views/search.js":57,"./views/selection.js":58,"./views/settings.js":59,"./views/settings/attachment-display.js":60,"./views/settings/gallery.js":61,"./views/settings/playlist.js":62,"./views/sidebar.js":63,"./views/site-icon-cropper.js":64,"./views/site-icon-preview.js":65,"./views/spinner.js":66,"./views/toolbar.js":67,"./views/toolbar/embed.js":68,"./views/toolbar/select.js":69,"./views/uploader/editor.js":70,"./views/uploader/inline.js":71,"./views/uploader/status-error.js":72,"./views/uploader/status.js":73,"./views/uploader/window.js":74,"./views/view.js":75}],20:[function(require,module,exports){ 2366 var View = wp.media.View, 2367 AttachmentCompat; 2455 var Controller = wp.media.controller, 2456 SiteIconCropper; 2368 2457 2369 2458 /** 2370 * wp.media. view.AttachmentCompat2459 * wp.media.controller.SiteIconCropper 2371 2460 * 2372 * A view to display fields added via the `attachment_fields_to_edit` filter.2461 * A state for cropping a Site Icon. 2373 2462 * 2374 * @memberOf wp.media. view2463 * @memberOf wp.media.controller 2375 2464 * 2376 2465 * @class 2377 * @augments wp.media. View2378 * @augments wp. Backbone.View2379 * @augments Backbone. View2466 * @augments wp.media.controller.Cropper 2467 * @augments wp.media.controller.State 2468 * @augments Backbone.Model 2380 2469 */ 2381 AttachmentCompat = View.extend(/** @lends wp.media.view.AttachmentCompat.prototype */{ 2382 tagName: 'form', 2383 className: 'compat-item', 2470 SiteIconCropper = Controller.Cropper.extend(/** @lends wp.media.controller.SiteIconCropper.prototype */{ 2471 activate: function() { 2472 this.frame.on( 'content:create:crop', this.createCropContent, this ); 2473 this.frame.on( 'close', this.removeCropper, this ); 2474 this.set('selection', new Backbone.Collection(this.frame._selection.single)); 2475 }, 2476 2477 createCropContent: function() { 2478 this.cropperView = new wp.media.view.SiteIconCropper({ 2479 controller: this, 2480 attachment: this.get('selection').first() 2481 }); 2482 this.cropperView.on('image-loaded', this.createCropToolbar, this); 2483 this.frame.content.set(this.cropperView); 2384 2484 2385 events: {2386 'submit': 'preventDefault',2387 'change input': 'save',2388 'change select': 'save',2389 'change textarea': 'save'2390 2485 }, 2391 2486 2392 initialize: function() { 2393 this.listenTo( this.model, 'change:compat', this.render ); 2487 doCrop: function( attachment ) { 2488 var cropDetails = attachment.get( 'cropDetails' ), 2489 control = this.get( 'control' ); 2490 2491 cropDetails.dst_width = control.params.width; 2492 cropDetails.dst_height = control.params.height; 2493 2494 return wp.ajax.post( 'crop-image', { 2495 nonce: attachment.get( 'nonces' ).edit, 2496 id: attachment.get( 'id' ), 2497 context: 'site-icon', 2498 cropDetails: cropDetails 2499 } ); 2500 } 2501 }); 2502 2503 module.exports = SiteIconCropper; 2504 2505 2506 /***/ }), 2507 /* 45 */ 2508 /***/ (function(module, exports) { 2509 2510 /** 2511 * wp.media.View 2512 * 2513 * The base view class for media. 2514 * 2515 * Undelegating events, removing events from the model, and 2516 * removing events from the controller mirror the code for 2517 * `Backbone.View.dispose` in Backbone 0.9.8 development. 2518 * 2519 * This behavior has since been removed, and should not be used 2520 * outside of the media manager. 2521 * 2522 * @memberOf wp.media 2523 * 2524 * @class 2525 * @augments wp.Backbone.View 2526 * @augments Backbone.View 2527 */ 2528 var View = wp.Backbone.View.extend(/** @lends wp.media.View.prototype */{ 2529 constructor: function( options ) { 2530 if ( options && options.controller ) { 2531 this.controller = options.controller; 2532 } 2533 wp.Backbone.View.apply( this, arguments ); 2394 2534 }, 2395 2535 /** 2396 * @returns {wp.media.view.AttachmentCompat} Returns itself to allow chaining 2536 * @todo The internal comment mentions this might have been a stop-gap 2537 * before Backbone 0.9.8 came out. Figure out if Backbone core takes 2538 * care of this in Backbone.View now. 2539 * 2540 * @returns {wp.media.View} Returns itself to allow chaining 2397 2541 */ 2398 2542 dispose: function() { 2399 if ( this.$(':focus').length ) { 2400 this.save(); 2543 // Undelegating events, removing events from the model, and 2544 // removing events from the controller mirror the code for 2545 // `Backbone.View.dispose` in Backbone 0.9.8 development. 2546 this.undelegateEvents(); 2547 2548 if ( this.model && this.model.off ) { 2549 this.model.off( null, null, this ); 2401 2550 } 2402 /** 2403 * call 'dispose' directly on the parent class 2404 */ 2405 return View.prototype.dispose.apply( this, arguments ); 2406 }, 2407 /** 2408 * @returns {wp.media.view.AttachmentCompat} Returns itself to allow chaining 2409 */ 2410 render: function() { 2411 var compat = this.model.get('compat'); 2412 if ( ! compat || ! compat.item ) { 2413 return; 2551 2552 if ( this.collection && this.collection.off ) { 2553 this.collection.off( null, null, this ); 2554 } 2555 2556 // Unbind controller events. 2557 if ( this.controller && this.controller.off ) { 2558 this.controller.off( null, null, this ); 2414 2559 } 2415 2560 2416 this.views.detach();2417 this.$el.html( compat.item );2418 this.views.render();2419 2561 return this; 2420 2562 }, 2421 2563 /** 2422 * @param {Object} event 2423 */ 2424 preventDefault: function( event ) { 2425 event.preventDefault(); 2426 }, 2427 /** 2428 * @param {Object} event 2564 * @returns {wp.media.View} Returns itself to allow chaining 2429 2565 */ 2430 save: function( event ) { 2431 var data = {}; 2432 2433 if ( event ) { 2434 event.preventDefault(); 2435 } 2436 2437 _.each( this.$el.serializeArray(), function( pair ) { 2438 data[ pair.name ] = pair.value; 2439 }); 2440 2441 this.controller.trigger( 'attachment:compat:waiting', ['waiting'] ); 2442 this.model.saveCompat( data ).always( _.bind( this.postSave, this ) ); 2443 }, 2444 2445 postSave: function() { 2446 this.controller.trigger( 'attachment:compat:ready', ['ready'] ); 2566 remove: function() { 2567 this.dispose(); 2568 /** 2569 * call 'remove' directly on the parent class 2570 */ 2571 return wp.Backbone.View.prototype.remove.apply( this, arguments ); 2447 2572 } 2448 2573 }); 2449 2574 2450 module.exports = AttachmentCompat;2575 module.exports = View; 2451 2576 2452 },{}],21:[function(require,module,exports){ 2453 var $ = jQuery, 2454 AttachmentFilters; 2577 2578 /***/ }), 2579 /* 46 */ 2580 /***/ (function(module, exports) { 2455 2581 2456 2582 /** 2457 * wp.media.view.AttachmentFilters 2583 * wp.media.view.Frame 2584 * 2585 * A frame is a composite view consisting of one or more regions and one or more 2586 * states. 2458 2587 * 2459 2588 * @memberOf wp.media.view 2460 2589 * 2590 * @see wp.media.controller.State 2591 * @see wp.media.controller.Region 2592 * 2461 2593 * @class 2462 2594 * @augments wp.media.View 2463 2595 * @augments wp.Backbone.View 2464 2596 * @augments Backbone.View 2597 * @mixes wp.media.controller.StateMachine 2465 2598 */ 2466 AttachmentFilters = wp.media.View.extend(/** @lends wp.media.view.AttachmentFilters.prototype */{ 2467 tagName: 'select', 2468 className: 'attachment-filters', 2469 id: 'media-attachment-filters', 2470 2471 events: { 2472 change: 'change' 2599 var Frame = wp.media.View.extend(/** @lends wp.media.view.Frame.prototype */{ 2600 initialize: function() { 2601 _.defaults( this.options, { 2602 mode: [ 'select' ] 2603 }); 2604 this._createRegions(); 2605 this._createStates(); 2606 this._createModes(); 2473 2607 }, 2474 2608 2475 keys: [], 2609 _createRegions: function() { 2610 // Clone the regions array. 2611 this.regions = this.regions ? this.regions.slice() : []; 2476 2612 2477 initialize: function() { 2478 this.createFilters(); 2479 _.extend( this.filters, this.options.filters ); 2613 // Initialize regions. 2614 _.each( this.regions, function( region ) { 2615 this[ region ] = new wp.media.controller.Region({ 2616 view: this, 2617 id: region, 2618 selector: '.media-frame-' + region 2619 }); 2620 }, this ); 2621 }, 2622 /** 2623 * Create the frame's states. 2624 * 2625 * @see wp.media.controller.State 2626 * @see wp.media.controller.StateMachine 2627 * 2628 * @fires wp.media.controller.State#ready 2629 */ 2630 _createStates: function() { 2631 // Create the default `states` collection. 2632 this.states = new Backbone.Collection( null, { 2633 model: wp.media.controller.State 2634 }); 2480 2635 2481 // Build `<option>` elements. 2482 this.$el.html( _.chain( this.filters ).map( function( filter, value ) { 2483 return { 2484 el: $( '<option></option>' ).val( value ).html( filter.text )[0], 2485 priority: filter.priority || 50 2486 }; 2487 }, this ).sortBy('priority').pluck('el').value() ); 2636 // Ensure states have a reference to the frame. 2637 this.states.on( 'add', function( model ) { 2638 model.frame = this; 2639 model.trigger('ready'); 2640 }, this ); 2488 2641 2489 this.listenTo( this.model, 'change', this.select ); 2490 this.select(); 2642 if ( this.options.states ) { 2643 this.states.add( this.options.states ); 2644 } 2491 2645 }, 2492 2646 2493 2647 /** 2494 * @abstract 2648 * A frame can be in a mode or multiple modes at one time. 2649 * 2650 * For example, the manage media frame can be in the `Bulk Select` or `Edit` mode. 2495 2651 */ 2496 createFilters: function() { 2497 this.filters = {}; 2498 }, 2652 _createModes: function() { 2653 // Store active "modes" that the frame is in. Unrelated to region modes. 2654 this.activeModes = new Backbone.Collection(); 2655 this.activeModes.on( 'add remove reset', _.bind( this.triggerModeEvents, this ) ); 2499 2656 2657 _.each( this.options.mode, function( mode ) { 2658 this.activateMode( mode ); 2659 }, this ); 2660 }, 2500 2661 /** 2501 * When the selected filter changes, update the Attachment Query properties to match. 2662 * Reset all states on the frame to their defaults. 2663 * 2664 * @returns {wp.media.view.Frame} Returns itself to allow chaining 2502 2665 */ 2503 change: function() { 2504 var filter = this.filters[ this.el.value ]; 2505 if ( filter ) { 2506 this.model.set( filter.props ); 2507 } 2666 reset: function() { 2667 this.states.invoke( 'trigger', 'reset' ); 2668 return this; 2508 2669 }, 2509 2510 select: function() { 2511 var model = this.model, 2512 value = 'all', 2513 props = model.toJSON(); 2514 2515 _.find( this.filters, function( filter, id ) { 2516 var equal = _.all( filter.props, function( prop, key ) { 2517 return prop === ( _.isUndefined( props[ key ] ) ? null : props[ key ] ); 2518 }); 2519 2520 if ( equal ) { 2521 return value = id; 2670 /** 2671 * Map activeMode collection events to the frame. 2672 */ 2673 triggerModeEvents: function( model, collection, options ) { 2674 var collectionEvent, 2675 modeEventMap = { 2676 add: 'activate', 2677 remove: 'deactivate' 2678 }, 2679 eventToTrigger; 2680 // Probably a better way to do this. 2681 _.each( options, function( value, key ) { 2682 if ( value ) { 2683 collectionEvent = key; 2522 2684 } 2523 }); 2524 2525 this.$el.val( value ); 2526 } 2527 }); 2685 } ); 2528 2686 2529 module.exports = AttachmentFilters; 2687 if ( ! _.has( modeEventMap, collectionEvent ) ) { 2688 return; 2689 } 2530 2690 2531 },{}],22:[function(require,module,exports){ 2532 var l10n = wp.media.view.l10n, 2533 All; 2534 2535 /** 2536 * wp.media.view.AttachmentFilters.All 2537 * 2538 * @memberOf wp.media.view.AttachmentFilters 2539 * 2540 * @class 2541 * @augments wp.media.view.AttachmentFilters 2542 * @augments wp.media.View 2543 * @augments wp.Backbone.View 2544 * @augments Backbone.View 2545 */ 2546 All = wp.media.view.AttachmentFilters.extend(/** @lends wp.media.view.AttachmentFilters.All.prototype */{ 2547 createFilters: function() { 2548 var filters = {}; 2549 2550 _.each( wp.media.view.settings.mimeTypes || {}, function( text, key ) { 2551 filters[ key ] = { 2552 text: text, 2553 props: { 2554 status: null, 2555 type: key, 2556 uploadedTo: null, 2557 orderby: 'date', 2558 order: 'DESC' 2559 } 2560 }; 2561 }); 2562 2563 filters.all = { 2564 text: l10n.allMediaItems, 2565 props: { 2566 status: null, 2567 type: null, 2568 uploadedTo: null, 2569 orderby: 'date', 2570 order: 'DESC' 2571 }, 2572 priority: 10 2573 }; 2574 2575 if ( wp.media.view.settings.post.id ) { 2576 filters.uploaded = { 2577 text: l10n.uploadedToThisPost, 2578 props: { 2579 status: null, 2580 type: null, 2581 uploadedTo: wp.media.view.settings.post.id, 2582 orderby: 'menuOrder', 2583 order: 'ASC' 2584 }, 2585 priority: 20 2586 }; 2691 eventToTrigger = model.get('id') + ':' + modeEventMap[collectionEvent]; 2692 this.trigger( eventToTrigger ); 2693 }, 2694 /** 2695 * Activate a mode on the frame. 2696 * 2697 * @param string mode Mode ID. 2698 * @returns {this} Returns itself to allow chaining. 2699 */ 2700 activateMode: function( mode ) { 2701 // Bail if the mode is already active. 2702 if ( this.isModeActive( mode ) ) { 2703 return; 2587 2704 } 2705 this.activeModes.add( [ { id: mode } ] ); 2706 // Add a CSS class to the frame so elements can be styled for the mode. 2707 this.$el.addClass( 'mode-' + mode ); 2588 2708 2589 filters.unattached = { 2590 text: l10n.unattached, 2591 props: { 2592 status: null, 2593 uploadedTo: 0, 2594 type: null, 2595 orderby: 'menuOrder', 2596 order: 'ASC' 2597 }, 2598 priority: 50 2599 }; 2600 2601 if ( wp.media.view.settings.mediaTrash && 2602 this.controller.isModeActive( 'grid' ) ) { 2603 2604 filters.trash = { 2605 text: l10n.trash, 2606 props: { 2607 uploadedTo: null, 2608 status: 'trash', 2609 type: null, 2610 orderby: 'date', 2611 order: 'DESC' 2612 }, 2613 priority: 50 2614 }; 2709 return this; 2710 }, 2711 /** 2712 * Deactivate a mode on the frame. 2713 * 2714 * @param string mode Mode ID. 2715 * @returns {this} Returns itself to allow chaining. 2716 */ 2717 deactivateMode: function( mode ) { 2718 // Bail if the mode isn't active. 2719 if ( ! this.isModeActive( mode ) ) { 2720 return this; 2615 2721 } 2722 this.activeModes.remove( this.activeModes.where( { id: mode } ) ); 2723 this.$el.removeClass( 'mode-' + mode ); 2724 /** 2725 * Frame mode deactivation event. 2726 * 2727 * @event wp.media.view.Frame#{mode}:deactivate 2728 */ 2729 this.trigger( mode + ':deactivate' ); 2616 2730 2617 this.filters = filters; 2731 return this; 2732 }, 2733 /** 2734 * Check if a mode is enabled on the frame. 2735 * 2736 * @param string mode Mode ID. 2737 * @return bool 2738 */ 2739 isModeActive: function( mode ) { 2740 return Boolean( this.activeModes.where( { id: mode } ).length ); 2618 2741 } 2619 2742 }); 2620 2743 2621 module.exports = All; 2622 2623 },{}],23:[function(require,module,exports){ 2624 var l10n = wp.media.view.l10n, 2625 DateFilter; 2744 // Make the `Frame` a `StateMachine`. 2745 _.extend( Frame.prototype, wp.media.controller.StateMachine.prototype ); 2626 2746 2627 /** 2628 * A filter dropdown for month/dates. 2629 * 2630 * @memberOf wp.media.view.AttachmentFilters 2631 * 2632 * @class 2633 * @augments wp.media.view.AttachmentFilters 2634 * @augments wp.media.View 2635 * @augments wp.Backbone.View 2636 * @augments Backbone.View 2637 */ 2638 DateFilter = wp.media.view.AttachmentFilters.extend(/** @lends wp.media.view.AttachmentFilters.Date.prototype */{ 2639 id: 'media-attachment-date-filters', 2747 module.exports = Frame; 2640 2748 2641 createFilters: function() {2642 var filters = {};2643 _.each( wp.media.view.settings.months || {}, function( value, index ) {2644 filters[ index ] = {2645 text: value.text,2646 props: {2647 year: value.year,2648 monthnum: value.month2649 }2650 };2651 });2652 filters.all = {2653 text: l10n.allDates,2654 props: {2655 monthnum: false,2656 year: false2657 },2658 priority: 102659 };2660 this.filters = filters;2661 }2662 });2663 2749 2664 module.exports = DateFilter; 2750 /***/ }), 2751 /* 47 */ 2752 /***/ (function(module, exports) { 2665 2753 2666 },{}],24:[function(require,module,exports){ 2667 var l10n = wp.media.view.l10n,2668 Uploaded;2754 var Frame = wp.media.view.Frame, 2755 $ = jQuery, 2756 MediaFrame; 2669 2757 2670 2758 /** 2671 * wp.media.view. AttachmentFilters.Uploaded2759 * wp.media.view.MediaFrame 2672 2760 * 2673 * @memberOf wp.media.view.AttachmentFilters 2761 * The frame used to create the media modal. 2762 * 2763 * @memberOf wp.media.view 2674 2764 * 2675 2765 * @class 2676 * @augments wp.media.view. AttachmentFilters2766 * @augments wp.media.view.Frame 2677 2767 * @augments wp.media.View 2678 2768 * @augments wp.Backbone.View 2679 2769 * @augments Backbone.View 2770 * @mixes wp.media.controller.StateMachine 2680 2771 */ 2681 Uploaded = wp.media.view.AttachmentFilters.extend(/** @lends wp.media.view.AttachmentFilters.Uploaded.prototype */{ 2682 createFilters: function() { 2683 var type = this.model.get('type'), 2684 types = wp.media.view.settings.mimeTypes, 2685 text; 2772 MediaFrame = Frame.extend(/** @lends wp.media.view.MediaFrame.prototype */{ 2773 className: 'media-frame', 2774 template: wp.template('media-frame'), 2775 regions: ['menu','title','content','toolbar','router'], 2686 2776 2687 if ( types && type ){2688 text = types[ type ];2689 }2777 events: { 2778 'click div.media-frame-title h1': 'toggleMenu' 2779 }, 2690 2780 2691 this.filters = { 2692 all: { 2693 text: text || l10n.allMediaItems, 2694 props: { 2695 uploadedTo: null, 2696 orderby: 'date', 2697 order: 'DESC' 2698 }, 2699 priority: 10 2700 }, 2781 /** 2782 * @constructs 2783 */ 2784 initialize: function() { 2785 Frame.prototype.initialize.apply( this, arguments ); 2701 2786 2702 uploaded: { 2703 text: l10n.uploadedToThisPost, 2704 props: { 2705 uploadedTo: wp.media.view.settings.post.id, 2706 orderby: 'menuOrder', 2707 order: 'ASC' 2708 }, 2709 priority: 20 2710 }, 2787 _.defaults( this.options, { 2788 title: '', 2789 modal: true, 2790 uploader: true 2791 }); 2711 2792 2712 unattached: { 2713 text: l10n.unattached, 2714 props: { 2715 uploadedTo: 0, 2716 orderby: 'menuOrder', 2717 order: 'ASC' 2718 }, 2719 priority: 50 2720 } 2721 }; 2722 } 2723 }); 2793 // Ensure core UI is enabled. 2794 this.$el.addClass('wp-core-ui'); 2724 2795 2725 module.exports = Uploaded; 2796 // Initialize modal container view. 2797 if ( this.options.modal ) { 2798 this.modal = new wp.media.view.Modal({ 2799 controller: this, 2800 title: this.options.title 2801 }); 2726 2802 2727 },{}],25:[function(require,module,exports){ 2728 var View = wp.media.View, 2729 $ = jQuery, 2730 Attachment; 2803 this.modal.content( this ); 2804 } 2731 2805 2732 /** 2733 * wp.media.view.Attachment 2734 * 2735 * @memberOf wp.media.view 2736 * 2737 * @class 2738 * @augments wp.media.View 2739 * @augments wp.Backbone.View 2740 * @augments Backbone.View 2741 */ 2742 Attachment = View.extend(/** @lends wp.media.view.Attachment.prototype */{ 2743 tagName: 'li', 2744 className: 'attachment', 2745 template: wp.template('attachment'), 2746 2747 attributes: function() { 2748 return { 2749 'tabIndex': 0, 2750 'role': 'checkbox', 2751 'aria-label': this.model.get( 'title' ), 2752 'aria-checked': false, 2753 'data-id': this.model.get( 'id' ) 2754 }; 2755 }, 2756 2757 events: { 2758 'click': 'toggleSelectionHandler', 2759 'change [data-setting]': 'updateSetting', 2760 'change [data-setting] input': 'updateSetting', 2761 'change [data-setting] select': 'updateSetting', 2762 'change [data-setting] textarea': 'updateSetting', 2763 'click .attachment-close': 'removeFromLibrary', 2764 'click .check': 'checkClickHandler', 2765 'keydown': 'toggleSelectionHandler' 2766 }, 2806 // Force the uploader off if the upload limit has been exceeded or 2807 // if the browser isn't supported. 2808 if ( wp.Uploader.limitExceeded || ! wp.Uploader.browser.supported ) { 2809 this.options.uploader = false; 2810 } 2767 2811 2768 buttons: {}, 2812 // Initialize window-wide uploader. 2813 if ( this.options.uploader ) { 2814 this.uploader = new wp.media.view.UploaderWindow({ 2815 controller: this, 2816 uploader: { 2817 dropzone: this.modal ? this.modal.$el : this.$el, 2818 container: this.$el 2819 } 2820 }); 2821 this.views.set( '.media-frame-uploader', this.uploader ); 2822 } 2769 2823 2770 initialize: function() { 2771 var selection = this.options.selection, 2772 options = _.defaults( this.options, { 2773 rerenderOnModelChange: true 2774 } ); 2824 this.on( 'attach', _.bind( this.views.ready, this.views ), this ); 2775 2825 2776 if ( options.rerenderOnModelChange ) { 2777 this.listenTo( this.model, 'change', this.render ); 2778 } else { 2779 this.listenTo( this.model, 'change:percent', this.progress ); 2780 } 2781 this.listenTo( this.model, 'change:title', this._syncTitle ); 2782 this.listenTo( this.model, 'change:caption', this._syncCaption ); 2783 this.listenTo( this.model, 'change:artist', this._syncArtist ); 2784 this.listenTo( this.model, 'change:album', this._syncAlbum ); 2826 // Bind default title creation. 2827 this.on( 'title:create:default', this.createTitle, this ); 2828 this.title.mode('default'); 2785 2829 2786 // Update the selection. 2787 this.listenTo( this.model, 'add', this.select ); 2788 this.listenTo( this.model, 'remove', this.deselect ); 2789 if ( selection ) { 2790 selection.on( 'reset', this.updateSelect, this ); 2791 // Update the model's details view. 2792 this.listenTo( this.model, 'selection:single selection:unsingle', this.details ); 2793 this.details( this.model, this.controller.state().get('selection') ); 2794 } 2830 this.on( 'title:render', function( view ) { 2831 view.$el.append( '<span class="dashicons dashicons-arrow-down"></span>' ); 2832 }); 2795 2833 2796 this.listenTo( this.controller, 'attachment:compat:waiting attachment:compat:ready', this.updateSave ); 2834 // Bind default menu. 2835 this.on( 'menu:create:default', this.createMenu, this ); 2797 2836 }, 2798 2837 /** 2799 * @returns {wp.media.view. Attachment} Returns itself to allow chaining2838 * @returns {wp.media.view.MediaFrame} Returns itself to allow chaining 2800 2839 */ 2801 dispose: function() { 2802 var selection = this.options.selection; 2803 2804 // Make sure all settings are saved before removing the view. 2805 this.updateAll(); 2806 2807 if ( selection ) { 2808 selection.off( null, null, this ); 2840 render: function() { 2841 // Activate the default state if no active state exists. 2842 if ( ! this.state() && this.options.state ) { 2843 this.setState( this.options.state ); 2809 2844 } 2810 2845 /** 2811 * call ' dispose' directly on the parent class2846 * call 'render' directly on the parent class 2812 2847 */ 2813 View.prototype.dispose.apply( this, arguments ); 2814 return this; 2848 return Frame.prototype.render.apply( this, arguments ); 2815 2849 }, 2816 2850 /** 2817 * @returns {wp.media.view.Attachment} Returns itself to allow chaining 2851 * @param {Object} title 2852 * @this wp.media.controller.Region 2818 2853 */ 2819 render: function() { 2820 var options = _.defaults( this.model.toJSON(), { 2821 orientation: 'landscape', 2822 uploading: false, 2823 type: '', 2824 subtype: '', 2825 icon: '', 2826 filename: '', 2827 caption: '', 2828 title: '', 2829 dateFormatted: '', 2830 width: '', 2831 height: '', 2832 compat: false, 2833 alt: '', 2834 description: '' 2835 }, this.options ); 2836 2837 options.buttons = this.buttons; 2838 options.describe = this.controller.state().get('describe'); 2854 createTitle: function( title ) { 2855 title.view = new wp.media.View({ 2856 controller: this, 2857 tagName: 'h1' 2858 }); 2859 }, 2860 /** 2861 * @param {Object} menu 2862 * @this wp.media.controller.Region 2863 */ 2864 createMenu: function( menu ) { 2865 menu.view = new wp.media.view.Menu({ 2866 controller: this 2867 }); 2868 }, 2839 2869 2840 if ( 'image' === options.type) {2841 options.size = this.imageSize();2842 }2870 toggleMenu: function() { 2871 this.$el.find( '.media-menu' ).toggleClass( 'visible' ); 2872 }, 2843 2873 2844 options.can = {}; 2845 if ( options.nonces ) { 2846 options.can.remove = !! options.nonces['delete']; 2847 options.can.save = !! options.nonces.update; 2848 } 2874 /** 2875 * @param {Object} toolbar 2876 * @this wp.media.controller.Region 2877 */ 2878 createToolbar: function( toolbar ) { 2879 toolbar.view = new wp.media.view.Toolbar({ 2880 controller: this 2881 }); 2882 }, 2883 /** 2884 * @param {Object} router 2885 * @this wp.media.controller.Region 2886 */ 2887 createRouter: function( router ) { 2888 router.view = new wp.media.view.Router({ 2889 controller: this 2890 }); 2891 }, 2892 /** 2893 * @param {Object} options 2894 */ 2895 createIframeStates: function( options ) { 2896 var settings = wp.media.view.settings, 2897 tabs = settings.tabs, 2898 tabUrl = settings.tabUrl, 2899 $postId; 2849 2900 2850 if ( this.controller.state().get('allowLocalEdits')) {2851 options.allowLocalEdits = true;2901 if ( ! tabs || ! tabUrl ) { 2902 return; 2852 2903 } 2853 2904 2854 if ( options.uploading && ! options.percent ) { 2855 options.percent = 0; 2905 // Add the post ID to the tab URL if it exists. 2906 $postId = $('#post_ID'); 2907 if ( $postId.length ) { 2908 tabUrl += '&post_id=' + $postId.val(); 2856 2909 } 2857 2910 2858 this.views.detach(); 2859 this.$el.html( this.template( options ) ); 2911 // Generate the tab states. 2912 _.each( tabs, function( title, id ) { 2913 this.state( 'iframe:' + id ).set( _.defaults({ 2914 tab: id, 2915 src: tabUrl + '&tab=' + id, 2916 title: title, 2917 content: 'iframe', 2918 menu: 'default' 2919 }, options ) ); 2920 }, this ); 2860 2921 2861 this.$el.toggleClass( 'uploading', options.uploading ); 2922 this.on( 'content:create:iframe', this.iframeContent, this ); 2923 this.on( 'content:deactivate:iframe', this.iframeContentCleanup, this ); 2924 this.on( 'menu:render:default', this.iframeMenu, this ); 2925 this.on( 'open', this.hijackThickbox, this ); 2926 this.on( 'close', this.restoreThickbox, this ); 2927 }, 2862 2928 2863 if ( options.uploading ) { 2864 this.$bar = this.$('.media-progress-bar div'); 2865 } else { 2866 delete this.$bar; 2867 } 2929 /** 2930 * @param {Object} content 2931 * @this wp.media.controller.Region 2932 */ 2933 iframeContent: function( content ) { 2934 this.$el.addClass('hide-toolbar'); 2935 content.view = new wp.media.view.Iframe({ 2936 controller: this 2937 }); 2938 }, 2868 2939 2869 // Check if the model is selected. 2870 this.updateSelect(); 2940 iframeContentCleanup: function() { 2941 this.$el.removeClass('hide-toolbar'); 2942 }, 2871 2943 2872 // Update the save status.2873 this.updateSave();2944 iframeMenu: function( view ) { 2945 var views = {}; 2874 2946 2875 this.views.render(); 2947 if ( ! view ) { 2948 return; 2949 } 2876 2950 2877 return this; 2878 }, 2951 _.each( wp.media.view.settings.tabs, function( title, id ) { 2952 views[ 'iframe:' + id ] = { 2953 text: this.state( 'iframe:' + id ).get('title'), 2954 priority: 200 2955 }; 2956 }, this ); 2879 2957 2880 progress: function() { 2881 if ( this.$bar && this.$bar.length ) { 2882 this.$bar.width( this.model.get('percent') + '%' ); 2883 } 2958 view.set( views ); 2884 2959 }, 2885 2960 2886 /** 2887 * @param {Object} event 2888 */ 2889 toggleSelectionHandler: function( event ) { 2890 var method; 2961 hijackThickbox: function() { 2962 var frame = this; 2891 2963 2892 // Don't do anything inside inputs and on the attachment check and remove buttons. 2893 if ( 'INPUT' === event.target.nodeName || 'BUTTON' === event.target.nodeName ) { 2964 if ( ! window.tb_remove || this._tb_remove ) { 2894 2965 return; 2895 2966 } 2896 2967 2897 // Catch arrow events 2898 if ( 37 === event.keyCode || 38 === event.keyCode || 39 === event.keyCode || 40 === event.keyCode ) { 2899 this.controller.trigger( 'attachment:keydown:arrow', event ); 2900 return; 2901 } 2968 this._tb_remove = window.tb_remove; 2969 window.tb_remove = function() { 2970 frame.close(); 2971 frame.reset(); 2972 frame.setState( frame.options.state ); 2973 frame._tb_remove.call( window ); 2974 }; 2975 }, 2902 2976 2903 // Catch enter and space events2904 if ( 'keydown' === event.type && 13 !== event.keyCode && 32 !== event.keyCode ) {2977 restoreThickbox: function() { 2978 if ( ! this._tb_remove ) { 2905 2979 return; 2906 2980 } 2907 2981 2908 event.preventDefault(); 2909 2910 // In the grid view, bubble up an edit:attachment event to the controller. 2911 if ( this.controller.isModeActive( 'grid' ) ) { 2912 if ( this.controller.isModeActive( 'edit' ) ) { 2913 // Pass the current target to restore focus when closing 2914 this.controller.trigger( 'edit:attachment', this.model, event.currentTarget ); 2915 return; 2916 } 2917 2918 if ( this.controller.isModeActive( 'select' ) ) { 2919 method = 'toggle'; 2920 } 2921 } 2922 2923 if ( event.shiftKey ) { 2924 method = 'between'; 2925 } else if ( event.ctrlKey || event.metaKey ) { 2926 method = 'toggle'; 2927 } 2928 2929 this.toggleSelection({ 2930 method: method 2931 }); 2982 window.tb_remove = this._tb_remove; 2983 delete this._tb_remove; 2984 } 2985 }); 2932 2986 2933 this.controller.trigger( 'selection:toggle' ); 2934 }, 2987 // Map some of the modal's methods to the frame. 2988 _.each(['open','close','attach','detach','escape'], function( method ) { 2935 2989 /** 2936 * @param {Object} options 2990 * @function open 2991 * @memberOf wp.media.view.MediaFrame 2992 * @instance 2993 * 2994 * @returns {wp.media.view.MediaFrame} Returns itself to allow chaining 2937 2995 */ 2938 toggleSelection: function( options ) { 2939 var collection = this.collection, 2940 selection = this.options.selection, 2941 model = this.model, 2942 method = options && options.method, 2943 single, models, singleIndex, modelIndex; 2944 2945 if ( ! selection ) { 2946 return; 2996 /** 2997 * @function close 2998 * @memberOf wp.media.view.MediaFrame 2999 * @instance 3000 * 3001 * @returns {wp.media.view.MediaFrame} Returns itself to allow chaining 3002 */ 3003 /** 3004 * @function attach 3005 * @memberOf wp.media.view.MediaFrame 3006 * @instance 3007 * 3008 * @returns {wp.media.view.MediaFrame} Returns itself to allow chaining 3009 */ 3010 /** 3011 * @function detach 3012 * @memberOf wp.media.view.MediaFrame 3013 * @instance 3014 * 3015 * @returns {wp.media.view.MediaFrame} Returns itself to allow chaining 3016 */ 3017 /** 3018 * @function escape 3019 * @memberOf wp.media.view.MediaFrame 3020 * @instance 3021 * 3022 * @returns {wp.media.view.MediaFrame} Returns itself to allow chaining 3023 */ 3024 MediaFrame.prototype[ method ] = function() { 3025 if ( this.modal ) { 3026 this.modal[ method ].apply( this.modal, arguments ); 2947 3027 } 3028 return this; 3029 }; 3030 }); 2948 3031 2949 single = selection.single(); 2950 method = _.isUndefined( method ) ? selection.multiple : method; 2951 2952 // If the `method` is set to `between`, select all models that 2953 // exist between the current and the selected model. 2954 if ( 'between' === method && single && selection.multiple ) { 2955 // If the models are the same, short-circuit. 2956 if ( single === model ) { 2957 return; 2958 } 2959 2960 singleIndex = collection.indexOf( single ); 2961 modelIndex = collection.indexOf( this.model ); 3032 module.exports = MediaFrame; 2962 3033 2963 if ( singleIndex < modelIndex ) {2964 models = collection.models.slice( singleIndex, modelIndex + 1 );2965 } else {2966 models = collection.models.slice( modelIndex, singleIndex + 1 );2967 }2968 3034 2969 selection.add( models ); 2970 selection.single( model ); 2971 return; 3035 /***/ }), 3036 /* 48 */ 3037 /***/ (function(module, exports) { 2972 3038 2973 // If the `method` is set to `toggle`, just flip the selection 2974 // status, regardless of whether the model is the single model. 2975 } else if ( 'toggle' === method ) { 2976 selection[ this.selected() ? 'remove' : 'add' ]( model ); 2977 selection.single( model ); 2978 return; 2979 } else if ( 'add' === method ) { 2980 selection.add( model ); 2981 selection.single( model ); 2982 return; 2983 } 3039 var MediaFrame = wp.media.view.MediaFrame, 3040 l10n = wp.media.view.l10n, 3041 Select; 2984 3042 2985 // Fixes bug that loses focus when selecting a featured image 2986 if ( ! method ) { 2987 method = 'add'; 2988 } 3043 /** 3044 * wp.media.view.MediaFrame.Select 3045 * 3046 * A frame for selecting an item or items from the media library. 3047 * 3048 * @memberOf wp.media.view.MediaFrame 3049 * 3050 * @class 3051 * @augments wp.media.view.MediaFrame 3052 * @augments wp.media.view.Frame 3053 * @augments wp.media.View 3054 * @augments wp.Backbone.View 3055 * @augments Backbone.View 3056 * @mixes wp.media.controller.StateMachine 3057 */ 3058 Select = MediaFrame.extend(/** @lends wp.media.view.MediaFrame.Select.prototype */{ 3059 initialize: function() { 3060 // Call 'initialize' directly on the parent class. 3061 MediaFrame.prototype.initialize.apply( this, arguments ); 2989 3062 2990 if ( method !== 'add' ) { 2991 method = 'reset'; 2992 } 3063 _.defaults( this.options, { 3064 selection: [], 3065 library: {}, 3066 multiple: false, 3067 state: 'library' 3068 }); 2993 3069 2994 if ( this.selected() ) { 2995 // If the model is the single model, remove it. 2996 // If it is not the same as the single model, 2997 // it now becomes the single model. 2998 selection[ single === model ? 'remove' : 'single' ]( model ); 2999 } else { 3000 // If the model is not selected, run the `method` on the 3001 // selection. By default, we `reset` the selection, but the 3002 // `method` can be set to `add` the model to the selection. 3003 selection[ method ]( model ); 3004 selection.single( model ); 3005 } 3070 this.createSelection(); 3071 this.createStates(); 3072 this.bindHandlers(); 3006 3073 }, 3007 3074 3008 updateSelect: function() {3009 this[ this.selected() ? 'select' : 'deselect' ]();3010 },3011 3075 /** 3012 * @returns {unresolved|Boolean} 3076 * Attach a selection collection to the frame. 3077 * 3078 * A selection is a collection of attachments used for a specific purpose 3079 * by a media frame. e.g. Selecting an attachment (or many) to insert into 3080 * post content. 3081 * 3082 * @see media.model.Selection 3013 3083 */ 3014 selected: function() {3084 createSelection: function() { 3015 3085 var selection = this.options.selection; 3016 if ( selection ) { 3017 return !! selection.get( this.model.cid ); 3086 3087 if ( ! (selection instanceof wp.media.model.Selection) ) { 3088 this.options.selection = new wp.media.model.Selection( selection, { 3089 multiple: this.options.multiple 3090 }); 3018 3091 } 3092 3093 this._selection = { 3094 attachments: new wp.media.model.Attachments(), 3095 difference: [] 3096 }; 3019 3097 }, 3098 3020 3099 /** 3021 * @param {Backbone.Model} model 3022 * @param {Backbone.Collection} collection 3100 * Create the default states on the frame. 3023 3101 */ 3024 select: function( model, collection ) { 3025 var selection = this.options.selection, 3026 controller = this.controller; 3027 3028 // Check if a selection exists and if it's the collection provided. 3029 // If they're not the same collection, bail; we're in another 3030 // selection's event loop. 3031 if ( ! selection || ( collection && collection !== selection ) ) { 3032 return; 3033 } 3102 createStates: function() { 3103 var options = this.options; 3034 3104 3035 // Bail if the model is already selected. 3036 if ( this.$el.hasClass( 'selected' ) ) { 3105 if ( this.options.states ) { 3037 3106 return; 3038 3107 } 3039 3108 3040 // Add 'selected' class to model, set aria-checked to true. 3041 this.$el.addClass( 'selected' ).attr( 'aria-checked', true ); 3042 // Make the checkbox tabable, except in media grid (bulk select mode). 3043 if ( ! ( controller.isModeActive( 'grid' ) && controller.isModeActive( 'select' ) ) ) { 3044 this.$( '.check' ).attr( 'tabindex', '0' ); 3045 } 3109 // Add the default states. 3110 this.states.add([ 3111 // Main states. 3112 new wp.media.controller.Library({ 3113 library: wp.media.query( options.library ), 3114 multiple: options.multiple, 3115 title: options.title, 3116 priority: 20 3117 }) 3118 ]); 3046 3119 }, 3120 3047 3121 /** 3048 * @param {Backbone.Model} model 3049 * @param {Backbone.Collection} collection 3122 * Bind region mode event callbacks. 3123 * 3124 * @see media.controller.Region.render 3050 3125 */ 3051 deselect: function( model, collection ) { 3052 var selection = this.options.selection; 3126 bindHandlers: function() { 3127 this.on( 'router:create:browse', this.createRouter, this ); 3128 this.on( 'router:render:browse', this.browseRouter, this ); 3129 this.on( 'content:create:browse', this.browseContent, this ); 3130 this.on( 'content:render:upload', this.uploadContent, this ); 3131 this.on( 'toolbar:create:select', this.createSelectToolbar, this ); 3132 }, 3053 3133 3054 // Check if a selection exists and if it's the collection provided.3055 // If they're not the same collection, bail; we're in another3056 // selection's event loop.3057 if ( ! selection || ( collection && collection !== selection ) ) {3058 return;3059 }3060 this.$el.removeClass( 'selected' ).attr( 'aria-checked', false )3061 .find( '.check' ).attr( 'tabindex', '-1' );3062 },3063 3134 /** 3064 * @param {Backbone.Model} model 3065 * @param {Backbone.Collection} collection 3135 * Render callback for the router region in the `browse` mode. 3136 * 3137 * @param {wp.media.view.Router} routerView 3066 3138 */ 3067 details: function( model, collection ) { 3068 var selection = this.options.selection, 3069 details; 3070 3071 if ( selection !== collection ) { 3072 return; 3073 } 3074 3075 details = selection.single(); 3076 this.$el.toggleClass( 'details', details === this.model ); 3139 browseRouter: function( routerView ) { 3140 routerView.set({ 3141 upload: { 3142 text: l10n.uploadFilesTitle, 3143 priority: 20 3144 }, 3145 browse: { 3146 text: l10n.mediaLibraryTitle, 3147 priority: 40 3148 } 3149 }); 3077 3150 }, 3151 3078 3152 /** 3079 * @param {string} size 3080 * @returns {Object} 3153 * Render callback for the content region in the `browse` mode. 3154 * 3155 * @param {wp.media.controller.Region} contentRegion 3081 3156 */ 3082 imageSize: function( size) {3083 var s izes = this.model.get('sizes'), matched = false;3157 browseContent: function( contentRegion ) { 3158 var state = this.state(); 3084 3159 3085 size = size || 'medium';3160 this.$el.removeClass('hide-toolbar'); 3086 3161 3087 // Use the provided image size if possible. 3088 if ( sizes ) { 3089 if ( sizes[ size ] ) { 3090 matched = sizes[ size ]; 3091 } else if ( sizes.large ) { 3092 matched = sizes.large; 3093 } else if ( sizes.thumbnail ) { 3094 matched = sizes.thumbnail; 3095 } else if ( sizes.full ) { 3096 matched = sizes.full; 3097 } 3162 // Browse our library of attachments. 3163 contentRegion.view = new wp.media.view.AttachmentsBrowser({ 3164 controller: this, 3165 collection: state.get('library'), 3166 selection: state.get('selection'), 3167 model: state, 3168 sortable: state.get('sortable'), 3169 search: state.get('searchable'), 3170 filters: state.get('filterable'), 3171 date: state.get('date'), 3172 display: state.has('display') ? state.get('display') : state.get('displaySettings'), 3173 dragInfo: state.get('dragInfo'), 3098 3174 3099 if ( matched ) { 3100 return _.clone( matched ); 3101 } 3102 } 3175 idealColumnWidth: state.get('idealColumnWidth'), 3176 suggestedWidth: state.get('suggestedWidth'), 3177 suggestedHeight: state.get('suggestedHeight'), 3103 3178 3104 return { 3105 url: this.model.get('url'), 3106 width: this.model.get('width'), 3107 height: this.model.get('height'), 3108 orientation: this.model.get('orientation') 3109 }; 3179 AttachmentView: state.get('AttachmentView') 3180 }); 3110 3181 }, 3182 3111 3183 /** 3112 * @param {Object} event3184 * Render callback for the content region in the `upload` mode. 3113 3185 */ 3114 updateSetting: function( event ) { 3115 var $setting = $( event.target ).closest('[data-setting]'), 3116 setting, value; 3117 3118 if ( ! $setting.length ) { 3119 return; 3120 } 3121 3122 setting = $setting.data('setting'); 3123 value = event.target.value; 3124 3125 if ( this.model.get( setting ) !== value ) { 3126 this.save( setting, value ); 3127 } 3186 uploadContent: function() { 3187 this.$el.removeClass( 'hide-toolbar' ); 3188 this.content.set( new wp.media.view.UploaderInline({ 3189 controller: this 3190 }) ); 3128 3191 }, 3129 3192 3130 3193 /** 3131 * Pass all the arguments to the model's save method.3194 * Toolbars 3132 3195 * 3133 * Records the aggregate status of all save requests and updates the 3134 * view's classes accordingly. 3196 * @param {Object} toolbar 3197 * @param {Object} [options={}] 3198 * @this wp.media.controller.Region 3135 3199 */ 3136 save: function() { 3137 var view = this, 3138 save = this._save = this._save || { status: 'ready' }, 3139 request = this.model.save.apply( this.model, arguments ), 3140 requests = save.requests ? $.when( request, save.requests ) : request; 3141 3142 // If we're waiting to remove 'Saved.', stop. 3143 if ( save.savedTimer ) { 3144 clearTimeout( save.savedTimer ); 3145 } 3146 3147 this.updateSave('waiting'); 3148 save.requests = requests; 3149 requests.always( function() { 3150 // If we've performed another request since this one, bail. 3151 if ( save.requests !== requests ) { 3152 return; 3153 } 3200 createSelectToolbar: function( toolbar, options ) { 3201 options = options || this.options.button || {}; 3202 options.controller = this; 3154 3203 3155 view.updateSave( requests.state() === 'resolved' ? 'complete' : 'error' ); 3156 save.savedTimer = setTimeout( function() { 3157 view.updateSave('ready'); 3158 delete save.savedTimer; 3159 }, 2000 ); 3160 }); 3161 }, 3162 /** 3163 * @param {string} status 3164 * @returns {wp.media.view.Attachment} Returns itself to allow chaining 3165 */ 3166 updateSave: function( status ) { 3167 var save = this._save = this._save || { status: 'ready' }; 3204 toolbar.view = new wp.media.view.Toolbar.Select( options ); 3205 } 3206 }); 3168 3207 3169 if ( status && status !== save.status ) { 3170 this.$el.removeClass( 'save-' + save.status ); 3171 save.status = status; 3172 } 3208 module.exports = Select; 3173 3209 3174 this.$el.addClass( 'save-' + save.status );3175 return this;3176 },3177 3210 3178 updateAll: function() { 3179 var $settings = this.$('[data-setting]'), 3180 model = this.model, 3181 changed; 3211 /***/ }), 3212 /* 49 */ 3213 /***/ (function(module, exports) { 3182 3214 3183 changed = _.chain( $settings ).map( function( el ) { 3184 var $input = $('input, textarea, select, [value]', el ), 3185 setting, value; 3215 var Select = wp.media.view.MediaFrame.Select, 3216 Library = wp.media.controller.Library, 3217 l10n = wp.media.view.l10n, 3218 Post; 3186 3219 3187 if ( ! $input.length ) { 3188 return; 3220 /** 3221 * wp.media.view.MediaFrame.Post 3222 * 3223 * The frame for manipulating media on the Edit Post page. 3224 * 3225 * @memberOf wp.media.view.MediaFrame 3226 * 3227 * @class 3228 * @augments wp.media.view.MediaFrame.Select 3229 * @augments wp.media.view.MediaFrame 3230 * @augments wp.media.view.Frame 3231 * @augments wp.media.View 3232 * @augments wp.Backbone.View 3233 * @augments Backbone.View 3234 * @mixes wp.media.controller.StateMachine 3235 */ 3236 Post = Select.extend(/** @lends wp.media.view.MediaFrame.Post.prototype */{ 3237 initialize: function() { 3238 this.counts = { 3239 audio: { 3240 count: wp.media.view.settings.attachmentCounts.audio, 3241 state: 'playlist' 3242 }, 3243 video: { 3244 count: wp.media.view.settings.attachmentCounts.video, 3245 state: 'video-playlist' 3189 3246 } 3247 }; 3190 3248 3191 setting = $(el).data('setting'); 3192 value = $input.val(); 3249 _.defaults( this.options, { 3250 multiple: true, 3251 editing: false, 3252 state: 'insert', 3253 metadata: {} 3254 }); 3193 3255 3194 // Record the value if it changed. 3195 if ( model.get( setting ) !== value ) { 3196 return [ setting, value ]; 3197 } 3198 }).compact().object().value(); 3256 // Call 'initialize' directly on the parent class. 3257 Select.prototype.initialize.apply( this, arguments ); 3258 this.createIframeStates(); 3199 3259 3200 if ( ! _.isEmpty( changed ) ) {3201 model.save( changed );3202 }3203 3260 }, 3261 3204 3262 /** 3205 * @param {Object} event3263 * Create the default states. 3206 3264 */ 3207 removeFromLibrary: function( event ) { 3208 // Catch enter and space events 3209 if ( 'keydown' === event.type && 13 !== event.keyCode && 32 !== event.keyCode ) { 3210 return; 3211 } 3265 createStates: function() { 3266 var options = this.options; 3212 3267 3213 // Stop propagation so the model isn't selected. 3214 event.stopPropagation(); 3268 this.states.add([ 3269 // Main states. 3270 new Library({ 3271 id: 'insert', 3272 title: l10n.insertMediaTitle, 3273 priority: 20, 3274 toolbar: 'main-insert', 3275 filterable: 'all', 3276 library: wp.media.query( options.library ), 3277 multiple: options.multiple ? 'reset' : false, 3278 editable: true, 3215 3279 3216 this.collection.remove( this.model ); 3217 }, 3280 // If the user isn't allowed to edit fields, 3281 // can they still edit it locally? 3282 allowLocalEdits: true, 3218 3283 3219 /** 3220 * Add the model if it isn't in the selection, if it is in the selection, 3221 * remove it. 3222 * 3223 * @param {[type]} event [description] 3224 * @return {[type]} [description] 3225 */ 3226 checkClickHandler: function ( event ) { 3227 var selection = this.options.selection; 3228 if ( ! selection ) { 3229 return; 3230 } 3231 event.stopPropagation(); 3232 if ( selection.where( { id: this.model.get( 'id' ) } ).length ) { 3233 selection.remove( this.model ); 3234 // Move focus back to the attachment tile (from the check). 3235 this.$el.focus(); 3236 } else { 3237 selection.add( this.model ); 3238 } 3239 } 3240 }); 3241 3242 // Ensure settings remain in sync between attachment views. 3243 _.each({ 3244 caption: '_syncCaption', 3245 title: '_syncTitle', 3246 artist: '_syncArtist', 3247 album: '_syncAlbum' 3248 }, function( method, setting ) { 3249 /** 3250 * @function _syncCaption 3251 * @memberOf wp.media.view.Attachment 3252 * @instance 3253 * 3254 * @param {Backbone.Model} model 3255 * @param {string} value 3256 * @returns {wp.media.view.Attachment} Returns itself to allow chaining 3257 */ 3258 /** 3259 * @function _syncTitle 3260 * @memberOf wp.media.view.Attachment 3261 * @instance 3262 * 3263 * @param {Backbone.Model} model 3264 * @param {string} value 3265 * @returns {wp.media.view.Attachment} Returns itself to allow chaining 3266 */ 3267 /** 3268 * @function _syncArtist 3269 * @memberOf wp.media.view.Attachment 3270 * @instance 3271 * 3272 * @param {Backbone.Model} model 3273 * @param {string} value 3274 * @returns {wp.media.view.Attachment} Returns itself to allow chaining 3275 */ 3276 /** 3277 * @function _syncAlbum 3278 * @memberOf wp.media.view.Attachment 3279 * @instance 3280 * 3281 * @param {Backbone.Model} model 3282 * @param {string} value 3283 * @returns {wp.media.view.Attachment} Returns itself to allow chaining 3284 */ 3285 Attachment.prototype[ method ] = function( model, value ) { 3286 var $setting = this.$('[data-setting="' + setting + '"]'); 3287 3288 if ( ! $setting.length ) { 3289 return this; 3290 } 3284 // Show the attachment display settings. 3285 displaySettings: true, 3286 // Update user settings when users adjust the 3287 // attachment display settings. 3288 displayUserSettings: true 3289 }), 3291 3290 3292 // If the updated value is in sync with the value in the DOM, there 3293 // is no need to re-render. If we're currently editing the value, 3294 // it will automatically be in sync, suppressing the re-render for 3295 // the view we're editing, while updating any others. 3296 if ( value === $setting.find('input, textarea, select, [value]').val() ) { 3297 return this; 3298 } 3291 new Library({ 3292 id: 'gallery', 3293 title: l10n.createGalleryTitle, 3294 priority: 40, 3295 toolbar: 'main-gallery', 3296 filterable: 'uploaded', 3297 multiple: 'add', 3298 editable: false, 3299 3299 3300 return this.render(); 3301 }; 3302 }); 3300 library: wp.media.query( _.defaults({ 3301 type: 'image' 3302 }, options.library ) ) 3303 }), 3303 3304 3304 module.exports = Attachment; 3305 // Embed states. 3306 new wp.media.controller.Embed( { metadata: options.metadata } ), 3305 3307 3306 },{}],26:[function(require,module,exports){ 3307 var Attachment = wp.media.view.Attachment, 3308 l10n = wp.media.view.l10n, 3309 Details; 3308 new wp.media.controller.EditImage( { model: options.editImage } ), 3310 3309 3311 /** 3312 * wp.media.view.Attachment.Details 3313 * 3314 * @memberOf wp.media.view.Attachment 3315 * 3316 * @class 3317 * @augments wp.media.view.Attachment 3318 * @augments wp.media.View 3319 * @augments wp.Backbone.View 3320 * @augments Backbone.View 3321 */ 3322 Details = Attachment.extend(/** @lends wp.media.view.Attachment.Details.prototype */{ 3323 tagName: 'div', 3324 className: 'attachment-details', 3325 template: wp.template('attachment-details'), 3310 // Gallery states. 3311 new wp.media.controller.GalleryEdit({ 3312 library: options.selection, 3313 editing: options.editing, 3314 menu: 'gallery' 3315 }), 3326 3316 3327 attributes: function() { 3328 return { 3329 'tabIndex': 0, 3330 'data-id': this.model.get( 'id' ) 3331 }; 3332 }, 3317 new wp.media.controller.GalleryAdd(), 3333 3318 3334 events: { 3335 'change [data-setting]': 'updateSetting', 3336 'change [data-setting] input': 'updateSetting', 3337 'change [data-setting] select': 'updateSetting', 3338 'change [data-setting] textarea': 'updateSetting', 3339 'click .delete-attachment': 'deleteAttachment', 3340 'click .trash-attachment': 'trashAttachment', 3341 'click .untrash-attachment': 'untrashAttachment', 3342 'click .edit-attachment': 'editAttachment', 3343 'keydown': 'toggleSelectionHandler' 3344 }, 3319 new Library({ 3320 id: 'playlist', 3321 title: l10n.createPlaylistTitle, 3322 priority: 60, 3323 toolbar: 'main-playlist', 3324 filterable: 'uploaded', 3325 multiple: 'add', 3326 editable: false, 3345 3327 3346 initialize: function(){3347 this.options = _.defaults( this.options, {3348 rerenderOnModelChange: false3349 });3328 library: wp.media.query( _.defaults({ 3329 type: 'audio' 3330 }, options.library ) ) 3331 }), 3350 3332 3351 this.on( 'ready', this.initialFocus ); 3352 // Call 'initialize' directly on the parent class. 3353 Attachment.prototype.initialize.apply( this, arguments ); 3354 }, 3333 // Playlist states. 3334 new wp.media.controller.CollectionEdit({ 3335 type: 'audio', 3336 collectionType: 'playlist', 3337 title: l10n.editPlaylistTitle, 3338 SettingsView: wp.media.view.Settings.Playlist, 3339 library: options.selection, 3340 editing: options.editing, 3341 menu: 'playlist', 3342 dragInfoText: l10n.playlistDragInfo, 3343 dragInfo: false 3344 }), 3355 3345 3356 initialFocus: function() { 3357 if ( ! wp.media.isTouchDevice ) { 3358 /* 3359 Previously focused the first ':input' (the readonly URL text field). 3360 Since the first ':input' is now a button (delete/trash): when pressing 3361 spacebar on an attachment, Firefox fires deleteAttachment/trashAttachment 3362 as soon as focus is moved. Explicitly target the first text field for now. 3363 @todo change initial focus logic, also for accessibility. 3364 */ 3365 this.$( 'input[type="text"]' ).eq( 0 ).focus(); 3366 } 3367 }, 3368 /** 3369 * @param {Object} event 3370 */ 3371 deleteAttachment: function( event ) { 3372 event.preventDefault(); 3346 new wp.media.controller.CollectionAdd({ 3347 type: 'audio', 3348 collectionType: 'playlist', 3349 title: l10n.addToPlaylistTitle 3350 }), 3373 3351 3374 if ( window.confirm( l10n.warnDelete ) ) { 3375 this.model.destroy(); 3376 // Keep focus inside media modal 3377 // after image is deleted 3378 this.controller.modal.focusManager.focus(); 3379 } 3380 }, 3381 /** 3382 * @param {Object} event 3383 */ 3384 trashAttachment: function( event ) { 3385 var library = this.controller.library; 3386 event.preventDefault(); 3352 new Library({ 3353 id: 'video-playlist', 3354 title: l10n.createVideoPlaylistTitle, 3355 priority: 60, 3356 toolbar: 'main-video-playlist', 3357 filterable: 'uploaded', 3358 multiple: 'add', 3359 editable: false, 3387 3360 3388 if ( wp.media.view.settings.mediaTrash && 3389 'edit-metadata' === this.controller.content.mode() ) { 3361 library: wp.media.query( _.defaults({ 3362 type: 'video' 3363 }, options.library ) ) 3364 }), 3390 3365 3391 this.model.set( 'status', 'trash' ); 3392 this.model.save().done( function() { 3393 library._requery( true ); 3394 } ); 3395 } else { 3396 this.model.destroy(); 3397 } 3398 }, 3399 /** 3400 * @param {Object} event 3401 */ 3402 untrashAttachment: function( event ) { 3403 var library = this.controller.library; 3404 event.preventDefault(); 3366 new wp.media.controller.CollectionEdit({ 3367 type: 'video', 3368 collectionType: 'playlist', 3369 title: l10n.editVideoPlaylistTitle, 3370 SettingsView: wp.media.view.Settings.Playlist, 3371 library: options.selection, 3372 editing: options.editing, 3373 menu: 'video-playlist', 3374 dragInfoText: l10n.videoPlaylistDragInfo, 3375 dragInfo: false 3376 }), 3405 3377 3406 this.model.set( 'status', 'inherit' ); 3407 this.model.save().done( function() { 3408 library._requery( true ); 3409 } ); 3410 }, 3411 /** 3412 * @param {Object} event 3413 */ 3414 editAttachment: function( event ) { 3415 var editState = this.controller.states.get( 'edit-image' ); 3416 if ( window.imageEdit && editState ) { 3417 event.preventDefault(); 3378 new wp.media.controller.CollectionAdd({ 3379 type: 'video', 3380 collectionType: 'playlist', 3381 title: l10n.addToVideoPlaylistTitle 3382 }) 3383 ]); 3418 3384 3419 editState.set( 'image', this.model ); 3420 this.controller.setState( 'edit-image' ); 3421 } else { 3422 this.$el.addClass('needs-refresh'); 3385 if ( wp.media.view.settings.post.featuredImageId ) { 3386 this.states.add( new wp.media.controller.FeaturedImage() ); 3423 3387 } 3424 3388 }, 3425 /**3426 * When reverse tabbing(shift+tab) out of the right details panel, deliver3427 * the focus to the item in the list that was being edited.3428 *3429 * @param {Object} event3430 */3431 toggleSelectionHandler: function( event ) {3432 if ( 'keydown' === event.type && 9 === event.keyCode && event.shiftKey && event.target === this.$( ':tabbable' ).get( 0 ) ) {3433 this.controller.trigger( 'attachment:details:shift-tab', event );3434 return false;3435 }3436 3389 3437 if ( 37 === event.keyCode || 38 === event.keyCode || 39 === event.keyCode || 40 === event.keyCode ) { 3438 this.controller.trigger( 'attachment:keydown:arrow', event ); 3439 return; 3440 } 3441 } 3442 }); 3390 bindHandlers: function() { 3391 var handlers, checkCounts; 3443 3392 3444 module.exports = Details;3393 Select.prototype.bindHandlers.apply( this, arguments ); 3445 3394 3446 },{}],27:[function(require,module,exports){ 3447 /** 3448 * wp.media.view.Attachment.EditLibrary 3449 * 3450 * @memberOf wp.media.view.Attachment 3451 * 3452 * @class 3453 * @augments wp.media.view.Attachment 3454 * @augments wp.media.View 3455 * @augments wp.Backbone.View 3456 * @augments Backbone.View 3457 */ 3458 var EditLibrary = wp.media.view.Attachment.extend(/** @lends wp.media.view.Attachment.EditLibrary.prototype */{ 3459 buttons: { 3460 close: true 3461 } 3462 }); 3395 this.on( 'activate', this.activate, this ); 3463 3396 3464 module.exports = EditLibrary; 3397 // Only bother checking media type counts if one of the counts is zero 3398 checkCounts = _.find( this.counts, function( type ) { 3399 return type.count === 0; 3400 } ); 3465 3401 3466 },{}],28:[function(require,module,exports){ 3467 /** 3468 * wp.media.view.Attachment.EditSelection 3469 * 3470 * @memberOf wp.media.view.Attachment 3471 * 3472 * @class 3473 * @augments wp.media.view.Attachment.Selection 3474 * @augments wp.media.view.Attachment 3475 * @augments wp.media.View 3476 * @augments wp.Backbone.View 3477 * @augments Backbone.View 3478 */ 3479 var EditSelection = wp.media.view.Attachment.Selection.extend(/** @lends wp.media.view.Attachment.EditSelection.prototype */{ 3480 buttons: { 3481 close: true 3482 } 3483 }); 3402 if ( typeof checkCounts !== 'undefined' ) { 3403 this.listenTo( wp.media.model.Attachments.all, 'change:type', this.mediaTypeCounts ); 3404 } 3484 3405 3485 module.exports = EditSelection; 3406 this.on( 'menu:create:gallery', this.createMenu, this ); 3407 this.on( 'menu:create:playlist', this.createMenu, this ); 3408 this.on( 'menu:create:video-playlist', this.createMenu, this ); 3409 this.on( 'toolbar:create:main-insert', this.createToolbar, this ); 3410 this.on( 'toolbar:create:main-gallery', this.createToolbar, this ); 3411 this.on( 'toolbar:create:main-playlist', this.createToolbar, this ); 3412 this.on( 'toolbar:create:main-video-playlist', this.createToolbar, this ); 3413 this.on( 'toolbar:create:featured-image', this.featuredImageToolbar, this ); 3414 this.on( 'toolbar:create:main-embed', this.mainEmbedToolbar, this ); 3486 3415 3487 },{}],29:[function(require,module,exports){ 3488 /** 3489 * wp.media.view.Attachment.Library 3490 * 3491 * @memberOf wp.media.view.Attachment 3492 * 3493 * @class 3494 * @augments wp.media.view.Attachment 3495 * @augments wp.media.View 3496 * @augments wp.Backbone.View 3497 * @augments Backbone.View 3498 */ 3499 var Library = wp.media.view.Attachment.extend(/** @lends wp.media.view.Attachment.Library.prototype */{ 3500 buttons: { 3501 check: true 3502 } 3503 }); 3416 handlers = { 3417 menu: { 3418 'default': 'mainMenu', 3419 'gallery': 'galleryMenu', 3420 'playlist': 'playlistMenu', 3421 'video-playlist': 'videoPlaylistMenu' 3422 }, 3504 3423 3505 module.exports = Library; 3424 content: { 3425 'embed': 'embedContent', 3426 'edit-image': 'editImageContent', 3427 'edit-selection': 'editSelectionContent' 3428 }, 3506 3429 3507 },{}],30:[function(require,module,exports){ 3508 /** 3509 * wp.media.view.Attachment.Selection 3510 * 3511 * @memberOf wp.media.view.Attachment 3512 * 3513 * @class 3514 * @augments wp.media.view.Attachment 3515 * @augments wp.media.View 3516 * @augments wp.Backbone.View 3517 * @augments Backbone.View 3518 */ 3519 var Selection = wp.media.view.Attachment.extend(/** @lends wp.media.view.Attachment.Selection.prototype */{ 3520 className: 'attachment selection', 3430 toolbar: { 3431 'main-insert': 'mainInsertToolbar', 3432 'main-gallery': 'mainGalleryToolbar', 3433 'gallery-edit': 'galleryEditToolbar', 3434 'gallery-add': 'galleryAddToolbar', 3435 'main-playlist': 'mainPlaylistToolbar', 3436 'playlist-edit': 'playlistEditToolbar', 3437 'playlist-add': 'playlistAddToolbar', 3438 'main-video-playlist': 'mainVideoPlaylistToolbar', 3439 'video-playlist-edit': 'videoPlaylistEditToolbar', 3440 'video-playlist-add': 'videoPlaylistAddToolbar' 3441 } 3442 }; 3521 3443 3522 // On click, just select the model, instead of removing the model from3523 // the selection.3524 toggleSelection: function() {3525 this.options.selection.single( this.model);3526 }3527 }); 3444 _.each( handlers, function( regionHandlers, region ) { 3445 _.each( regionHandlers, function( callback, handler ) { 3446 this.on( region + ':render:' + handler, this[ callback ], this ); 3447 }, this ); 3448 }, this ); 3449 }, 3528 3450 3529 module.exports = Selection; 3451 activate: function() { 3452 // Hide menu items for states tied to particular media types if there are no items 3453 _.each( this.counts, function( type ) { 3454 if ( type.count < 1 ) { 3455 this.menuItemVisibility( type.state, 'hide' ); 3456 } 3457 }, this ); 3458 }, 3530 3459 3531 },{}],31:[function(require,module,exports){ 3532 var View = wp.media.View, 3533 $ = jQuery, 3534 Attachments; 3460 mediaTypeCounts: function( model, attr ) { 3461 if ( typeof this.counts[ attr ] !== 'undefined' && this.counts[ attr ].count < 1 ) { 3462 this.counts[ attr ].count++; 3463 this.menuItemVisibility( this.counts[ attr ].state, 'show' ); 3464 } 3465 }, 3535 3466 3536 /** 3537 * wp.media.view.Attachments 3538 * 3539 * @memberOf wp.media.view 3540 * 3541 * @class 3542 * @augments wp.media.View 3543 * @augments wp.Backbone.View 3544 * @augments Backbone.View 3545 */ 3546 Attachments = View.extend(/** @lends wp.media.view.Attachments.prototype */{ 3547 tagName: 'ul', 3548 className: 'attachments', 3467 // Menus 3468 /** 3469 * @param {wp.Backbone.View} view 3470 */ 3471 mainMenu: function( view ) { 3472 view.set({ 3473 'library-separator': new wp.media.View({ 3474 className: 'separator', 3475 priority: 100 3476 }) 3477 }); 3478 }, 3549 3479 3550 attributes: { 3551 tabIndex: -1 3480 menuItemVisibility: function( state, visibility ) { 3481 var menu = this.menu.get(); 3482 if ( visibility === 'hide' ) { 3483 menu.hide( state ); 3484 } else if ( visibility === 'show' ) { 3485 menu.show( state ); 3486 } 3552 3487 }, 3488 /** 3489 * @param {wp.Backbone.View} view 3490 */ 3491 galleryMenu: function( view ) { 3492 var lastState = this.lastState(), 3493 previous = lastState && lastState.id, 3494 frame = this; 3553 3495 3554 initialize: function() { 3555 this.el.id = _.uniqueId('__attachments-view-'); 3496 view.set({ 3497 cancel: { 3498 text: l10n.cancelGalleryTitle, 3499 priority: 20, 3500 click: function() { 3501 if ( previous ) { 3502 frame.setState( previous ); 3503 } else { 3504 frame.close(); 3505 } 3556 3506 3557 _.defaults( this.options, { 3558 refreshSensitivity: wp.media.isTouchDevice ? 300 : 200, 3559 refreshThreshold: 3, 3560 AttachmentView: wp.media.view.Attachment, 3561 sortable: false, 3562 resize: true, 3563 idealColumnWidth: $( window ).width() < 640 ? 135 : 150 3507 // Keep focus inside media modal 3508 // after canceling a gallery 3509 this.controller.modal.focusManager.focus(); 3510 } 3511 }, 3512 separateCancel: new wp.media.View({ 3513 className: 'separator', 3514 priority: 40 3515 }) 3564 3516 }); 3517 }, 3565 3518 3566 this._viewsByCid = {}; 3567 this.$window = $( window ); 3568 this.resizeEvent = 'resize.media-modal-columns'; 3519 playlistMenu: function( view ) { 3520 var lastState = this.lastState(), 3521 previous = lastState && lastState.id, 3522 frame = this; 3569 3523 3570 this.collection.on( 'add', function( attachment ) { 3571 this.views.add( this.createAttachmentView( attachment ), { 3572 at: this.collection.indexOf( attachment ) 3573 }); 3574 }, this ); 3524 view.set({ 3525 cancel: { 3526 text: l10n.cancelPlaylistTitle, 3527 priority: 20, 3528 click: function() { 3529 if ( previous ) { 3530 frame.setState( previous ); 3531 } else { 3532 frame.close(); 3533 } 3534 } 3535 }, 3536 separateCancel: new wp.media.View({ 3537 className: 'separator', 3538 priority: 40 3539 }) 3540 }); 3541 }, 3575 3542 3576 this.collection.on( 'remove', function( attachment ) { 3577 var view = this._viewsByCid[ attachment.cid ]; 3578 delete this._viewsByCid[ attachment.cid ]; 3543 videoPlaylistMenu: function( view ) { 3544 var lastState = this.lastState(), 3545 previous = lastState && lastState.id, 3546 frame = this; 3579 3547 3580 if ( view ) { 3581 view.remove(); 3582 } 3583 }, this ); 3548 view.set({ 3549 cancel: { 3550 text: l10n.cancelVideoPlaylistTitle, 3551 priority: 20, 3552 click: function() { 3553 if ( previous ) { 3554 frame.setState( previous ); 3555 } else { 3556 frame.close(); 3557 } 3558 } 3559 }, 3560 separateCancel: new wp.media.View({ 3561 className: 'separator', 3562 priority: 40 3563 }) 3564 }); 3565 }, 3584 3566 3585 this.collection.on( 'reset', this.render, this ); 3567 // Content 3568 embedContent: function() { 3569 var view = new wp.media.view.Embed({ 3570 controller: this, 3571 model: this.state() 3572 }).render(); 3586 3573 3587 this. listenTo( this.controller, 'library:selection:add', this.attachmentFocus);3574 this.content.set( view ); 3588 3575 3589 // Throttle the scroll handler and bind this. 3590 this.scroll = _.chain( this.scroll ).bind( this ).throttle( this.options.refreshSensitivity ).value(); 3576 if ( ! wp.media.isTouchDevice ) { 3577 view.url.focus(); 3578 } 3579 }, 3591 3580 3592 this.options.scrollElement = this.options.scrollElement || this.el; 3593 $( this.options.scrollElement ).on( 'scroll', this.scroll ); 3581 editSelectionContent: function() { 3582 var state = this.state(), 3583 selection = state.get('selection'), 3584 view; 3594 3585 3595 this.initSortable(); 3586 view = new wp.media.view.AttachmentsBrowser({ 3587 controller: this, 3588 collection: selection, 3589 selection: selection, 3590 model: state, 3591 sortable: true, 3592 search: false, 3593 date: false, 3594 dragInfo: true, 3595 3596 AttachmentView: wp.media.view.Attachments.EditSelection 3597 }).render(); 3596 3598 3597 _.bindAll( this, 'setColumns' ); 3599 view.toolbar.set( 'backToLibrary', { 3600 text: l10n.returnToLibrary, 3601 priority: -100, 3598 3602 3599 if ( this.options.resize ) { 3600 this.on( 'ready', this.bindEvents ); 3601 this.controller.on( 'open', this.setColumns ); 3603 click: function() { 3604 this.controller.content.mode('browse'); 3605 } 3606 }); 3602 3607 3603 // Call this.setColumns() after this view has been rendered in the DOM so 3604 // attachments get proper width applied. 3605 _.defer( this.setColumns, this ); 3606 } 3607 }, 3608 // Browse our library of attachments. 3609 this.content.set( view ); 3608 3610 3609 bindEvents: function() {3610 this. $window.off( this.resizeEvent ).on( this.resizeEvent, _.debounce( this.setColumns, 50 ));3611 // Trigger the controller to set focus 3612 this.trigger( 'edit:selection', this ); 3611 3613 }, 3612 3614 3613 attachmentFocus: function() {3614 this.$( 'li:first' ).focus();3615 },3615 editImageContent: function() { 3616 var image = this.state().get('image'), 3617 view = new wp.media.view.EditImage( { model: image, controller: this } ).render(); 3616 3618 3617 restoreFocus: function() { 3618 this.$( 'li.selected:first' ).focus(); 3619 }, 3619 this.content.set( view ); 3620 3620 3621 arrowEvent: function( event ) { 3622 var attachments = this.$el.children( 'li' ), 3623 perRow = this.columns, 3624 index = attachments.filter( ':focus' ).index(), 3625 row = ( index + 1 ) <= perRow ? 1 : Math.ceil( ( index + 1 ) / perRow ); 3621 // after creating the wrapper view, load the actual editor via an ajax call 3622 view.loadEditor(); 3626 3623 3627 if ( index === -1 ) { 3628 return; 3629 } 3624 }, 3630 3625 3631 // Left arrow 3632 if ( 37 === event.keyCode ) { 3633 if ( 0 === index ) { 3634 return; 3635 } 3636 attachments.eq( index - 1 ).focus(); 3637 } 3626 // Toolbars 3638 3627 3639 // Up arrow 3640 if ( 38 === event.keyCode ) { 3641 if ( 1 === row ) { 3642 return; 3643 } 3644 attachments.eq( index - perRow ).focus(); 3645 } 3628 /** 3629 * @param {wp.Backbone.View} view 3630 */ 3631 selectionStatusToolbar: function( view ) { 3632 var editable = this.state().get('editable'); 3646 3633 3647 // Right arrow 3648 if ( 39 === event.keyCode ) { 3649 if ( attachments.length === index ) { 3650 return; 3651 } 3652 attachments.eq( index + 1 ).focus(); 3653 } 3634 view.set( 'selection', new wp.media.view.Selection({ 3635 controller: this, 3636 collection: this.state().get('selection'), 3637 priority: -40, 3654 3638 3655 // Down arrow3656 if ( 40 === event.keyCode ) {3657 if ( Math.ceil( attachments.length / perRow ) === row) {3658 return;3639 // If the selection is editable, pass the callback to 3640 // switch the content mode. 3641 editable: editable && function() { 3642 this.controller.content.mode('edit-selection'); 3659 3643 } 3660 attachments.eq( index + perRow ).focus(); 3661 } 3644 }).render() ); 3662 3645 }, 3663 3646 3664 dispose: function() {3665 this.collection.props.off( null, null, this );3666 if ( this.options.resize ) {3667 this.$window.off( this.resizeEvent );3668 }3647 /** 3648 * @param {wp.Backbone.View} view 3649 */ 3650 mainInsertToolbar: function( view ) { 3651 var controller = this; 3669 3652 3670 /** 3671 * call 'dispose' directly on the parent class 3672 */ 3673 View.prototype.dispose.apply( this, arguments ); 3674 }, 3653 this.selectionStatusToolbar( view ); 3675 3654 3676 setColumns: function() { 3677 var prev = this.columns, 3678 width = this.$el.width(); 3655 view.set( 'insert', { 3656 style: 'primary', 3657 priority: 80, 3658 text: l10n.insertIntoPost, 3659 requires: { selection: true }, 3679 3660 3680 if ( width ) { 3681 this.columns = Math.min( Math.round( width / this.options.idealColumnWidth ), 12 ) || 1; 3661 /** 3662 * @callback 3663 * @fires wp.media.controller.State#insert 3664 */ 3665 click: function() { 3666 var state = controller.state(), 3667 selection = state.get('selection'); 3682 3668 3683 if ( ! prev || prev !== this.columns ) {3684 this.$el.closest( '.media-frame-content' ).attr( 'data-columns', this.columns);3669 controller.close(); 3670 state.trigger( 'insert', selection ).reset(); 3685 3671 } 3686 } 3672 }); 3687 3673 }, 3688 3674 3689 initSortable: function() { 3690 var collection = this.collection; 3675 /** 3676 * @param {wp.Backbone.View} view 3677 */ 3678 mainGalleryToolbar: function( view ) { 3679 var controller = this; 3691 3680 3692 if ( ! this.options.sortable || ! $.fn.sortable ) { 3693 return; 3694 } 3681 this.selectionStatusToolbar( view ); 3695 3682 3696 this.$el.sortable( _.extend({ 3697 // If the `collection` has a `comparator`, disable sorting. 3698 disabled: !! collection.comparator, 3683 view.set( 'gallery', { 3684 style: 'primary', 3685 text: l10n.createNewGallery, 3686 priority: 60, 3687 requires: { selection: true }, 3699 3688 3700 // Change the position of the attachment as soon as the 3701 // mouse pointer overlaps a thumbnail. 3702 tolerance: 'pointer', 3689 click: function() { 3690 var selection = controller.state().get('selection'), 3691 edit = controller.state('gallery-edit'), 3692 models = selection.where({ type: 'image' }); 3703 3693 3704 // Record the initial `index` of the dragged model.3705 start: function( event, ui ) {3706 ui.item.data('sortableIndexStart', ui.item.index());3707 },3694 edit.set( 'library', new wp.media.model.Selection( models, { 3695 props: selection.props.toJSON(), 3696 multiple: true 3697 }) ); 3708 3698 3709 // Update the model's index in the collection. 3710 // Do so silently, as the view is already accurate. 3711 update: function( event, ui ) { 3712 var model = collection.at( ui.item.data('sortableIndexStart') ), 3713 comparator = collection.comparator; 3699 this.controller.setState('gallery-edit'); 3714 3700 3715 // Temporarily disable the comparator to prevent `add` 3716 // from re-sorting. 3717 delete collection.comparator; 3701 // Keep focus inside media modal 3702 // after jumping to gallery view 3703 this.controller.modal.focusManager.focus(); 3704 } 3705 }); 3706 }, 3718 3707 3719 // Silently shift the model to its new index. 3720 collection.remove( model, { 3721 silent: true 3722 }); 3723 collection.add( model, { 3724 silent: true, 3725 at: ui.item.index() 3726 }); 3708 mainPlaylistToolbar: function( view ) { 3709 var controller = this; 3727 3710 3728 // Restore the comparator. 3729 collection.comparator = comparator; 3711 this.selectionStatusToolbar( view ); 3730 3712 3731 // Fire the `reset` event to ensure other collections sync. 3732 collection.trigger( 'reset', collection ); 3713 view.set( 'playlist', { 3714 style: 'primary', 3715 text: l10n.createNewPlaylist, 3716 priority: 100, 3717 requires: { selection: true }, 3733 3718 3734 // If the collection is sorted by menu order, 3735 // update the menu order. 3736 collection.saveMenuOrder(); 3737 } 3738 }, this.options.sortable ) ); 3719 click: function() { 3720 var selection = controller.state().get('selection'), 3721 edit = controller.state('playlist-edit'), 3722 models = selection.where({ type: 'audio' }); 3739 3723 3740 // If the `orderby` property is changed on the `collection`, 3741 // check to see if we have a `comparator`. If so, disable sorting. 3742 collection.props.on( 'change:orderby', function() { 3743 this.$el.sortable( 'option', 'disabled', !! collection.comparator ); 3744 }, this ); 3724 edit.set( 'library', new wp.media.model.Selection( models, { 3725 props: selection.props.toJSON(), 3726 multiple: true 3727 }) ); 3745 3728 3746 this.collection.props.on( 'change:orderby', this.refreshSortable, this ); 3747 this.refreshSortable(); 3748 }, 3729 this.controller.setState('playlist-edit'); 3749 3730 3750 refreshSortable: function() { 3751 if ( ! this.options.sortable || ! $.fn.sortable ) { 3752 return; 3753 } 3731 // Keep focus inside media modal 3732 // after jumping to playlist view 3733 this.controller.modal.focusManager.focus(); 3734 } 3735 }); 3736 }, 3754 3737 3755 // If the `collection` has a `comparator`, disable sorting. 3756 var collection = this.collection, 3757 orderby = collection.props.get('orderby'), 3758 enabled = 'menuOrder' === orderby || ! collection.comparator; 3738 mainVideoPlaylistToolbar: function( view ) { 3739 var controller = this; 3759 3740 3760 this.$el.sortable( 'option', 'disabled', ! enabled ); 3761 }, 3741 this.selectionStatusToolbar( view ); 3762 3742 3763 /** 3764 * @param {wp.media.model.Attachment} attachment 3765 * @returns {wp.media.View} 3766 */ 3767 createAttachmentView: function( attachment ) { 3768 var view = new this.options.AttachmentView({ 3769 controller: this.controller, 3770 model: attachment, 3771 collection: this.collection, 3772 selection: this.options.selection 3773 }); 3743 view.set( 'video-playlist', { 3744 style: 'primary', 3745 text: l10n.createNewVideoPlaylist, 3746 priority: 100, 3747 requires: { selection: true }, 3774 3748 3775 return this._viewsByCid[ attachment.cid ] = view; 3776 }, 3749 click: function() { 3750 var selection = controller.state().get('selection'), 3751 edit = controller.state('video-playlist-edit'), 3752 models = selection.where({ type: 'video' }); 3777 3753 3778 prepare: function() { 3779 // Create all of the Attachment views, and replace 3780 // the list in a single DOM operation. 3781 if ( this.collection.length ) { 3782 this.views.set( this.collection.map( this.createAttachmentView, this ) ); 3754 edit.set( 'library', new wp.media.model.Selection( models, { 3755 props: selection.props.toJSON(), 3756 multiple: true 3757 }) ); 3783 3758 3784 // If there are no elements, clear the views and load some. 3785 } else { 3786 this.views.unset(); 3787 this.collection.more().done( this.scroll ); 3788 } 3789 }, 3759 this.controller.setState('video-playlist-edit'); 3790 3760 3791 ready: function() { 3792 // Trigger the scroll event to check if we're within the 3793 // threshold to query for additional attachments. 3794 this.scroll(); 3761 // Keep focus inside media modal 3762 // after jumping to video playlist view 3763 this.controller.modal.focusManager.focus(); 3764 } 3765 }); 3795 3766 }, 3796 3767 3797 scroll: function() { 3798 var view = this, 3799 el = this.options.scrollElement, 3800 scrollTop = el.scrollTop, 3801 toolbar; 3768 featuredImageToolbar: function( toolbar ) { 3769 this.createSelectToolbar( toolbar, { 3770 text: l10n.setFeaturedImage, 3771 state: this.options.state 3772 }); 3773 }, 3802 3774 3803 // The scroll event occurs on the document, but the element 3804 // that should be checked is the document body. 3805 if ( el === document ) { 3806 el = document.body; 3807 scrollTop = $(document).scrollTop(); 3808 } 3775 mainEmbedToolbar: function( toolbar ) { 3776 toolbar.view = new wp.media.view.Toolbar.Embed({ 3777 controller: this 3778 }); 3779 }, 3809 3780 3810 if ( ! $(el).is(':visible') || ! this.collection.hasMore() ) { 3811 return; 3812 } 3781 galleryEditToolbar: function() { 3782 var editing = this.state().get('editing'); 3783 this.toolbar.set( new wp.media.view.Toolbar({ 3784 controller: this, 3785 items: { 3786 insert: { 3787 style: 'primary', 3788 text: editing ? l10n.updateGallery : l10n.insertGallery, 3789 priority: 80, 3790 requires: { library: true }, 3813 3791 3814 toolbar = this.views.parent.toolbar; 3792 /** 3793 * @fires wp.media.controller.State#update 3794 */ 3795 click: function() { 3796 var controller = this.controller, 3797 state = controller.state(); 3815 3798 3816 // Show the spinner only if we are close to the bottom. 3817 if ( el.scrollHeight - ( scrollTop + el.clientHeight ) < el.clientHeight / 3 ) { 3818 toolbar.get('spinner').show(); 3819 } 3799 controller.close(); 3800 state.trigger( 'update', state.get('library') ); 3820 3801 3821 if ( el.scrollHeight < scrollTop + ( el.clientHeight * this.options.refreshThreshold ) ) {3822 this.collection.more().done(function() {3823 view.scroll();3824 toolbar.get('spinner').hide();3825 });3826 }3827 }3828 }); 3802 // Restore and reset the default state. 3803 controller.setState( controller.options.state ); 3804 controller.reset(); 3805 } 3806 } 3807 } 3808 }) ); 3809 }, 3829 3810 3830 module.exports = Attachments; 3811 galleryAddToolbar: function() { 3812 this.toolbar.set( new wp.media.view.Toolbar({ 3813 controller: this, 3814 items: { 3815 insert: { 3816 style: 'primary', 3817 text: l10n.addToGallery, 3818 priority: 80, 3819 requires: { selection: true }, 3831 3820 3832 },{}],32:[function(require,module,exports){ 3833 var View = wp.media.View, 3834 mediaTrash = wp.media.view.settings.mediaTrash, 3835 l10n = wp.media.view.l10n, 3836 $ = jQuery, 3837 AttachmentsBrowser; 3821 /** 3822 * @fires wp.media.controller.State#reset 3823 */ 3824 click: function() { 3825 var controller = this.controller, 3826 state = controller.state(), 3827 edit = controller.state('gallery-edit'); 3838 3828 3839 /** 3840 * wp.media.view.AttachmentsBrowser 3841 * 3842 * @memberOf wp.media.view 3843 * 3844 * @class 3845 * @augments wp.media.View 3846 * @augments wp.Backbone.View 3847 * @augments Backbone.View 3848 * 3849 * @param {object} [options] The options hash passed to the view. 3850 * @param {boolean|string} [options.filters=false] Which filters to show in the browser's toolbar. 3851 * Accepts 'uploaded' and 'all'. 3852 * @param {boolean} [options.search=true] Whether to show the search interface in the 3853 * browser's toolbar. 3854 * @param {boolean} [options.date=true] Whether to show the date filter in the 3855 * browser's toolbar. 3856 * @param {boolean} [options.display=false] Whether to show the attachments display settings 3857 * view in the sidebar. 3858 * @param {boolean|string} [options.sidebar=true] Whether to create a sidebar for the browser. 3859 * Accepts true, false, and 'errors'. 3860 */ 3861 AttachmentsBrowser = View.extend(/** @lends wp.media.view.AttachmentsBrowser.prototype */{ 3862 tagName: 'div', 3863 className: 'attachments-browser', 3829 edit.get('library').add( state.get('selection').models ); 3830 state.trigger('reset'); 3831 controller.setState('gallery-edit'); 3832 } 3833 } 3834 } 3835 }) ); 3836 }, 3864 3837 3865 initialize: function() { 3866 _.defaults( this.options, { 3867 filters: false, 3868 search: true, 3869 date: true, 3870 display: false, 3871 sidebar: true, 3872 AttachmentView: wp.media.view.Attachment.Library 3873 }); 3838 playlistEditToolbar: function() { 3839 var editing = this.state().get('editing'); 3840 this.toolbar.set( new wp.media.view.Toolbar({ 3841 controller: this, 3842 items: { 3843 insert: { 3844 style: 'primary', 3845 text: editing ? l10n.updatePlaylist : l10n.insertPlaylist, 3846 priority: 80, 3847 requires: { library: true }, 3874 3848 3875 this.controller.on( 'toggle:upload:attachment', this.toggleUploader, this ); 3876 this.controller.on( 'edit:selection', this.editSelection ); 3849 /** 3850 * @fires wp.media.controller.State#update 3851 */ 3852 click: function() { 3853 var controller = this.controller, 3854 state = controller.state(); 3877 3855 3878 // In the Media Library, the sidebar is used to display errors before the attachments grid. 3879 if ( this.options.sidebar && 'errors' === this.options.sidebar ) { 3880 this.createSidebar(); 3881 } 3856 controller.close(); 3857 state.trigger( 'update', state.get('library') ); 3882 3858 3883 /* 3884 * For accessibility reasons, place the Inline Uploader before other sections. 3885 * This way, in the Media Library, it's right after the Add New button, see ticket #37188. 3886 */ 3887 this.createUploader(); 3859 // Restore and reset the default state. 3860 controller.setState( controller.options.state ); 3861 controller.reset(); 3862 } 3863 } 3864 } 3865 }) ); 3866 }, 3888 3867 3889 /* 3890 * Create a multi-purpose toolbar. Used as main toolbar in the Media Library 3891 * and also for other things, for example the "Drag and drop to reorder" and 3892 * "Suggested dimensions" info in the media modal. 3893 */ 3894 this.createToolbar(); 3868 playlistAddToolbar: function() { 3869 this.toolbar.set( new wp.media.view.Toolbar({ 3870 controller: this, 3871 items: { 3872 insert: { 3873 style: 'primary', 3874 text: l10n.addToPlaylist, 3875 priority: 80, 3876 requires: { selection: true }, 3895 3877 3896 // Create the list of attachments. 3897 this.createAttachments(); 3878 /** 3879 * @fires wp.media.controller.State#reset 3880 */ 3881 click: function() { 3882 var controller = this.controller, 3883 state = controller.state(), 3884 edit = controller.state('playlist-edit'); 3898 3885 3899 // For accessibility reasons, place the normal sidebar after the attachments, see ticket #36909. 3900 if ( this.options.sidebar && 'errors' !== this.options.sidebar ) { 3901 this.createSidebar(); 3902 } 3886 edit.get('library').add( state.get('selection').models ); 3887 state.trigger('reset'); 3888 controller.setState('playlist-edit'); 3889 } 3890 } 3891 } 3892 }) ); 3893 }, 3903 3894 3904 this.updateContent(); 3895 videoPlaylistEditToolbar: function() { 3896 var editing = this.state().get('editing'); 3897 this.toolbar.set( new wp.media.view.Toolbar({ 3898 controller: this, 3899 items: { 3900 insert: { 3901 style: 'primary', 3902 text: editing ? l10n.updateVideoPlaylist : l10n.insertVideoPlaylist, 3903 priority: 140, 3904 requires: { library: true }, 3905 3905 3906 if ( ! this.options.sidebar || 'errors' === this.options.sidebar ) { 3907 this.$el.addClass( 'hide-sidebar' ); 3906 click: function() { 3907 var controller = this.controller, 3908 state = controller.state(), 3909 library = state.get('library'); 3908 3910 3909 if ( 'errors' === this.options.sidebar ) { 3910 this.$el.addClass( 'sidebar-for-errors' ); 3911 } 3912 } 3911 library.type = 'video'; 3913 3912 3914 this.collection.on( 'add remove reset', this.updateContent, this);3915 },3913 controller.close(); 3914 state.trigger( 'update', library ); 3916 3915 3917 editSelection: function( modal ) { 3918 modal.$( '.media-button-backToLibrary' ).focus(); 3916 // Restore and reset the default state. 3917 controller.setState( controller.options.state ); 3918 controller.reset(); 3919 } 3920 } 3921 } 3922 }) ); 3919 3923 }, 3920 3924 3921 /** 3922 * @returns {wp.media.view.AttachmentsBrowser} Returns itself to allow chaining 3923 */ 3924 dispose: function() { 3925 this.options.selection.off( null, null, this ); 3926 View.prototype.dispose.apply( this, arguments ); 3927 return this; 3928 }, 3925 videoPlaylistAddToolbar: function() { 3926 this.toolbar.set( new wp.media.view.Toolbar({ 3927 controller: this, 3928 items: { 3929 insert: { 3930 style: 'primary', 3931 text: l10n.addToVideoPlaylist, 3932 priority: 140, 3933 requires: { selection: true }, 3929 3934 3930 createToolbar: function() { 3931 var LibraryViewSwitcher, Filters, toolbarOptions; 3935 click: function() { 3936 var controller = this.controller, 3937 state = controller.state(), 3938 edit = controller.state('video-playlist-edit'); 3932 3939 3933 toolbarOptions = { 3934 controller: this.controller 3935 }; 3940 edit.get('library').add( state.get('selection').models ); 3941 state.trigger('reset'); 3942 controller.setState('video-playlist-edit'); 3943 } 3944 } 3945 } 3946 }) ); 3947 } 3948 }); 3936 3949 3937 if ( this.controller.isModeActive( 'grid' ) ) { 3938 toolbarOptions.className = 'media-toolbar wp-filter'; 3939 } 3950 module.exports = Post; 3940 3951 3941 /**3942 * @member {wp.media.view.Toolbar}3943 */3944 this.toolbar = new wp.media.view.Toolbar( toolbarOptions );3945 3952 3946 this.views.add( this.toolbar ); 3953 /***/ }), 3954 /* 50 */ 3955 /***/ (function(module, exports) { 3947 3956 3948 this.toolbar.set( 'spinner', new wp.media.view.Spinner({ 3949 priority: -603950 }) );3957 var Select = wp.media.view.MediaFrame.Select, 3958 l10n = wp.media.view.l10n, 3959 ImageDetails; 3951 3960 3952 if ( -1 !== $.inArray( this.options.filters, [ 'uploaded', 'all' ] ) ) { 3953 // "Filters" will return a <select>, need to render 3954 // screen reader text before 3955 this.toolbar.set( 'filtersLabel', new wp.media.view.Label({ 3956 value: l10n.filterByType, 3957 attributes: { 3958 'for': 'media-attachment-filters' 3959 }, 3960 priority: -80 3961 }).render() ); 3961 /** 3962 * wp.media.view.MediaFrame.ImageDetails 3963 * 3964 * A media frame for manipulating an image that's already been inserted 3965 * into a post. 3966 * 3967 * @memberOf wp.media.view.MediaFrame 3968 * 3969 * @class 3970 * @augments wp.media.view.MediaFrame.Select 3971 * @augments wp.media.view.MediaFrame 3972 * @augments wp.media.view.Frame 3973 * @augments wp.media.View 3974 * @augments wp.Backbone.View 3975 * @augments Backbone.View 3976 * @mixes wp.media.controller.StateMachine 3977 */ 3978 ImageDetails = Select.extend(/** @lends wp.media.view.MediaFrame.ImageDetails.prototype */{ 3979 defaults: { 3980 id: 'image', 3981 url: '', 3982 menu: 'image-details', 3983 content: 'image-details', 3984 toolbar: 'image-details', 3985 type: 'link', 3986 title: l10n.imageDetailsTitle, 3987 priority: 120 3988 }, 3962 3989 3963 if ( 'uploaded' === this.options.filters ) { 3964 this.toolbar.set( 'filters', new wp.media.view.AttachmentFilters.Uploaded({ 3965 controller: this.controller, 3966 model: this.collection.props, 3967 priority: -80 3968 }).render() ); 3969 } else { 3970 Filters = new wp.media.view.AttachmentFilters.All({ 3971 controller: this.controller, 3972 model: this.collection.props, 3973 priority: -80 3974 }); 3990 initialize: function( options ) { 3991 this.image = new wp.media.model.PostImage( options.metadata ); 3992 this.options.selection = new wp.media.model.Selection( this.image.attachment, { multiple: false } ); 3993 Select.prototype.initialize.apply( this, arguments ); 3994 }, 3975 3995 3976 this.toolbar.set( 'filters', Filters.render() ); 3977 } 3978 } 3996 bindHandlers: function() { 3997 Select.prototype.bindHandlers.apply( this, arguments ); 3998 this.on( 'menu:create:image-details', this.createMenu, this ); 3999 this.on( 'content:create:image-details', this.imageDetailsContent, this ); 4000 this.on( 'content:render:edit-image', this.editImageContent, this ); 4001 this.on( 'toolbar:render:image-details', this.renderImageDetailsToolbar, this ); 4002 // override the select toolbar 4003 this.on( 'toolbar:render:replace', this.renderReplaceImageToolbar, this ); 4004 }, 3979 4005 3980 // Feels odd to bring the global media library switcher into the Attachment 3981 // browser view. Is this a use case for doAction( 'add:toolbar-items:attachments-browser', this.toolbar ); 3982 // which the controller can tap into and add this view? 3983 if ( this.controller.isModeActive( 'grid' ) ) { 3984 LibraryViewSwitcher = View.extend({ 3985 className: 'view-switch media-grid-view-switch', 3986 template: wp.template( 'media-library-view-switcher') 3987 }); 4006 createStates: function() { 4007 this.states.add([ 4008 new wp.media.controller.ImageDetails({ 4009 image: this.image, 4010 editable: false 4011 }), 4012 new wp.media.controller.ReplaceImage({ 4013 id: 'replace-image', 4014 library: wp.media.query( { type: 'image' } ), 4015 image: this.image, 4016 multiple: false, 4017 title: l10n.imageReplaceTitle, 4018 toolbar: 'replace', 4019 priority: 80, 4020 displaySettings: true 4021 }), 4022 new wp.media.controller.EditImage( { 4023 image: this.image, 4024 selection: this.options.selection 4025 } ) 4026 ]); 4027 }, 3988 4028 3989 this.toolbar.set( 'libraryViewSwitcher', new LibraryViewSwitcher({ 3990 controller: this.controller, 3991 priority: -90 3992 }).render() ); 4029 imageDetailsContent: function( options ) { 4030 options.view = new wp.media.view.ImageDetails({ 4031 controller: this, 4032 model: this.state().image, 4033 attachment: this.state().image.attachment 4034 }); 4035 }, 3993 4036 3994 // DateFilter is a <select>, screen reader text needs to be rendered before 3995 this.toolbar.set( 'dateFilterLabel', new wp.media.view.Label({ 3996 value: l10n.filterByDate, 3997 attributes: { 3998 'for': 'media-attachment-date-filters' 3999 }, 4000 priority: -75 4001 }).render() ); 4002 this.toolbar.set( 'dateFilter', new wp.media.view.DateFilter({ 4003 controller: this.controller, 4004 model: this.collection.props, 4005 priority: -75 4006 }).render() ); 4037 editImageContent: function() { 4038 var state = this.state(), 4039 model = state.get('image'), 4040 view; 4007 4041 4008 // BulkSelection is a <div> with subviews, including screen reader text 4009 this.toolbar.set( 'selectModeToggleButton', new wp.media.view.SelectModeToggleButton({ 4010 text: l10n.bulkSelect, 4011 controller: this.controller, 4012 priority: -70 4013 }).render() ); 4042 if ( ! model ) { 4043 return; 4044 } 4014 4045 4015 this.toolbar.set( 'deleteSelectedButton', new wp.media.view.DeleteSelectedButton({ 4016 filters: Filters, 4017 style: 'primary', 4018 disabled: true, 4019 text: mediaTrash ? l10n.trashSelected : l10n.deleteSelected, 4020 controller: this.controller, 4021 priority: -60, 4022 click: function() { 4023 var changed = [], removed = [], 4024 selection = this.controller.state().get( 'selection' ), 4025 library = this.controller.state().get( 'library' ); 4046 view = new wp.media.view.EditImage( { model: model, controller: this } ).render(); 4026 4047 4027 if ( ! selection.length ) { 4028 return; 4029 } 4048 this.content.set( view ); 4030 4049 4031 if ( ! mediaTrash && ! window.confirm( l10n.warnBulkDelete ) ) { 4032 return; 4033 } 4050 // after bringing in the frame, load the actual editor via an ajax call 4051 view.loadEditor(); 4034 4052 4035 if ( mediaTrash && 4036 'trash' !== selection.at( 0 ).get( 'status' ) && 4037 ! window.confirm( l10n.warnBulkTrash ) ) { 4053 }, 4038 4054 4039 return; 4040 } 4055 renderImageDetailsToolbar: function() { 4056 this.toolbar.set( new wp.media.view.Toolbar({ 4057 controller: this, 4058 items: { 4059 select: { 4060 style: 'primary', 4061 text: l10n.update, 4062 priority: 80, 4041 4063 4042 selection.each( function( model ) { 4043 if ( ! model.get( 'nonces' )['delete'] ) { 4044 removed.push( model ); 4045 return; 4046 } 4064 click: function() { 4065 var controller = this.controller, 4066 state = controller.state(); 4047 4067 4048 if ( mediaTrash && 'trash' === model.get( 'status' ) ) { 4049 model.set( 'status', 'inherit' ); 4050 changed.push( model.save() ); 4051 removed.push( model ); 4052 } else if ( mediaTrash ) { 4053 model.set( 'status', 'trash' ); 4054 changed.push( model.save() ); 4055 removed.push( model ); 4056 } else { 4057 model.destroy({wait: true}); 4058 } 4059 } ); 4068 controller.close(); 4060 4069 4061 if ( changed.length ) { 4062 selection.remove( removed ); 4070 // not sure if we want to use wp.media.string.image which will create a shortcode or 4071 // perhaps wp.html.string to at least to build the <img /> 4072 state.trigger( 'update', controller.image.toJSON() ); 4063 4073 4064 $.when.apply( null, changed ).then( _.bind( function() { 4065 library._requery( true ); 4066 this.controller.trigger( 'selection:action:done' ); 4067 }, this ) ); 4068 } else { 4069 this.controller.trigger( 'selection:action:done' ); 4074 // Restore and reset the default state. 4075 controller.setState( controller.options.state ); 4076 controller.reset(); 4070 4077 } 4071 4078 } 4072 }).render() ); 4079 } 4080 }) ); 4081 }, 4073 4082 4074 if ( mediaTrash ) { 4075 this.toolbar.set( 'deleteSelectedPermanentlyButton', new wp.media.view.DeleteSelectedPermanentlyButton({ 4076 filters: Filters, 4077 style: 'primary', 4078 disabled: true, 4079 text: l10n.deleteSelected, 4080 controller: this.controller, 4081 priority: -55, 4082 click: function() { 4083 var removed = [], 4084 destroy = [], 4085 selection = this.controller.state().get( 'selection' ); 4083 renderReplaceImageToolbar: function() { 4084 var frame = this, 4085 lastState = frame.lastState(), 4086 previous = lastState && lastState.id; 4086 4087 4087 if ( ! selection.length || ! window.confirm( l10n.warnBulkDelete ) ) { 4088 return; 4088 this.toolbar.set( new wp.media.view.Toolbar({ 4089 controller: this, 4090 items: { 4091 back: { 4092 text: l10n.back, 4093 priority: 20, 4094 click: function() { 4095 if ( previous ) { 4096 frame.setState( previous ); 4097 } else { 4098 frame.close(); 4089 4099 } 4100 } 4101 }, 4090 4102 4091 selection.each( function( model ){4092 if ( ! model.get( 'nonces' )['delete'] ) {4093 removed.push( model );4094 return;4095 }4103 replace: { 4104 style: 'primary', 4105 text: l10n.replace, 4106 priority: 80, 4107 requires: { selection: true }, 4096 4108 4097 destroy.push( model ); 4098 } ); 4109 click: function() { 4110 var controller = this.controller, 4111 state = controller.state(), 4112 selection = state.get( 'selection' ), 4113 attachment = selection.single(); 4099 4114 4100 if ( removed.length ) { 4101 selection.remove( removed ); 4102 } 4115 controller.close(); 4103 4116 4104 if ( destroy.length ) { 4105 $.when.apply( null, destroy.map( function (item) { 4106 return item.destroy(); 4107 } ) ).then( _.bind( function() { 4108 this.controller.trigger( 'selection:action:done' ); 4109 }, this ) ); 4110 } 4117 controller.image.changeAttachment( attachment, state.display( attachment ) ); 4118 4119 // not sure if we want to use wp.media.string.image which will create a shortcode or 4120 // perhaps wp.html.string to at least to build the <img /> 4121 state.trigger( 'replace', controller.image.toJSON() ); 4122 4123 // Restore and reset the default state. 4124 controller.setState( controller.options.state ); 4125 controller.reset(); 4111 4126 } 4112 } ).render() );4127 } 4113 4128 } 4129 }) ); 4130 } 4114 4131 4115 } else if ( this.options.date ) { 4116 // DateFilter is a <select>, screen reader text needs to be rendered before 4117 this.toolbar.set( 'dateFilterLabel', new wp.media.view.Label({ 4118 value: l10n.filterByDate, 4119 attributes: { 4120 'for': 'media-attachment-date-filters' 4121 }, 4122 priority: -75 4123 }).render() ); 4124 this.toolbar.set( 'dateFilter', new wp.media.view.DateFilter({ 4125 controller: this.controller, 4126 model: this.collection.props, 4127 priority: -75 4128 }).render() ); 4129 } 4132 }); 4130 4133 4131 if ( this.options.search ) { 4132 // Search is an input, screen reader text needs to be rendered before 4133 this.toolbar.set( 'searchLabel', new wp.media.view.Label({ 4134 value: l10n.searchMediaLabel, 4135 attributes: { 4136 'for': 'media-search-input' 4137 }, 4138 priority: 60 4139 }).render() ); 4140 this.toolbar.set( 'search', new wp.media.view.Search({ 4141 controller: this.controller, 4142 model: this.collection.props, 4143 priority: 60 4144 }).render() ); 4145 } 4134 module.exports = ImageDetails; 4146 4135 4147 if ( this.options.dragInfo ) {4148 this.toolbar.set( 'dragInfo', new View({4149 el: $( '<div class="instructions">' + l10n.dragInfo + '</div>' )[0],4150 priority: -404151 }) );4152 }4153 4136 4154 if ( this.options.suggestedWidth && this.options.suggestedHeight ) { 4155 this.toolbar.set( 'suggestedDimensions', new View({ 4156 el: $( '<div class="instructions">' + l10n.suggestedDimensions.replace( '%1$s', this.options.suggestedWidth ).replace( '%2$s', this.options.suggestedHeight ) + '</div>' )[0], 4157 priority: -40 4158 }) ); 4159 } 4160 }, 4137 /***/ }), 4138 /* 51 */ 4139 /***/ (function(module, exports) { 4161 4140 4162 updateContent: function() { 4163 var view = this, 4164 noItemsView; 4141 var $ = jQuery, 4142 Modal; 4165 4143 4166 if ( this.controller.isModeActive( 'grid' ) ) { 4167 noItemsView = view.attachmentsNoResults; 4168 } else { 4169 noItemsView = view.uploader; 4170 } 4144 /** 4145 * wp.media.view.Modal 4146 * 4147 * A modal view, which the media modal uses as its default container. 4148 * 4149 * @memberOf wp.media.view 4150 * 4151 * @class 4152 * @augments wp.media.View 4153 * @augments wp.Backbone.View 4154 * @augments Backbone.View 4155 */ 4156 Modal = wp.media.View.extend(/** @lends wp.media.view.Modal.prototype */{ 4157 tagName: 'div', 4158 template: wp.template('media-modal'), 4171 4159 4172 if ( ! this.collection.length ) { 4173 this.toolbar.get( 'spinner' ).show(); 4174 this.dfd = this.collection.more().done( function() { 4175 if ( ! view.collection.length ) { 4176 noItemsView.$el.removeClass( 'hidden' ); 4177 } else { 4178 noItemsView.$el.addClass( 'hidden' ); 4179 } 4180 view.toolbar.get( 'spinner' ).hide(); 4181 } ); 4182 } else { 4183 noItemsView.$el.addClass( 'hidden' ); 4184 view.toolbar.get( 'spinner' ).hide(); 4185 } 4160 attributes: { 4161 tabindex: 0 4186 4162 }, 4187 4163 4188 createUploader: function() { 4189 this.uploader = new wp.media.view.UploaderInline({ 4190 controller: this.controller, 4191 status: false, 4192 message: this.controller.isModeActive( 'grid' ) ? '' : l10n.noItemsFound, 4193 canClose: this.controller.isModeActive( 'grid' ) 4164 events: { 4165 'click .media-modal-backdrop, .media-modal-close': 'escapeHandler', 4166 'keydown': 'keydown' 4167 }, 4168 4169 clickedOpenerEl: null, 4170 4171 initialize: function() { 4172 _.defaults( this.options, { 4173 container: document.body, 4174 title: '', 4175 propagate: true, 4176 freeze: true 4194 4177 }); 4195 4178 4196 this.uploader.$el.addClass( 'hidden' ); 4197 this.views.add( this.uploader ); 4179 this.focusManager = new wp.media.view.FocusManager({ 4180 el: this.el 4181 }); 4182 }, 4183 /** 4184 * @returns {Object} 4185 */ 4186 prepare: function() { 4187 return { 4188 title: this.options.title 4189 }; 4198 4190 }, 4199 4191 4200 toggleUploader: function() { 4201 if ( this.uploader.$el.hasClass( 'hidden' ) ) { 4202 this.uploader.show(); 4203 } else { 4204 this.uploader.hide(); 4192 /** 4193 * @returns {wp.media.view.Modal} Returns itself to allow chaining 4194 */ 4195 attach: function() { 4196 if ( this.views.attached ) { 4197 return this; 4205 4198 } 4206 },4207 4199 4208 createAttachments: function() { 4209 this.attachments = new wp.media.view.Attachments({ 4210 controller: this.controller, 4211 collection: this.collection, 4212 selection: this.options.selection, 4213 model: this.model, 4214 sortable: this.options.sortable, 4215 scrollElement: this.options.scrollElement, 4216 idealColumnWidth: this.options.idealColumnWidth, 4200 if ( ! this.views.rendered ) { 4201 this.render(); 4202 } 4217 4203 4218 // The single `Attachment` view to be used in the `Attachments` view. 4219 AttachmentView: this.options.AttachmentView 4220 }); 4204 this.$el.appendTo( this.options.container ); 4221 4205 4222 // Add keydown listener to the instance of the Attachments view4223 this. controller.on( 'attachment:keydown:arrow', _.bind( this.attachments.arrowEvent, this.attachments ) );4224 this. controller.on( 'attachment:details:shift-tab', _.bind( this.attachments.restoreFocus, this.attachments ));4206 // Manually mark the view as attached and trigger ready. 4207 this.views.attached = true; 4208 this.views.ready(); 4225 4209 4226 this.views.add( this.attachments ); 4210 return this.propagate('attach'); 4211 }, 4212 4213 /** 4214 * @returns {wp.media.view.Modal} Returns itself to allow chaining 4215 */ 4216 detach: function() { 4217 if ( this.$el.is(':visible') ) { 4218 this.close(); 4219 } 4220 4221 this.$el.detach(); 4222 this.views.attached = false; 4223 return this.propagate('detach'); 4224 }, 4225 4226 /** 4227 * @returns {wp.media.view.Modal} Returns itself to allow chaining 4228 */ 4229 open: function() { 4230 var $el = this.$el, 4231 options = this.options, 4232 mceEditor; 4227 4233 4234 if ( $el.is(':visible') ) { 4235 return this; 4236 } 4228 4237 4229 if ( this.controller.isModeActive( 'grid' ) ) { 4230 this.attachmentsNoResults = new View({ 4231 controller: this.controller, 4232 tagName: 'p' 4233 }); 4238 this.clickedOpenerEl = document.activeElement; 4234 4239 4235 this.attachmentsNoResults.$el.addClass( 'hidden no-media' ); 4236 this.attachmentsNoResults.$el.html( l10n.noMedia ); 4240 if ( ! this.views.attached ) { 4241 this.attach(); 4242 } 4237 4243 4238 this.views.add( this.attachmentsNoResults ); 4244 // If the `freeze` option is set, record the window's scroll position. 4245 if ( options.freeze ) { 4246 this._freeze = { 4247 scrollTop: $( window ).scrollTop() 4248 }; 4239 4249 } 4240 },4241 4250 4242 createSidebar: function() { 4243 var options = this.options, 4244 selection = options.selection, 4245 sidebar = this.sidebar = new wp.media.view.Sidebar({ 4246 controller: this.controller 4247 }); 4251 // Disable page scrolling. 4252 $( 'body' ).addClass( 'modal-open' ); 4248 4253 4249 this.views.add( sidebar);4254 $el.show(); 4250 4255 4251 if ( this.controller.uploader ) { 4252 sidebar.set( 'uploads', new wp.media.view.UploaderStatus({ 4253 controller: this.controller, 4254 priority: 40 4255 }) ); 4256 // Try to close the onscreen keyboard 4257 if ( 'ontouchend' in document ) { 4258 if ( ( mceEditor = window.tinymce && window.tinymce.activeEditor ) && ! mceEditor.isHidden() && mceEditor.iframeElement ) { 4259 mceEditor.iframeElement.focus(); 4260 mceEditor.iframeElement.blur(); 4261 4262 setTimeout( function() { 4263 mceEditor.iframeElement.blur(); 4264 }, 100 ); 4265 } 4256 4266 } 4257 4267 4258 selection.on( 'selection:single', this.createSingle, this ); 4259 selection.on( 'selection:unsingle', this.disposeSingle, this ); 4268 this.$el.focus(); 4260 4269 4261 if ( selection.single() ) { 4262 this.createSingle(); 4263 } 4270 return this.propagate('open'); 4264 4271 }, 4265 4272 4266 createSingle: function() { 4267 var sidebar = this.sidebar, 4268 single = this.options.selection.single(); 4273 /** 4274 * @param {Object} options 4275 * @returns {wp.media.view.Modal} Returns itself to allow chaining 4276 */ 4277 close: function( options ) { 4278 var freeze = this._freeze; 4269 4279 4270 sidebar.set( 'details', new wp.media.view.Attachment.Details({ 4271 controller: this.controller, 4272 model: single, 4273 priority: 80 4274 }) ); 4280 if ( ! this.views.attached || ! this.$el.is(':visible') ) { 4281 return this; 4282 } 4275 4283 4276 sidebar.set( 'compat', new wp.media.view.AttachmentCompat({ 4277 controller: this.controller, 4278 model: single, 4279 priority: 120 4280 }) ); 4284 // Enable page scrolling. 4285 $( 'body' ).removeClass( 'modal-open' ); 4281 4286 4282 if ( this.options.display ) {4283 sidebar.set( 'display', new wp.media.view.Settings.AttachmentDisplay({4284 controller: this.controller, 4285 model: this.model.display( single ),4286 attachment: single,4287 priority: 160,4288 userSettings: this.model.get('displayUserSettings')4289 }));4287 // Hide modal and remove restricted media modal tab focus once it's closed 4288 this.$el.hide().undelegate( 'keydown' ); 4289 4290 // Put focus back in useful location once modal is closed. 4291 if ( null !== this.clickedOpenerEl ) { 4292 this.clickedOpenerEl.focus(); 4293 } else { 4294 $( '#wpbody-content' ).focus(); 4290 4295 } 4291 4296 4292 // Show the sidebar on mobile 4293 if ( this.model.id === 'insert' ) { 4294 sidebar.$el.addClass( 'visible' ); 4297 this.propagate('close'); 4298 4299 // If the `freeze` option is set, restore the container's scroll position. 4300 if ( freeze ) { 4301 $( window ).scrollTop( freeze.scrollTop ); 4295 4302 } 4296 },4297 4303 4298 disposeSingle: function() { 4299 var sidebar = this.sidebar; 4300 sidebar.unset('details'); 4301 sidebar.unset('compat'); 4302 sidebar.unset('display'); 4303 // Hide the sidebar on mobile 4304 sidebar.$el.removeClass( 'visible' ); 4305 } 4306 }); 4304 if ( options && options.escape ) { 4305 this.propagate('escape'); 4306 } 4307 4307 4308 module.exports = AttachmentsBrowser; 4308 return this; 4309 }, 4310 /** 4311 * @returns {wp.media.view.Modal} Returns itself to allow chaining 4312 */ 4313 escape: function() { 4314 return this.close({ escape: true }); 4315 }, 4316 /** 4317 * @param {Object} event 4318 */ 4319 escapeHandler: function( event ) { 4320 event.preventDefault(); 4321 this.escape(); 4322 }, 4309 4323 4310 },{}],33:[function(require,module,exports){ 4311 var Attachments = wp.media.view.Attachments, 4312 Selection; 4324 /** 4325 * @param {Array|Object} content Views to register to '.media-modal-content' 4326 * @returns {wp.media.view.Modal} Returns itself to allow chaining 4327 */ 4328 content: function( content ) { 4329 this.views.set( '.media-modal-content', content ); 4330 return this; 4331 }, 4313 4332 4314 /** 4315 * wp.media.view.Attachments.Selection 4316 * 4317 * @memberOf wp.media.view.Attachments 4318 * 4319 * @class 4320 * @augments wp.media.view.Attachments 4321 * @augments wp.media.View 4322 * @augments wp.Backbone.View 4323 * @augments Backbone.View 4324 */ 4325 Selection = Attachments.extend(/** @lends wp.media.view.Attachments.Selection.prototype */{ 4326 events: {}, 4327 initialize: function() { 4328 _.defaults( this.options, { 4329 sortable: false, 4330 resize: false, 4333 /** 4334 * Triggers a modal event and if the `propagate` option is set, 4335 * forwards events to the modal's controller. 4336 * 4337 * @param {string} id 4338 * @returns {wp.media.view.Modal} Returns itself to allow chaining 4339 */ 4340 propagate: function( id ) { 4341 this.trigger( id ); 4331 4342 4332 // The single `Attachment` view to be used in the `Attachments` view. 4333 AttachmentView: wp.media.view.Attachment.Selection 4334 }); 4335 // Call 'initialize' directly on the parent class. 4336 return Attachments.prototype.initialize.apply( this, arguments ); 4343 if ( this.options.propagate ) { 4344 this.controller.trigger( id ); 4345 } 4346 4347 return this; 4348 }, 4349 /** 4350 * @param {Object} event 4351 */ 4352 keydown: function( event ) { 4353 // Close the modal when escape is pressed. 4354 if ( 27 === event.which && this.$el.is(':visible') ) { 4355 this.escape(); 4356 event.stopImmediatePropagation(); 4357 } 4337 4358 } 4338 4359 }); 4339 4360 4340 module.exports = Selection;4361 module.exports = Modal; 4341 4362 4342 },{}],34:[function(require,module,exports){ 4343 var $ = Backbone.$, 4344 ButtonGroup; 4363 4364 /***/ }), 4365 /* 52 */ 4366 /***/ (function(module, exports) { 4345 4367 4346 4368 /** 4347 * wp.media.view. ButtonGroup4369 * wp.media.view.FocusManager 4348 4370 * 4349 4371 * @memberOf wp.media.view 4350 4372 * … … var $ = Backbone.$, 4353 4375 * @augments wp.Backbone.View 4354 4376 * @augments Backbone.View 4355 4377 */ 4356 ButtonGroup = wp.media.View.extend(/** @lends wp.media.view.ButtonGroup.prototype */{ 4357 tagName: 'div', 4358 className: 'button-group button-large media-button-group', 4359 4360 initialize: function() { 4361 /** 4362 * @member {wp.media.view.Button[]} 4363 */ 4364 this.buttons = _.map( this.options.buttons || [], function( button ) { 4365 if ( button instanceof Backbone.View ) { 4366 return button; 4367 } else { 4368 return new wp.media.view.Button( button ).render(); 4369 } 4370 }); 4371 4372 delete this.options.buttons; 4378 var FocusManager = wp.media.View.extend(/** @lends wp.media.view.FocusManager.prototype */{ 4373 4379 4374 if ( this.options.classes ) { 4375 this.$el.addClass( this.options.classes ); 4376 } 4380 events: { 4381 'keydown': 'constrainTabbing' 4377 4382 }, 4378 4383 4384 focus: function() { // Reset focus on first left menu item 4385 this.$('.media-menu-item').first().focus(); 4386 }, 4379 4387 /** 4380 * @ returns {wp.media.view.ButtonGroup}4388 * @param {Object} event 4381 4389 */ 4382 render: function() { 4383 this.$el.html( $( _.pluck( this.buttons, 'el' ) ).detach() ); 4384 return this; 4390 constrainTabbing: function( event ) { 4391 var tabbables; 4392 4393 // Look for the tab key. 4394 if ( 9 !== event.keyCode ) { 4395 return; 4396 } 4397 4398 // Skip the file input added by Plupload. 4399 tabbables = this.$( ':tabbable' ).not( '.moxie-shim input[type="file"]' ); 4400 4401 // Keep tab focus within media modal while it's open 4402 if ( tabbables.last()[0] === event.target && ! event.shiftKey ) { 4403 tabbables.first().focus(); 4404 return false; 4405 } else if ( tabbables.first()[0] === event.target && event.shiftKey ) { 4406 tabbables.last().focus(); 4407 return false; 4408 } 4385 4409 } 4410 4386 4411 }); 4387 4412 4388 module.exports = ButtonGroup; 4413 module.exports = FocusManager; 4414 4415 4416 /***/ }), 4417 /* 53 */ 4418 /***/ (function(module, exports) { 4419 4420 var $ = jQuery, 4421 UploaderWindow; 4389 4422 4390 },{}],35:[function(require,module,exports){4391 4423 /** 4392 * wp.media.view.Button 4424 * wp.media.view.UploaderWindow 4425 * 4426 * An uploader window that allows for dragging and dropping media. 4393 4427 * 4394 4428 * @memberOf wp.media.view 4395 4429 * … … module.exports = ButtonGroup; 4397 4431 * @augments wp.media.View 4398 4432 * @augments wp.Backbone.View 4399 4433 * @augments Backbone.View 4434 * 4435 * @param {object} [options] Options hash passed to the view. 4436 * @param {object} [options.uploader] Uploader properties. 4437 * @param {jQuery} [options.uploader.browser] 4438 * @param {jQuery} [options.uploader.dropzone] jQuery collection of the dropzone. 4439 * @param {object} [options.uploader.params] 4400 4440 */ 4401 var Button = wp.media.View.extend(/** @lends wp.media.view.Button.prototype */{4402 tagName: 'button',4403 className: 'media-button',4404 attributes: { type: 'button' },4441 UploaderWindow = wp.media.View.extend(/** @lends wp.media.view.UploaderWindow.prototype */{ 4442 tagName: 'div', 4443 className: 'uploader-window', 4444 template: wp.template('uploader-window'), 4405 4445 4406 events: { 4407 'click': 'click' 4408 }, 4446 initialize: function() { 4447 var uploader; 4409 4448 4410 defaults: { 4411 text: '', 4412 style: '', 4413 size: 'large', 4414 disabled: false 4415 }, 4449 this.$browser = $( '<button type="button" class="browser" />' ).hide().appendTo( 'body' ); 4416 4450 4417 initialize: function() { 4418 /** 4419 * Create a model with the provided `defaults`. 4420 * 4421 * @member {Backbone.Model} 4422 */ 4423 this.model = new Backbone.Model( this.defaults ); 4451 uploader = this.options.uploader = _.defaults( this.options.uploader || {}, { 4452 dropzone: this.$el, 4453 browser: this.$browser, 4454 params: {} 4455 }); 4424 4456 4425 // If any of the `options` have a key from `defaults`, apply its 4426 // value to the `model` and remove it from the `options object. 4427 _.each( this.defaults, function( def, key ) { 4428 var value = this.options[ key ]; 4429 if ( _.isUndefined( value ) ) { 4430 return; 4431 } 4457 // Ensure the dropzone is a jQuery collection. 4458 if ( uploader.dropzone && ! (uploader.dropzone instanceof $) ) { 4459 uploader.dropzone = $( uploader.dropzone ); 4460 } 4432 4461 4433 this.model.set( key, value ); 4434 delete this.options[ key ]; 4462 this.controller.on( 'activate', this.refresh, this ); 4463 4464 this.controller.on( 'detach', function() { 4465 this.$browser.remove(); 4435 4466 }, this ); 4467 }, 4436 4468 4437 this.listenTo( this.model, 'change', this.render ); 4469 refresh: function() { 4470 if ( this.uploader ) { 4471 this.uploader.refresh(); 4472 } 4438 4473 }, 4439 /**4440 * @returns {wp.media.view.Button} Returns itself to allow chaining4441 */4442 render: function() {4443 var classes = [ 'button', this.className ],4444 model = this.model.toJSON();4445 4474 4446 if ( model.style ) { 4447 classes.push( 'button-' + model.style ); 4475 ready: function() { 4476 var postId = wp.media.view.settings.post.id, 4477 dropzone; 4478 4479 // If the uploader already exists, bail. 4480 if ( this.uploader ) { 4481 return; 4448 4482 } 4449 4483 4450 if ( model.size) {4451 classes.push( 'button-' + model.size );4484 if ( postId ) { 4485 this.options.uploader.params.post_id = postId; 4452 4486 } 4487 this.uploader = new wp.Uploader( this.options.uploader ); 4453 4488 4454 classes = _.uniq( classes.concat( this.options.classes ) ); 4455 this.el.className = classes.join(' '); 4489 dropzone = this.uploader.dropzone; 4490 dropzone.on( 'dropzone:enter', _.bind( this.show, this ) ); 4491 dropzone.on( 'dropzone:leave', _.bind( this.hide, this ) ); 4456 4492 4457 this.$el.attr( 'disabled', model.disabled);4458 this.$el.text( this.model.get('text') );4493 $( this.uploader ).on( 'uploader:ready', _.bind( this._ready, this ) ); 4494 }, 4459 4495 4460 return this; 4496 _ready: function() { 4497 this.controller.trigger( 'uploader:ready' ); 4461 4498 }, 4462 /**4463 * @param {Object} event4464 */4465 click: function( event ) {4466 if ( '#' === this.attributes.href ) {4467 event.preventDefault();4468 }4469 4499 4470 if ( this.options.click && ! this.model.get('disabled') ) { 4471 this.options.click.apply( this, arguments ); 4472 } 4500 show: function() { 4501 var $el = this.$el.show(); 4502 4503 // Ensure that the animation is triggered by waiting until 4504 // the transparent element is painted into the DOM. 4505 _.defer( function() { 4506 $el.css({ opacity: 1 }); 4507 }); 4508 }, 4509 4510 hide: function() { 4511 var $el = this.$el.css({ opacity: 0 }); 4512 4513 wp.media.transition( $el ).done( function() { 4514 // Transition end events are subject to race conditions. 4515 // Make sure that the value is set as intended. 4516 if ( '0' === $el.css('opacity') ) { 4517 $el.hide(); 4518 } 4519 }); 4520 4521 // https://core.trac.wordpress.org/ticket/27341 4522 _.delay( function() { 4523 if ( '0' === $el.css('opacity') && $el.is(':visible') ) { 4524 $el.hide(); 4525 } 4526 }, 500 ); 4473 4527 } 4474 4528 }); 4475 4529 4476 module.exports = Button; 4530 module.exports = UploaderWindow; 4531 4532 4533 /***/ }), 4534 /* 54 */ 4535 /***/ (function(module, exports) { 4477 4536 4478 },{}],36:[function(require,module,exports){4479 4537 var View = wp.media.View, 4480 UploaderStatus = wp.media.view.UploaderStatus,4481 4538 l10n = wp.media.view.l10n, 4482 4539 $ = jQuery, 4483 Cropper;4540 EditorUploader; 4484 4541 4485 4542 /** 4486 * wp.media.view.Cropper 4487 * 4488 * Uses the imgAreaSelect plugin to allow a user to crop an image. 4543 * Creates a dropzone on WP editor instances (elements with .wp-editor-wrap) 4544 * and relays drag'n'dropped files to a media workflow. 4489 4545 * 4490 * Takes imgAreaSelect options from 4491 * wp.customize.HeaderControl.calculateImageSelectOptions via 4492 * wp.customize.HeaderControl.openMM. 4546 * wp.media.view.EditorUploader 4493 4547 * 4494 4548 * @memberOf wp.media.view 4495 4549 * … … var View = wp.media.View, 4498 4552 * @augments wp.Backbone.View 4499 4553 * @augments Backbone.View 4500 4554 */ 4501 Cropper = View.extend(/** @lends wp.media.view.Cropper.prototype */{ 4502 className: 'crop-content', 4503 template: wp.template('crop-content'), 4555 EditorUploader = View.extend(/** @lends wp.media.view.EditorUploader.prototype */{ 4556 tagName: 'div', 4557 className: 'uploader-editor', 4558 template: wp.template( 'uploader-editor' ), 4559 4560 localDrag: false, 4561 overContainer: false, 4562 overDropzone: false, 4563 draggingFile: null, 4564 4565 /** 4566 * Bind drag'n'drop events to callbacks. 4567 */ 4504 4568 initialize: function() { 4505 _.bindAll(this, 'onImageLoad'); 4506 }, 4507 ready: function() { 4508 this.controller.frame.on('content:error:crop', this.onError, this); 4509 this.$image = this.$el.find('.crop-image'); 4510 this.$image.on('load', this.onImageLoad); 4511 $(window).on('resize.cropper', _.debounce(this.onImageLoad, 250)); 4512 }, 4513 remove: function() { 4514 $(window).off('resize.cropper'); 4515 this.$el.remove(); 4516 this.$el.off(); 4517 View.prototype.remove.apply(this, arguments); 4518 }, 4519 prepare: function() { 4520 return { 4521 title: l10n.cropYourImage, 4522 url: this.options.attachment.get('url') 4523 }; 4524 }, 4525 onImageLoad: function() { 4526 var imgOptions = this.controller.get('imgSelectOptions'), 4527 imgSelect; 4569 this.initialized = false; 4528 4570 4529 if (typeof imgOptions === 'function') { 4530 imgOptions = imgOptions(this.options.attachment, this.controller); 4571 // Bail if not enabled or UA does not support drag'n'drop or File API. 4572 if ( ! window.tinyMCEPreInit || ! window.tinyMCEPreInit.dragDropUpload || ! this.browserSupport() ) { 4573 return this; 4531 4574 } 4532 4575 4533 imgOptions = _.extend(imgOptions, { 4534 parent: this.$el, 4535 onInit: function() { 4536 this.parent.children().on( 'mousedown touchstart', function( e ){ 4576 this.$document = $(document); 4577 this.dropzones = []; 4578 this.files = []; 4537 4579 4538 if ( e.shiftKey ) { 4539 imgSelect.setOptions( { 4540 aspectRatio: '1:1' 4541 } ); 4542 } else { 4543 imgSelect.setOptions( { 4544 aspectRatio: false 4545 } ); 4546 } 4547 } ); 4580 this.$document.on( 'drop', '.uploader-editor', _.bind( this.drop, this ) ); 4581 this.$document.on( 'dragover', '.uploader-editor', _.bind( this.dropzoneDragover, this ) ); 4582 this.$document.on( 'dragleave', '.uploader-editor', _.bind( this.dropzoneDragleave, this ) ); 4583 this.$document.on( 'click', '.uploader-editor', _.bind( this.click, this ) ); 4584 4585 this.$document.on( 'dragover', _.bind( this.containerDragover, this ) ); 4586 this.$document.on( 'dragleave', _.bind( this.containerDragleave, this ) ); 4587 4588 this.$document.on( 'dragstart dragend drop', _.bind( function( event ) { 4589 this.localDrag = event.type === 'dragstart'; 4590 4591 if ( event.type === 'drop' ) { 4592 this.containerDragleave(); 4548 4593 } 4549 } ); 4550 this.trigger('image-loaded'); 4551 imgSelect = this.controller.imgSelect = this.$image.imgAreaSelect(imgOptions); 4594 }, this ) ); 4595 4596 this.initialized = true; 4597 return this; 4552 4598 }, 4553 onError: function() {4554 var filename = this.options.attachment.get('filename');4555 4599 4556 this.views.add( '.upload-errors', new wp.media.view.UploaderStatusError({ 4557 filename: UploaderStatus.prototype.filename(filename), 4558 message: window._wpMediaViewsL10n.cropError 4559 }), { at: 0 }); 4560 } 4561 }); 4600 /** 4601 * Check browser support for drag'n'drop. 4602 * 4603 * @return Boolean 4604 */ 4605 browserSupport: function() { 4606 var supports = false, div = document.createElement('div'); 4607 4608 supports = ( 'draggable' in div ) || ( 'ondragstart' in div && 'ondrop' in div ); 4609 supports = supports && !! ( window.File && window.FileList && window.FileReader ); 4610 return supports; 4611 }, 4612 4613 isDraggingFile: function( event ) { 4614 if ( this.draggingFile !== null ) { 4615 return this.draggingFile; 4616 } 4617 4618 if ( _.isUndefined( event.originalEvent ) || _.isUndefined( event.originalEvent.dataTransfer ) ) { 4619 return false; 4620 } 4621 4622 this.draggingFile = _.indexOf( event.originalEvent.dataTransfer.types, 'Files' ) > -1 && 4623 _.indexOf( event.originalEvent.dataTransfer.types, 'text/plain' ) === -1; 4562 4624 4563 module.exports = Cropper; 4625 return this.draggingFile; 4626 }, 4564 4627 4565 },{}],37:[function(require,module,exports){ 4566 var View = wp.media.View, 4567 EditImage; 4628 refresh: function( e ) { 4629 var dropzone_id; 4630 for ( dropzone_id in this.dropzones ) { 4631 // Hide the dropzones only if dragging has left the screen. 4632 this.dropzones[ dropzone_id ].toggle( this.overContainer || this.overDropzone ); 4633 } 4568 4634 4569 /** 4570 * wp.media.view.EditImage 4571 * 4572 * @memberOf wp.media.view 4573 * 4574 * @class 4575 * @augments wp.media.View 4576 * @augments wp.Backbone.View 4577 * @augments Backbone.View 4578 */ 4579 EditImage = View.extend(/** @lends wp.media.view.EditImage.prototype */{ 4580 className: 'image-editor', 4581 template: wp.template('image-editor'), 4635 if ( ! _.isUndefined( e ) ) { 4636 $( e.target ).closest( '.uploader-editor' ).toggleClass( 'droppable', this.overDropzone ); 4637 } 4582 4638 4583 initialize: function( options ) { 4584 this.editor = window.imageEdit; 4585 this.controller = options.controller; 4586 View.prototype.initialize.apply( this, arguments ); 4587 }, 4639 if ( ! this.overContainer && ! this.overDropzone ) { 4640 this.draggingFile = null; 4641 } 4588 4642 4589 prepare: function() { 4590 return this.model.toJSON(); 4643 return this; 4591 4644 }, 4592 4645 4593 loadEditor: function() {4594 var dfd = this.editor.open( this.model.get('id'), this.model.get('nonces').edit, this );4595 dfd.done( _.bind( this.focus, this ) );4596 },4646 render: function() { 4647 if ( ! this.initialized ) { 4648 return this; 4649 } 4597 4650 4598 focus: function() { 4599 this.$( '.imgedit-submit .button' ).eq( 0 ).focus(); 4651 View.prototype.render.apply( this, arguments ); 4652 $( '.wp-editor-wrap' ).each( _.bind( this.attach, this ) ); 4653 return this; 4600 4654 }, 4601 4655 4602 back: function() { 4603 var lastState = this.controller.lastState(); 4604 this.controller.setState( lastState ); 4656 attach: function( index, editor ) { 4657 // Attach a dropzone to an editor. 4658 var dropzone = this.$el.clone(); 4659 this.dropzones.push( dropzone ); 4660 $( editor ).append( dropzone ); 4661 return this; 4605 4662 }, 4606 4663 4607 refresh: function() { 4608 this.model.fetch(); 4609 }, 4664 /** 4665 * When a file is dropped on the editor uploader, open up an editor media workflow 4666 * and upload the file immediately. 4667 * 4668 * @param {jQuery.Event} event The 'drop' event. 4669 */ 4670 drop: function( event ) { 4671 var $wrap, uploadView; 4610 4672 4611 save: function() {4612 var lastState = this.controller.lastState();4673 this.containerDragleave( event ); 4674 this.dropzoneDragleave( event ); 4613 4675 4614 this. model.fetch().done( _.bind( function() {4615 this.controller.setState( lastState );4616 }, this ) );4617 }4676 this.files = event.originalEvent.dataTransfer.files; 4677 if ( this.files.length < 1 ) { 4678 return; 4679 } 4618 4680 4619 }); 4681 // Set the active editor to the drop target. 4682 $wrap = $( event.target ).parents( '.wp-editor-wrap' ); 4683 if ( $wrap.length > 0 && $wrap[0].id ) { 4684 window.wpActiveEditor = $wrap[0].id.slice( 3, -5 ); 4685 } 4620 4686 4621 module.exports = EditImage; 4687 if ( ! this.workflow ) { 4688 this.workflow = wp.media.editor.open( window.wpActiveEditor, { 4689 frame: 'post', 4690 state: 'insert', 4691 title: l10n.addMedia, 4692 multiple: true 4693 }); 4622 4694 4623 },{}],38:[function(require,module,exports){ 4624 /** 4625 * wp.media.view.Embed 4626 * 4627 * @memberOf wp.media.view 4628 * 4629 * @class 4630 * @augments wp.media.View 4631 * @augments wp.Backbone.View 4632 * @augments Backbone.View 4633 */ 4634 var Embed = wp.media.View.extend(/** @lends wp.media.view.Ember.prototype */{ 4635 className: 'media-embed', 4695 uploadView = this.workflow.uploader; 4636 4696 4637 initialize: function() { 4638 /** 4639 * @member {wp.media.view.EmbedUrl} 4640 */ 4641 this.url = new wp.media.view.EmbedUrl({ 4642 controller: this.controller, 4643 model: this.model.props 4644 }).render(); 4697 if ( uploadView.uploader && uploadView.uploader.ready ) { 4698 this.addFiles.apply( this ); 4699 } else { 4700 this.workflow.on( 'uploader:ready', this.addFiles, this ); 4701 } 4702 } else { 4703 this.workflow.state().reset(); 4704 this.addFiles.apply( this ); 4705 this.workflow.open(); 4706 } 4645 4707 4646 this.views.set([ this.url ]); 4647 this.refresh(); 4648 this.listenTo( this.model, 'change:type', this.refresh ); 4649 this.listenTo( this.model, 'change:loading', this.loading ); 4708 return false; 4650 4709 }, 4651 4710 4652 4711 /** 4653 * @param {Object} view4712 * Add the files to the uploader. 4654 4713 */ 4655 settings: function( view ) { 4656 if ( this._settings ) { 4657 this._settings.remove(); 4714 addFiles: function() { 4715 if ( this.files.length ) { 4716 this.workflow.uploader.uploader.uploader.addFile( _.toArray( this.files ) ); 4717 this.files = []; 4658 4718 } 4659 this._settings = view; 4660 this.views.add( view ); 4719 return this; 4661 4720 }, 4662 4721 4663 refresh: function() { 4664 var type = this.model.get('type'), 4665 constructor; 4666 4667 if ( 'image' === type ) { 4668 constructor = wp.media.view.EmbedImage; 4669 } else if ( 'link' === type ) { 4670 constructor = wp.media.view.EmbedLink; 4671 } else { 4722 containerDragover: function( event ) { 4723 if ( this.localDrag || ! this.isDraggingFile( event ) ) { 4672 4724 return; 4673 4725 } 4674 4726 4675 this.settings( new constructor({ 4676 controller: this.controller, 4677 model: this.model.props, 4678 priority: 40 4679 }) ); 4727 this.overContainer = true; 4728 this.refresh(); 4680 4729 }, 4681 4730 4682 loading: function() { 4683 this.$el.toggleClass( 'embed-loading', this.model.get('loading') ); 4684 } 4685 }); 4731 containerDragleave: function() { 4732 this.overContainer = false; 4686 4733 4687 module.exports = Embed; 4734 // Throttle dragleave because it's called when bouncing from some elements to others. 4735 _.delay( _.bind( this.refresh, this ), 50 ); 4736 }, 4688 4737 4689 },{}],39:[function(require,module,exports){ 4690 var AttachmentDisplay = wp.media.view.Settings.AttachmentDisplay, 4691 EmbedImage; 4738 dropzoneDragover: function( event ) { 4739 if ( this.localDrag || ! this.isDraggingFile( event ) ) { 4740 return; 4741 } 4692 4742 4693 /** 4694 * wp.media.view.EmbedImage 4695 * 4696 * @memberOf wp.media.view 4697 * 4698 * @class 4699 * @augments wp.media.view.Settings.AttachmentDisplay 4700 * @augments wp.media.view.Settings 4701 * @augments wp.media.View 4702 * @augments wp.Backbone.View 4703 * @augments Backbone.View 4704 */ 4705 EmbedImage = AttachmentDisplay.extend(/** @lends wp.media.view.EmbedImage.prototype */{ 4706 className: 'embed-media-settings', 4707 template: wp.template('embed-image-settings'), 4743 this.overDropzone = true; 4744 this.refresh( event ); 4745 return false; 4746 }, 4708 4747 4709 initialize: function() { 4710 /** 4711 * Call `initialize` directly on parent class with passed arguments 4712 */ 4713 AttachmentDisplay.prototype.initialize.apply( this, arguments ); 4714 this.listenTo( this.model, 'change:url', this.updateImage ); 4748 dropzoneDragleave: function( e ) { 4749 this.overDropzone = false; 4750 _.delay( _.bind( this.refresh, this, e ), 50 ); 4715 4751 }, 4716 4752 4717 updateImage: function() { 4718 this.$('img').attr( 'src', this.model.get('url') ); 4753 click: function( e ) { 4754 // In the rare case where the dropzone gets stuck, hide it on click. 4755 this.containerDragleave( e ); 4756 this.dropzoneDragleave( e ); 4757 this.localDrag = false; 4719 4758 } 4720 4759 }); 4721 4760 4722 module.exports = E mbedImage;4761 module.exports = EditorUploader; 4723 4762 4724 },{}],40:[function(require,module,exports){ 4725 var $ = jQuery, 4726 EmbedLink; 4763 4764 /***/ }), 4765 /* 55 */ 4766 /***/ (function(module, exports) { 4767 4768 var View = wp.media.View, 4769 UploaderInline; 4727 4770 4728 4771 /** 4729 * wp.media.view.EmbedLink 4772 * wp.media.view.UploaderInline 4773 * 4774 * The inline uploader that shows up in the 'Upload Files' tab. 4730 4775 * 4731 4776 * @memberOf wp.media.view 4732 4777 * 4733 4778 * @class 4734 * @augments wp.media.view.Settings4735 4779 * @augments wp.media.View 4736 4780 * @augments wp.Backbone.View 4737 4781 * @augments Backbone.View 4738 4782 */ 4739 EmbedLink = wp.media.view.Settings.extend(/** @lends wp.media.view.EmbedLink.prototype */{ 4740 className: 'embed-link-settings', 4741 template: wp.template('embed-link-settings'), 4783 UploaderInline = View.extend(/** @lends wp.media.view.UploaderInline.prototype */{ 4784 tagName: 'div', 4785 className: 'uploader-inline', 4786 template: wp.template('uploader-inline'), 4742 4787 4743 initialize: function(){4744 this.listenTo( this.model, 'change:url', this.updateoEmbed );4788 events: { 4789 'click .close': 'hide' 4745 4790 }, 4746 4791 4747 updateoEmbed: _.debounce( function() { 4748 var url = this.model.get( 'url' ); 4792 initialize: function() { 4793 _.defaults( this.options, { 4794 message: '', 4795 status: true, 4796 canClose: false 4797 }); 4749 4798 4750 // clear out previous results 4751 this.$('.embed-container').hide().find('.embed-preview').empty(); 4752 this.$( '.setting' ).hide(); 4799 if ( ! this.options.$browser && this.controller.uploader ) { 4800 this.options.$browser = this.controller.uploader.$browser; 4801 } 4802 4803 if ( _.isUndefined( this.options.postId ) ) { 4804 this.options.postId = wp.media.view.settings.post.id; 4805 } 4753 4806 4754 // only proceed with embed if the field contains more than 11 characters4755 // Example: http://a.io is 11 chars4756 if ( url && ( url.length < 11 || ! url.match(/^http(s)?:\/\//) ) ) {4757 return;4807 if ( this.options.status ) { 4808 this.views.set( '.upload-inline-status', new wp.media.view.UploaderStatus({ 4809 controller: this.controller 4810 }) ); 4758 4811 } 4812 }, 4759 4813 4760 this.fetch(); 4761 }, wp.media.controller.Embed.sensitivity ), 4814 prepare: function() { 4815 var suggestedWidth = this.controller.state().get('suggestedWidth'), 4816 suggestedHeight = this.controller.state().get('suggestedHeight'), 4817 data = {}; 4762 4818 4763 fetch: function() {4764 var url = this.model.get( 'url' ), re, youTubeEmbedMatch;4819 data.message = this.options.message; 4820 data.canClose = this.options.canClose; 4765 4821 4766 // check if they haven't typed in 500 ms4767 if ( $('#embed-url-field').val() !== url ) {4768 return;4822 if ( suggestedWidth && suggestedHeight ) { 4823 data.suggestedWidth = suggestedWidth; 4824 data.suggestedHeight = suggestedHeight; 4769 4825 } 4770 4826 4771 if ( this.dfd && 'pending' === this.dfd.state() ) { 4772 this.dfd.abort(); 4827 return data; 4828 }, 4829 /** 4830 * @returns {wp.media.view.UploaderInline} Returns itself to allow chaining 4831 */ 4832 dispose: function() { 4833 if ( this.disposing ) { 4834 /** 4835 * call 'dispose' directly on the parent class 4836 */ 4837 return View.prototype.dispose.apply( this, arguments ); 4773 4838 } 4774 4839 4775 // Support YouTube embed urls, since they work once in the editor. 4776 re = /https?:\/\/www\.youtube\.com\/embed\/([^/]+)/; 4777 youTubeEmbedMatch = re.exec( url ); 4778 if ( youTubeEmbedMatch ) { 4779 url = 'https://www.youtube.com/watch?v=' + youTubeEmbedMatch[ 1 ]; 4780 } 4840 // Run remove on `dispose`, so we can be sure to refresh the 4841 // uploader with a view-less DOM. Track whether we're disposing 4842 // so we don't trigger an infinite loop. 4843 this.disposing = true; 4844 return this.remove(); 4845 }, 4846 /** 4847 * @returns {wp.media.view.UploaderInline} Returns itself to allow chaining 4848 */ 4849 remove: function() { 4850 /** 4851 * call 'remove' directly on the parent class 4852 */ 4853 var result = View.prototype.remove.apply( this, arguments ); 4781 4854 4782 this.dfd = wp.apiRequest({ 4783 url: wp.media.view.settings.oEmbedProxyUrl, 4784 data: { 4785 url: url, 4786 maxwidth: this.model.get( 'width' ), 4787 maxheight: this.model.get( 'height' ) 4788 }, 4789 type: 'GET', 4790 dataType: 'json', 4791 context: this 4792 }) 4793 .done( function( response ) { 4794 this.renderoEmbed( { 4795 data: { 4796 body: response.html || '' 4797 } 4798 } ); 4799 } ) 4800 .fail( this.renderFail ); 4855 _.defer( _.bind( this.refresh, this ) ); 4856 return result; 4801 4857 }, 4802 4858 4803 renderFail: function ( response, status ) { 4804 if ( 'abort' === status ) { 4805 return; 4859 refresh: function() { 4860 var uploader = this.controller.uploader; 4861 4862 if ( uploader ) { 4863 uploader.refresh(); 4806 4864 } 4807 this.$( '.link-text' ).show();4808 4865 }, 4866 /** 4867 * @returns {wp.media.view.UploaderInline} 4868 */ 4869 ready: function() { 4870 var $browser = this.options.$browser, 4871 $placeholder; 4809 4872 4810 renderoEmbed: function( response) {4811 var html = ( response && response.data && response.data.body ) || '';4873 if ( this.controller.uploader ) { 4874 $placeholder = this.$('.browser'); 4812 4875 4813 if ( html ) { 4814 this.$('.embed-container').show().find('.embed-preview').html( html ); 4815 } else { 4816 this.renderFail(); 4876 // Check if we've already replaced the placeholder. 4877 if ( $placeholder[0] === $browser[0] ) { 4878 return; 4879 } 4880 4881 $browser.detach().text( $placeholder.text() ); 4882 $browser[0].className = $placeholder[0].className; 4883 $placeholder.replaceWith( $browser.show() ); 4884 } 4885 4886 this.refresh(); 4887 return this; 4888 }, 4889 show: function() { 4890 this.$el.removeClass( 'hidden' ); 4891 if ( this.controller.$uploaderToggler && this.controller.$uploaderToggler.length ) { 4892 this.controller.$uploaderToggler.attr( 'aria-expanded', 'true' ); 4893 } 4894 }, 4895 hide: function() { 4896 this.$el.addClass( 'hidden' ); 4897 if ( this.controller.$uploaderToggler && this.controller.$uploaderToggler.length ) { 4898 this.controller.$uploaderToggler 4899 .attr( 'aria-expanded', 'false' ) 4900 // Move focus back to the toggle button when closing the uploader. 4901 .focus(); 4817 4902 } 4818 4903 } 4904 4819 4905 }); 4820 4906 4821 module.exports = EmbedLink; 4907 module.exports = UploaderInline; 4908 4909 4910 /***/ }), 4911 /* 56 */ 4912 /***/ (function(module, exports) { 4822 4913 4823 },{}],41:[function(require,module,exports){4824 4914 var View = wp.media.View, 4825 $ = jQuery, 4826 EmbedUrl; 4915 UploaderStatus; 4827 4916 4828 4917 /** 4829 * wp.media.view.EmbedUrl 4918 * wp.media.view.UploaderStatus 4919 * 4920 * An uploader status for on-going uploads. 4830 4921 * 4831 4922 * @memberOf wp.media.view 4832 4923 * … … var View = wp.media.View, 4835 4926 * @augments wp.Backbone.View 4836 4927 * @augments Backbone.View 4837 4928 */ 4838 EmbedUrl = View.extend(/** @lends wp.media.view.EmbedUrl.prototype */{4839 tagName: 'label',4840 className: 'embed-url',4929 UploaderStatus = View.extend(/** @lends wp.media.view.UploaderStatus.prototype */{ 4930 className: 'media-uploader-status', 4931 template: wp.template('uploader-status'), 4841 4932 4842 4933 events: { 4843 'input': 'url', 4844 'keyup': 'url', 4845 'change': 'url' 4934 'click .upload-dismiss-errors': 'dismiss' 4846 4935 }, 4847 4936 4848 4937 initialize: function() { 4849 this.$input = $('<input id="embed-url-field" type="url" />').val( this.model.get('url') ); 4850 this.input = this.$input[0]; 4851 4852 this.spinner = $('<span class="spinner" />')[0]; 4853 this.$el.append([ this.input, this.spinner ]); 4854 4855 this.listenTo( this.model, 'change:url', this.render ); 4938 this.queue = wp.Uploader.queue; 4939 this.queue.on( 'add remove reset', this.visibility, this ); 4940 this.queue.on( 'add remove reset change:percent', this.progress, this ); 4941 this.queue.on( 'add remove reset change:uploading', this.info, this ); 4856 4942 4857 if ( this.model.get( 'url' ) ) { 4858 _.delay( _.bind( function () { 4859 this.model.trigger( 'change:url' ); 4860 }, this ), 500 ); 4861 } 4943 this.errors = wp.Uploader.errors; 4944 this.errors.reset(); 4945 this.errors.on( 'add remove reset', this.visibility, this ); 4946 this.errors.on( 'add', this.error, this ); 4862 4947 }, 4863 4948 /** 4864 * @returns {wp.media.view. EmbedUrl} Returns itself to allow chaining4949 * @returns {wp.media.view.UploaderStatus} 4865 4950 */ 4866 render: function() { 4867 var $input = this.$input; 4868 4869 if ( $input.is(':focus') ) { 4870 return; 4871 } 4872 4873 this.input.value = this.model.get('url') || 'http://'; 4951 dispose: function() { 4952 wp.Uploader.queue.off( null, null, this ); 4874 4953 /** 4875 * Call `render` directly on parent class with passed arguments4954 * call 'dispose' directly on the parent class 4876 4955 */ 4877 View.prototype. render.apply( this, arguments );4956 View.prototype.dispose.apply( this, arguments ); 4878 4957 return this; 4879 4958 }, 4880 4959 4960 visibility: function() { 4961 this.$el.toggleClass( 'uploading', !! this.queue.length ); 4962 this.$el.toggleClass( 'errors', !! this.errors.length ); 4963 this.$el.toggle( !! this.queue.length || !! this.errors.length ); 4964 }, 4965 4881 4966 ready: function() { 4882 if ( ! wp.media.isTouchDevice ) { 4883 this.focus(); 4967 _.each({ 4968 '$bar': '.media-progress-bar div', 4969 '$index': '.upload-index', 4970 '$total': '.upload-total', 4971 '$filename': '.upload-filename' 4972 }, function( selector, key ) { 4973 this[ key ] = this.$( selector ); 4974 }, this ); 4975 4976 this.visibility(); 4977 this.progress(); 4978 this.info(); 4979 }, 4980 4981 progress: function() { 4982 var queue = this.queue, 4983 $bar = this.$bar; 4984 4985 if ( ! $bar || ! queue.length ) { 4986 return; 4884 4987 } 4988 4989 $bar.width( ( queue.reduce( function( memo, attachment ) { 4990 if ( ! attachment.get('uploading') ) { 4991 return memo + 100; 4992 } 4993 4994 var percent = attachment.get('percent'); 4995 return memo + ( _.isNumber( percent ) ? percent : 100 ); 4996 }, 0 ) / queue.length ) + '%' ); 4885 4997 }, 4886 4998 4887 url: function( event ) { 4888 this.model.set( 'url', $.trim( event.target.value ) ); 4999 info: function() { 5000 var queue = this.queue, 5001 index = 0, active; 5002 5003 if ( ! queue.length ) { 5004 return; 5005 } 5006 5007 active = this.queue.find( function( attachment, i ) { 5008 index = i; 5009 return attachment.get('uploading'); 5010 }); 5011 5012 this.$index.text( index + 1 ); 5013 this.$total.text( queue.length ); 5014 this.$filename.html( active ? this.filename( active.get('filename') ) : '' ); 5015 }, 5016 /** 5017 * @param {string} filename 5018 * @returns {string} 5019 */ 5020 filename: function( filename ) { 5021 return _.escape( filename ); 5022 }, 5023 /** 5024 * @param {Backbone.Model} error 5025 */ 5026 error: function( error ) { 5027 this.views.add( '.upload-errors', new wp.media.view.UploaderStatusError({ 5028 filename: this.filename( error.get('file').name ), 5029 message: error.get('message') 5030 }), { at: 0 }); 4889 5031 }, 4890 5032 4891 5033 /** 4892 * If the input is visible, focus and select its contents.5034 * @param {Object} event 4893 5035 */ 4894 focus: function() { 4895 var $input = this.$input; 4896 if ( $input.is(':visible') ) { 4897 $input.focus()[0].select(); 5036 dismiss: function( event ) { 5037 var errors = this.views.get('.upload-errors'); 5038 5039 event.preventDefault(); 5040 5041 if ( errors ) { 5042 _.invoke( errors, 'remove' ); 4898 5043 } 5044 wp.Uploader.errors.reset(); 4899 5045 } 4900 5046 }); 4901 5047 4902 module.exports = EmbedUrl; 5048 module.exports = UploaderStatus; 5049 5050 5051 /***/ }), 5052 /* 57 */ 5053 /***/ (function(module, exports) { 4903 5054 4904 },{}],42:[function(require,module,exports){4905 5055 /** 4906 * wp.media.view. FocusManager5056 * wp.media.view.UploaderStatusError 4907 5057 * 4908 5058 * @memberOf wp.media.view 4909 5059 * … … module.exports = EmbedUrl; 4912 5062 * @augments wp.Backbone.View 4913 5063 * @augments Backbone.View 4914 5064 */ 4915 var FocusManager = wp.media.View.extend(/** @lends wp.media.view.FocusManager.prototype */{ 4916 4917 events: { 4918 'keydown': 'constrainTabbing' 4919 }, 4920 4921 focus: function() { // Reset focus on first left menu item 4922 this.$('.media-menu-item').first().focus(); 4923 }, 4924 /** 4925 * @param {Object} event 4926 */ 4927 constrainTabbing: function( event ) { 4928 var tabbables; 4929 4930 // Look for the tab key. 4931 if ( 9 !== event.keyCode ) { 4932 return; 4933 } 5065 var UploaderStatusError = wp.media.View.extend(/** @lends wp.media.view.UploaderStatusError.prototype */{ 5066 className: 'upload-error', 5067 template: wp.template('uploader-status-error') 5068 }); 4934 5069 4935 // Skip the file input added by Plupload. 4936 tabbables = this.$( ':tabbable' ).not( '.moxie-shim input[type="file"]' ); 5070 module.exports = UploaderStatusError; 4937 5071 4938 // Keep tab focus within media modal while it's open4939 if ( tabbables.last()[0] === event.target && ! event.shiftKey ) {4940 tabbables.first().focus();4941 return false;4942 } else if ( tabbables.first()[0] === event.target && event.shiftKey ) {4943 tabbables.last().focus();4944 return false;4945 }4946 }4947 5072 4948 }); 5073 /***/ }), 5074 /* 58 */ 5075 /***/ (function(module, exports) { 4949 5076 4950 module.exports = FocusManager; 5077 var View = wp.media.View, 5078 Toolbar; 4951 5079 4952 },{}],43:[function(require,module,exports){4953 5080 /** 4954 * wp.media.view. Frame5081 * wp.media.view.Toolbar 4955 5082 * 4956 * A frame is a composite view consisting of one or more regions and one or more4957 * states.5083 * A toolbar which consists of a primary and a secondary section. Each sections 5084 * can be filled with views. 4958 5085 * 4959 5086 * @memberOf wp.media.view 4960 5087 * 4961 * @see wp.media.controller.State4962 * @see wp.media.controller.Region4963 *4964 5088 * @class 4965 5089 * @augments wp.media.View 4966 5090 * @augments wp.Backbone.View 4967 5091 * @augments Backbone.View 4968 * @mixes wp.media.controller.StateMachine4969 5092 */ 4970 var Frame = wp.media.View.extend(/** @lends wp.media.view.Frame.prototype */{ 5093 Toolbar = View.extend(/** @lends wp.media.view.Toolbar.prototype */{ 5094 tagName: 'div', 5095 className: 'media-toolbar', 5096 4971 5097 initialize: function() { 4972 _.defaults( this.options, { 4973 mode: [ 'select' ] 4974 }); 4975 this._createRegions(); 4976 this._createStates(); 4977 this._createModes(); 4978 }, 5098 var state = this.controller.state(), 5099 selection = this.selection = state.get('selection'), 5100 library = this.library = state.get('library'); 4979 5101 4980 _createRegions: function() { 4981 // Clone the regions array. 4982 this.regions = this.regions ? this.regions.slice() : []; 5102 this._views = {}; 4983 5103 4984 // Initialize regions. 4985 _.each( this.regions, function( region ) { 4986 this[ region ] = new wp.media.controller.Region({ 4987 view: this, 4988 id: region, 4989 selector: '.media-frame-' + region 4990 }); 4991 }, this ); 4992 }, 4993 /** 4994 * Create the frame's states. 4995 * 4996 * @see wp.media.controller.State 4997 * @see wp.media.controller.StateMachine 4998 * 4999 * @fires wp.media.controller.State#ready 5000 */ 5001 _createStates: function() { 5002 // Create the default `states` collection. 5003 this.states = new Backbone.Collection( null, { 5004 model: wp.media.controller.State 5005 }); 5104 // The toolbar is composed of two `PriorityList` views. 5105 this.primary = new wp.media.view.PriorityList(); 5106 this.secondary = new wp.media.view.PriorityList(); 5107 this.primary.$el.addClass('media-toolbar-primary search-form'); 5108 this.secondary.$el.addClass('media-toolbar-secondary'); 5006 5109 5007 // Ensure states have a reference to the frame. 5008 this.states.on( 'add', function( model ) { 5009 model.frame = this; 5010 model.trigger('ready'); 5011 }, this ); 5110 this.views.set([ this.secondary, this.primary ]); 5012 5111 5013 if ( this.options. states ) {5014 this.s tates.add( this.options.states);5112 if ( this.options.items ) { 5113 this.set( this.options.items, { silent: true }); 5015 5114 } 5016 },5017 5115 5018 /** 5019 * A frame can be in a mode or multiple modes at one time. 5020 * 5021 * For example, the manage media frame can be in the `Bulk Select` or `Edit` mode. 5022 */ 5023 _createModes: function() { 5024 // Store active "modes" that the frame is in. Unrelated to region modes. 5025 this.activeModes = new Backbone.Collection(); 5026 this.activeModes.on( 'add remove reset', _.bind( this.triggerModeEvents, this ) ); 5116 if ( ! this.options.silent ) { 5117 this.render(); 5118 } 5027 5119 5028 _.each( this.options.mode, function( mode ) { 5029 this.activateMode( mode ); 5030 }, this ); 5120 if ( selection ) { 5121 selection.on( 'add remove reset', this.refresh, this ); 5122 } 5123 5124 if ( library ) { 5125 library.on( 'add remove reset', this.refresh, this ); 5126 } 5031 5127 }, 5032 5128 /** 5033 * Reset all states on the frame to their defaults. 5034 * 5035 * @returns {wp.media.view.Frame} Returns itself to allow chaining 5129 * @returns {wp.media.view.Toolbar} Returns itsef to allow chaining 5036 5130 */ 5037 reset: function() { 5038 this.states.invoke( 'trigger', 'reset' ); 5039 return this; 5131 dispose: function() { 5132 if ( this.selection ) { 5133 this.selection.off( null, null, this ); 5134 } 5135 5136 if ( this.library ) { 5137 this.library.off( null, null, this ); 5138 } 5139 /** 5140 * call 'dispose' directly on the parent class 5141 */ 5142 return View.prototype.dispose.apply( this, arguments ); 5143 }, 5144 5145 ready: function() { 5146 this.refresh(); 5040 5147 }, 5148 5041 5149 /** 5042 * Map activeMode collection events to the frame. 5150 * @param {string} id 5151 * @param {Backbone.View|Object} view 5152 * @param {Object} [options={}] 5153 * @returns {wp.media.view.Toolbar} Returns itself to allow chaining 5043 5154 */ 5044 triggerModeEvents: function( model, collection, options ) { 5045 var collectionEvent, 5046 modeEventMap = { 5047 add: 'activate', 5048 remove: 'deactivate' 5049 }, 5050 eventToTrigger; 5051 // Probably a better way to do this. 5052 _.each( options, function( value, key ) { 5053 if ( value ) { 5054 collectionEvent = key; 5155 set: function( id, view, options ) { 5156 var list; 5157 options = options || {}; 5158 5159 // Accept an object with an `id` : `view` mapping. 5160 if ( _.isObject( id ) ) { 5161 _.each( id, function( view, id ) { 5162 this.set( id, view, { silent: true }); 5163 }, this ); 5164 5165 } else { 5166 if ( ! ( view instanceof Backbone.View ) ) { 5167 view.classes = [ 'media-button-' + id ].concat( view.classes || [] ); 5168 view = new wp.media.view.Button( view ).render(); 5055 5169 } 5056 } );5057 5170 5058 if ( ! _.has( modeEventMap, collectionEvent ) ) { 5059 return; 5171 view.controller = view.controller || this.controller; 5172 5173 this._views[ id ] = view; 5174 5175 list = view.options.priority < 0 ? 'secondary' : 'primary'; 5176 this[ list ].set( id, view, options ); 5060 5177 } 5061 5178 5062 eventToTrigger = model.get('id') + ':' + modeEventMap[collectionEvent]; 5063 this.trigger( eventToTrigger ); 5064 }, 5065 /** 5066 * Activate a mode on the frame. 5067 * 5068 * @param string mode Mode ID. 5069 * @returns {this} Returns itself to allow chaining. 5070 */ 5071 activateMode: function( mode ) { 5072 // Bail if the mode is already active. 5073 if ( this.isModeActive( mode ) ) { 5074 return; 5179 if ( ! options.silent ) { 5180 this.refresh(); 5075 5181 } 5076 this.activeModes.add( [ { id: mode } ] );5077 // Add a CSS class to the frame so elements can be styled for the mode.5078 this.$el.addClass( 'mode-' + mode );5079 5182 5080 5183 return this; 5081 5184 }, 5082 5185 /** 5083 * Deactivate a mode on the frame. 5084 * 5085 * @param string mode Mode ID. 5086 * @returns {this} Returns itself to allow chaining. 5186 * @param {string} id 5187 * @returns {wp.media.view.Button} 5087 5188 */ 5088 deactivateMode: function( mode ) { 5089 // Bail if the mode isn't active. 5090 if ( ! this.isModeActive( mode ) ) { 5091 return this; 5092 } 5093 this.activeModes.remove( this.activeModes.where( { id: mode } ) ); 5094 this.$el.removeClass( 'mode-' + mode ); 5095 /** 5096 * Frame mode deactivation event. 5097 * 5098 * @event wp.media.view.Frame#{mode}:deactivate 5099 */ 5100 this.trigger( mode + ':deactivate' ); 5189 get: function( id ) { 5190 return this._views[ id ]; 5191 }, 5192 /** 5193 * @param {string} id 5194 * @param {Object} options 5195 * @returns {wp.media.view.Toolbar} Returns itself to allow chaining 5196 */ 5197 unset: function( id, options ) { 5198 delete this._views[ id ]; 5199 this.primary.unset( id, options ); 5200 this.secondary.unset( id, options ); 5101 5201 5202 if ( ! options || ! options.silent ) { 5203 this.refresh(); 5204 } 5102 5205 return this; 5103 5206 }, 5104 /** 5105 * Check if a mode is enabled on the frame. 5106 * 5107 * @param string mode Mode ID. 5108 * @return bool 5109 */ 5110 isModeActive: function( mode ) { 5111 return Boolean( this.activeModes.where( { id: mode } ).length ); 5207 5208 refresh: function() { 5209 var state = this.controller.state(), 5210 library = state.get('library'), 5211 selection = state.get('selection'); 5212 5213 _.each( this._views, function( button ) { 5214 if ( ! button.model || ! button.options || ! button.options.requires ) { 5215 return; 5216 } 5217 5218 var requires = button.options.requires, 5219 disabled = false; 5220 5221 // Prevent insertion of attachments if any of them are still uploading 5222 if ( selection && selection.models ) { 5223 disabled = _.some( selection.models, function( attachment ) { 5224 return attachment.get('uploading') === true; 5225 }); 5226 } 5227 5228 if ( requires.selection && selection && ! selection.length ) { 5229 disabled = true; 5230 } else if ( requires.library && library && ! library.length ) { 5231 disabled = true; 5232 } 5233 button.model.set( 'disabled', disabled ); 5234 }); 5112 5235 } 5113 5236 }); 5114 5237 5115 // Make the `Frame` a `StateMachine`. 5116 _.extend( Frame.prototype, wp.media.controller.StateMachine.prototype ); 5238 module.exports = Toolbar; 5117 5239 5118 module.exports = Frame;5119 5240 5120 },{}],44:[function(require,module,exports){ 5121 var Select = wp.media.view.MediaFrame.Select, 5241 /***/ }), 5242 /* 59 */ 5243 /***/ (function(module, exports) { 5244 5245 var Toolbar = wp.media.view.Toolbar, 5122 5246 l10n = wp.media.view.l10n, 5123 ImageDetails;5247 Select; 5124 5248 5125 5249 /** 5126 * wp.media.view.MediaFrame.ImageDetails 5127 * 5128 * A media frame for manipulating an image that's already been inserted 5129 * into a post. 5250 * wp.media.view.Toolbar.Select 5130 5251 * 5131 * @memberOf wp.media.view. MediaFrame5252 * @memberOf wp.media.view.Toolbar 5132 5253 * 5133 5254 * @class 5134 * @augments wp.media.view.MediaFrame.Select 5135 * @augments wp.media.view.MediaFrame 5136 * @augments wp.media.view.Frame 5255 * @augments wp.media.view.Toolbar 5137 5256 * @augments wp.media.View 5138 5257 * @augments wp.Backbone.View 5139 5258 * @augments Backbone.View 5140 * @mixes wp.media.controller.StateMachine5141 5259 */ 5142 ImageDetails = Select.extend(/** @lends wp.media.view.MediaFrame.ImageDetails.prototype */{ 5143 defaults: { 5144 id: 'image', 5145 url: '', 5146 menu: 'image-details', 5147 content: 'image-details', 5148 toolbar: 'image-details', 5149 type: 'link', 5150 title: l10n.imageDetailsTitle, 5151 priority: 120 5152 }, 5153 5154 initialize: function( options ) { 5155 this.image = new wp.media.model.PostImage( options.metadata ); 5156 this.options.selection = new wp.media.model.Selection( this.image.attachment, { multiple: false } ); 5157 Select.prototype.initialize.apply( this, arguments ); 5158 }, 5260 Select = Toolbar.extend(/** @lends wp.media.view.Toolbar.Select.prototype */{ 5261 initialize: function() { 5262 var options = this.options; 5159 5263 5160 bindHandlers: function() { 5161 Select.prototype.bindHandlers.apply( this, arguments ); 5162 this.on( 'menu:create:image-details', this.createMenu, this ); 5163 this.on( 'content:create:image-details', this.imageDetailsContent, this ); 5164 this.on( 'content:render:edit-image', this.editImageContent, this ); 5165 this.on( 'toolbar:render:image-details', this.renderImageDetailsToolbar, this ); 5166 // override the select toolbar 5167 this.on( 'toolbar:render:replace', this.renderReplaceImageToolbar, this ); 5168 }, 5264 _.bindAll( this, 'clickSelect' ); 5169 5265 5170 createStates: function() { 5171 this.states.add([ 5172 new wp.media.controller.ImageDetails({ 5173 image: this.image, 5174 editable: false 5175 }), 5176 new wp.media.controller.ReplaceImage({ 5177 id: 'replace-image', 5178 library: wp.media.query( { type: 'image' } ), 5179 image: this.image, 5180 multiple: false, 5181 title: l10n.imageReplaceTitle, 5182 toolbar: 'replace', 5183 priority: 80, 5184 displaySettings: true 5185 }), 5186 new wp.media.controller.EditImage( { 5187 image: this.image, 5188 selection: this.options.selection 5189 } ) 5190 ]); 5191 }, 5266 _.defaults( options, { 5267 event: 'select', 5268 state: false, 5269 reset: true, 5270 close: true, 5271 text: l10n.select, 5192 5272 5193 imageDetailsContent: function( options ) { 5194 options.view = new wp.media.view.ImageDetails({ 5195 controller: this, 5196 model: this.state().image, 5197 attachment: this.state().image.attachment 5273 // Does the button rely on the selection? 5274 requires: { 5275 selection: true 5276 } 5198 5277 }); 5199 },5200 5201 editImageContent: function() {5202 var state = this.state(),5203 model = state.get('image'),5204 view;5205 5206 if ( ! model ) {5207 return;5208 }5209 5210 view = new wp.media.view.EditImage( { model: model, controller: this } ).render();5211 5212 this.content.set( view );5213 5214 // after bringing in the frame, load the actual editor via an ajax call5215 view.loadEditor();5216 5217 },5218 5219 renderImageDetailsToolbar: function() {5220 this.toolbar.set( new wp.media.view.Toolbar({5221 controller: this,5222 items: {5223 select: {5224 style: 'primary',5225 text: l10n.update,5226 priority: 80,5227 5228 click: function() {5229 var controller = this.controller,5230 state = controller.state();5231 5232 controller.close();5233 5278 5234 // not sure if we want to use wp.media.string.image which will create a shortcode or 5235 // perhaps wp.html.string to at least to build the <img /> 5236 state.trigger( 'update', controller.image.toJSON() ); 5237 5238 // Restore and reset the default state. 5239 controller.setState( controller.options.state ); 5240 controller.reset(); 5241 } 5242 } 5279 options.items = _.defaults( options.items || {}, { 5280 select: { 5281 style: 'primary', 5282 text: options.text, 5283 priority: 80, 5284 click: this.clickSelect, 5285 requires: options.requires 5243 5286 } 5244 }) ); 5287 }); 5288 // Call 'initialize' directly on the parent class. 5289 Toolbar.prototype.initialize.apply( this, arguments ); 5245 5290 }, 5246 5291 5247 renderReplaceImageToolbar: function() { 5248 var frame = this, 5249 lastState = frame.lastState(), 5250 previous = lastState && lastState.id; 5251 5252 this.toolbar.set( new wp.media.view.Toolbar({ 5253 controller: this, 5254 items: { 5255 back: { 5256 text: l10n.back, 5257 priority: 20, 5258 click: function() { 5259 if ( previous ) { 5260 frame.setState( previous ); 5261 } else { 5262 frame.close(); 5263 } 5264 } 5265 }, 5266 5267 replace: { 5268 style: 'primary', 5269 text: l10n.replace, 5270 priority: 80, 5271 requires: { selection: true }, 5272 5273 click: function() { 5274 var controller = this.controller, 5275 state = controller.state(), 5276 selection = state.get( 'selection' ), 5277 attachment = selection.single(); 5292 clickSelect: function() { 5293 var options = this.options, 5294 controller = this.controller; 5278 5295 5279 controller.close(); 5296 if ( options.close ) { 5297 controller.close(); 5298 } 5280 5299 5281 controller.image.changeAttachment( attachment, state.display( attachment ) ); 5300 if ( options.event ) { 5301 controller.state().trigger( options.event ); 5302 } 5282 5303 5283 // not sure if we want to use wp.media.string.image which will create a shortcode or5284 // perhaps wp.html.string to at least to build the <img />5285 state.trigger( 'replace', controller.image.toJSON() );5304 if ( options.state ) { 5305 controller.setState( options.state ); 5306 } 5286 5307 5287 // Restore and reset the default state. 5288 controller.setState( controller.options.state ); 5289 controller.reset(); 5290 } 5291 } 5292 } 5293 }) ); 5308 if ( options.reset ) { 5309 controller.reset(); 5310 } 5294 5311 } 5295 5296 5312 }); 5297 5313 5298 module.exports = ImageDetails;5314 module.exports = Select; 5299 5315 5300 },{}],45:[function(require,module,exports){ 5301 var Select = wp.media.view.MediaFrame.Select, 5302 Library = wp.media.controller.Library, 5316 5317 /***/ }), 5318 /* 60 */ 5319 /***/ (function(module, exports) { 5320 5321 var Select = wp.media.view.Toolbar.Select, 5303 5322 l10n = wp.media.view.l10n, 5304 Post;5323 Embed; 5305 5324 5306 5325 /** 5307 * wp.media.view.MediaFrame.Post 5308 * 5309 * The frame for manipulating media on the Edit Post page. 5326 * wp.media.view.Toolbar.Embed 5310 5327 * 5311 * @memberOf wp.media.view. MediaFrame5328 * @memberOf wp.media.view.Toolbar 5312 5329 * 5313 5330 * @class 5314 * @augments wp.media.view.MediaFrame.Select 5315 * @augments wp.media.view.MediaFrame 5316 * @augments wp.media.view.Frame 5331 * @augments wp.media.view.Toolbar.Select 5332 * @augments wp.media.view.Toolbar 5317 5333 * @augments wp.media.View 5318 5334 * @augments wp.Backbone.View 5319 5335 * @augments Backbone.View 5320 * @mixes wp.media.controller.StateMachine5321 5336 */ 5322 Post = Select.extend(/** @lends wp.media.view.MediaFrame.Post.prototype */{5337 Embed = Select.extend(/** @lends wp.media.view.Toolbar.Embed.prototype */{ 5323 5338 initialize: function() { 5324 this.counts = {5325 audio: {5326 count: wp.media.view.settings.attachmentCounts.audio,5327 state: 'playlist'5328 },5329 video: {5330 count: wp.media.view.settings.attachmentCounts.video,5331 state: 'video-playlist'5332 }5333 };5334 5335 5339 _.defaults( this.options, { 5336 multiple: true, 5337 editing: false, 5338 state: 'insert', 5339 metadata: {} 5340 text: l10n.insertIntoPost, 5341 requires: false 5340 5342 }); 5341 5342 5343 // Call 'initialize' directly on the parent class. 5343 5344 Select.prototype.initialize.apply( this, arguments ); 5344 this.createIframeStates(); 5345 }, 5346 5347 refresh: function() { 5348 var url = this.controller.state().props.get('url'); 5349 this.get('select').model.set( 'disabled', ! url || url === 'http://' ); 5350 /** 5351 * call 'refresh' directly on the parent class 5352 */ 5353 Select.prototype.refresh.apply( this, arguments ); 5354 } 5355 }); 5356 5357 module.exports = Embed; 5358 5359 5360 /***/ }), 5361 /* 61 */ 5362 /***/ (function(module, exports) { 5345 5363 5364 /** 5365 * wp.media.view.Button 5366 * 5367 * @memberOf wp.media.view 5368 * 5369 * @class 5370 * @augments wp.media.View 5371 * @augments wp.Backbone.View 5372 * @augments Backbone.View 5373 */ 5374 var Button = wp.media.View.extend(/** @lends wp.media.view.Button.prototype */{ 5375 tagName: 'button', 5376 className: 'media-button', 5377 attributes: { type: 'button' }, 5378 5379 events: { 5380 'click': 'click' 5346 5381 }, 5347 5382 5348 /** 5349 * Create the default states. 5350 */ 5351 createStates: function() { 5352 var options = this.options; 5383 defaults: { 5384 text: '', 5385 style: '', 5386 size: 'large', 5387 disabled: false 5388 }, 5353 5389 5354 this.states.add([ 5355 // Main states. 5356 new Library({ 5357 id: 'insert', 5358 title: l10n.insertMediaTitle, 5359 priority: 20, 5360 toolbar: 'main-insert', 5361 filterable: 'all', 5362 library: wp.media.query( options.library ), 5363 multiple: options.multiple ? 'reset' : false, 5364 editable: true, 5390 initialize: function() { 5391 /** 5392 * Create a model with the provided `defaults`. 5393 * 5394 * @member {Backbone.Model} 5395 */ 5396 this.model = new Backbone.Model( this.defaults ); 5365 5397 5366 // If the user isn't allowed to edit fields, 5367 // can they still edit it locally? 5368 allowLocalEdits: true, 5398 // If any of the `options` have a key from `defaults`, apply its 5399 // value to the `model` and remove it from the `options object. 5400 _.each( this.defaults, function( def, key ) { 5401 var value = this.options[ key ]; 5402 if ( _.isUndefined( value ) ) { 5403 return; 5404 } 5369 5405 5370 // Show the attachment display settings. 5371 displaySettings: true, 5372 // Update user settings when users adjust the 5373 // attachment display settings. 5374 displayUserSettings: true 5375 }), 5406 this.model.set( key, value ); 5407 delete this.options[ key ]; 5408 }, this ); 5376 5409 5377 new Library({5378 id: 'gallery',5379 title: l10n.createGalleryTitle,5380 priority: 40,5381 toolbar: 'main-gallery',5382 filterable: 'uploaded',5383 multiple: 'add',5384 editable: false,5410 this.listenTo( this.model, 'change', this.render ); 5411 }, 5412 /** 5413 * @returns {wp.media.view.Button} Returns itself to allow chaining 5414 */ 5415 render: function() { 5416 var classes = [ 'button', this.className ], 5417 model = this.model.toJSON(); 5385 5418 5386 library: wp.media.query( _.defaults({ 5387 type: 'image' 5388 }, options.library ) ) 5389 }), 5419 if ( model.style ) { 5420 classes.push( 'button-' + model.style ); 5421 } 5390 5422 5391 // Embed states. 5392 new wp.media.controller.Embed( { metadata: options.metadata } ), 5423 if ( model.size ) { 5424 classes.push( 'button-' + model.size ); 5425 } 5393 5426 5394 new wp.media.controller.EditImage( { model: options.editImage } ), 5427 classes = _.uniq( classes.concat( this.options.classes ) ); 5428 this.el.className = classes.join(' '); 5395 5429 5396 // Gallery states. 5397 new wp.media.controller.GalleryEdit({ 5398 library: options.selection, 5399 editing: options.editing, 5400 menu: 'gallery' 5401 }), 5430 this.$el.attr( 'disabled', model.disabled ); 5431 this.$el.text( this.model.get('text') ); 5402 5432 5403 new wp.media.controller.GalleryAdd(), 5433 return this; 5434 }, 5435 /** 5436 * @param {Object} event 5437 */ 5438 click: function( event ) { 5439 if ( '#' === this.attributes.href ) { 5440 event.preventDefault(); 5441 } 5404 5442 5405 new Library({ 5406 id: 'playlist', 5407 title: l10n.createPlaylistTitle, 5408 priority: 60, 5409 toolbar: 'main-playlist', 5410 filterable: 'uploaded', 5411 multiple: 'add', 5412 editable: false, 5443 if ( this.options.click && ! this.model.get('disabled') ) { 5444 this.options.click.apply( this, arguments ); 5445 } 5446 } 5447 }); 5413 5448 5414 library: wp.media.query( _.defaults({ 5415 type: 'audio' 5416 }, options.library ) ) 5417 }), 5449 module.exports = Button; 5418 5450 5419 // Playlist states.5420 new wp.media.controller.CollectionEdit({5421 type: 'audio',5422 collectionType: 'playlist',5423 title: l10n.editPlaylistTitle,5424 SettingsView: wp.media.view.Settings.Playlist,5425 library: options.selection,5426 editing: options.editing,5427 menu: 'playlist',5428 dragInfoText: l10n.playlistDragInfo,5429 dragInfo: false5430 }),5431 5451 5432 new wp.media.controller.CollectionAdd({ 5433 type: 'audio', 5434 collectionType: 'playlist', 5435 title: l10n.addToPlaylistTitle 5436 }), 5452 /***/ }), 5453 /* 62 */ 5454 /***/ (function(module, exports) { 5437 5455 5438 new Library({ 5439 id: 'video-playlist', 5440 title: l10n.createVideoPlaylistTitle, 5441 priority: 60, 5442 toolbar: 'main-video-playlist', 5443 filterable: 'uploaded', 5444 multiple: 'add', 5445 editable: false, 5456 var $ = Backbone.$, 5457 ButtonGroup; 5446 5458 5447 library: wp.media.query( _.defaults({ 5448 type: 'video' 5449 }, options.library ) ) 5450 }), 5459 /** 5460 * wp.media.view.ButtonGroup 5461 * 5462 * @memberOf wp.media.view 5463 * 5464 * @class 5465 * @augments wp.media.View 5466 * @augments wp.Backbone.View 5467 * @augments Backbone.View 5468 */ 5469 ButtonGroup = wp.media.View.extend(/** @lends wp.media.view.ButtonGroup.prototype */{ 5470 tagName: 'div', 5471 className: 'button-group button-large media-button-group', 5451 5472 5452 new wp.media.controller.CollectionEdit({5453 type: 'video',5454 collectionType: 'playlist',5455 title: l10n.editVideoPlaylistTitle,5456 SettingsView: wp.media.view.Settings.Playlist,5457 library: options.selection,5458 editing: options.editing,5459 menu: 'video-playlist',5460 dragInfoText: l10n.videoPlaylistDragInfo,5461 dragInfo: false5462 }),5473 initialize: function() { 5474 /** 5475 * @member {wp.media.view.Button[]} 5476 */ 5477 this.buttons = _.map( this.options.buttons || [], function( button ) { 5478 if ( button instanceof Backbone.View ) { 5479 return button; 5480 } else { 5481 return new wp.media.view.Button( button ).render(); 5482 } 5483 }); 5463 5484 5464 new wp.media.controller.CollectionAdd({ 5465 type: 'video', 5466 collectionType: 'playlist', 5467 title: l10n.addToVideoPlaylistTitle 5468 }) 5469 ]); 5485 delete this.options.buttons; 5470 5486 5471 if ( wp.media.view.settings.post.featuredImageId) {5472 this. states.add( new wp.media.controller.FeaturedImage());5487 if ( this.options.classes ) { 5488 this.$el.addClass( this.options.classes ); 5473 5489 } 5474 5490 }, 5475 5491 5476 bindHandlers: function() { 5477 var handlers, checkCounts; 5492 /** 5493 * @returns {wp.media.view.ButtonGroup} 5494 */ 5495 render: function() { 5496 this.$el.html( $( _.pluck( this.buttons, 'el' ) ).detach() ); 5497 return this; 5498 } 5499 }); 5478 5500 5479 Select.prototype.bindHandlers.apply( this, arguments );5501 module.exports = ButtonGroup; 5480 5502 5481 this.on( 'activate', this.activate, this );5482 5503 5483 // Only bother checking media type counts if one of the counts is zero 5484 checkCounts = _.find( this.counts, function( type ) { 5485 return type.count === 0; 5486 } ); 5504 /***/ }), 5505 /* 63 */ 5506 /***/ (function(module, exports) { 5487 5507 5488 if ( typeof checkCounts !== 'undefined' ) { 5489 this.listenTo( wp.media.model.Attachments.all, 'change:type', this.mediaTypeCounts ); 5490 } 5508 /** 5509 * wp.media.view.PriorityList 5510 * 5511 * @memberOf wp.media.view 5512 * 5513 * @class 5514 * @augments wp.media.View 5515 * @augments wp.Backbone.View 5516 * @augments Backbone.View 5517 */ 5518 var PriorityList = wp.media.View.extend(/** @lends wp.media.view.PriorityList.prototype */{ 5519 tagName: 'div', 5491 5520 5492 this.on( 'menu:create:gallery', this.createMenu, this ); 5493 this.on( 'menu:create:playlist', this.createMenu, this ); 5494 this.on( 'menu:create:video-playlist', this.createMenu, this ); 5495 this.on( 'toolbar:create:main-insert', this.createToolbar, this ); 5496 this.on( 'toolbar:create:main-gallery', this.createToolbar, this ); 5497 this.on( 'toolbar:create:main-playlist', this.createToolbar, this ); 5498 this.on( 'toolbar:create:main-video-playlist', this.createToolbar, this ); 5499 this.on( 'toolbar:create:featured-image', this.featuredImageToolbar, this ); 5500 this.on( 'toolbar:create:main-embed', this.mainEmbedToolbar, this ); 5521 initialize: function() { 5522 this._views = {}; 5501 5523 5502 handlers = { 5503 menu: { 5504 'default': 'mainMenu', 5505 'gallery': 'galleryMenu', 5506 'playlist': 'playlistMenu', 5507 'video-playlist': 'videoPlaylistMenu' 5508 }, 5524 this.set( _.extend( {}, this._views, this.options.views ), { silent: true }); 5525 delete this.options.views; 5509 5526 5510 content: { 5511 'embed': 'embedContent', 5512 'edit-image': 'editImageContent', 5513 'edit-selection': 'editSelectionContent' 5514 }, 5527 if ( ! this.options.silent ) { 5528 this.render(); 5529 } 5530 }, 5531 /** 5532 * @param {string} id 5533 * @param {wp.media.View|Object} view 5534 * @param {Object} options 5535 * @returns {wp.media.view.PriorityList} Returns itself to allow chaining 5536 */ 5537 set: function( id, view, options ) { 5538 var priority, views, index; 5515 5539 5516 toolbar: { 5517 'main-insert': 'mainInsertToolbar', 5518 'main-gallery': 'mainGalleryToolbar', 5519 'gallery-edit': 'galleryEditToolbar', 5520 'gallery-add': 'galleryAddToolbar', 5521 'main-playlist': 'mainPlaylistToolbar', 5522 'playlist-edit': 'playlistEditToolbar', 5523 'playlist-add': 'playlistAddToolbar', 5524 'main-video-playlist': 'mainVideoPlaylistToolbar', 5525 'video-playlist-edit': 'videoPlaylistEditToolbar', 5526 'video-playlist-add': 'videoPlaylistAddToolbar' 5527 } 5528 }; 5540 options = options || {}; 5529 5541 5530 _.each( handlers, function( regionHandlers, region ) { 5531 _.each( regionHandlers, function( callback, handler ) { 5532 this.on( region + ':render:' + handler, this[ callback ], this ); 5542 // Accept an object with an `id` : `view` mapping. 5543 if ( _.isObject( id ) ) { 5544 _.each( id, function( view, id ) { 5545 this.set( id, view ); 5533 5546 }, this ); 5534 }, this );5535 },5547 return this; 5548 } 5536 5549 5537 activate: function() { 5538 // Hide menu items for states tied to particular media types if there are no items 5539 _.each( this.counts, function( type ) { 5540 if ( type.count < 1 ) { 5541 this.menuItemVisibility( type.state, 'hide' ); 5550 if ( ! (view instanceof Backbone.View) ) { 5551 view = this.toView( view, id, options ); 5552 } 5553 view.controller = view.controller || this.controller; 5554 5555 this.unset( id ); 5556 5557 priority = view.options.priority || 10; 5558 views = this.views.get() || []; 5559 5560 _.find( views, function( existing, i ) { 5561 if ( existing.options.priority > priority ) { 5562 index = i; 5563 return true; 5542 5564 } 5543 }, this ); 5544 }, 5565 }); 5545 5566 5546 mediaTypeCounts: function( model, attr ) { 5547 if ( typeof this.counts[ attr ] !== 'undefined' && this.counts[ attr ].count < 1 ) { 5548 this.counts[ attr ].count++; 5549 this.menuItemVisibility( this.counts[ attr ].state, 'show' ); 5550 } 5551 }, 5567 this._views[ id ] = view; 5568 this.views.add( view, { 5569 at: _.isNumber( index ) ? index : views.length || 0 5570 }); 5552 5571 5553 // Menus 5572 return this; 5573 }, 5554 5574 /** 5555 * @param {wp.Backbone.View} view 5575 * @param {string} id 5576 * @returns {wp.media.View} 5556 5577 */ 5557 mainMenu: function( view ) { 5558 view.set({ 5559 'library-separator': new wp.media.View({ 5560 className: 'separator', 5561 priority: 100 5562 }) 5563 }); 5578 get: function( id ) { 5579 return this._views[ id ]; 5564 5580 }, 5581 /** 5582 * @param {string} id 5583 * @returns {wp.media.view.PriorityList} 5584 */ 5585 unset: function( id ) { 5586 var view = this.get( id ); 5565 5587 5566 menuItemVisibility: function( state, visibility ) { 5567 var menu = this.menu.get(); 5568 if ( visibility === 'hide' ) { 5569 menu.hide( state ); 5570 } else if ( visibility === 'show' ) { 5571 menu.show( state ); 5588 if ( view ) { 5589 view.remove(); 5572 5590 } 5591 5592 delete this._views[ id ]; 5593 return this; 5573 5594 }, 5574 5595 /** 5575 * @param {wp.Backbone.View} view 5596 * @param {Object} options 5597 * @returns {wp.media.View} 5576 5598 */ 5577 galleryMenu: function( view) {5578 var lastState = this.lastState(),5579 previous = lastState && lastState.id,5580 frame = this;5599 toView: function( options ) { 5600 return new wp.media.View( options ); 5601 } 5602 }); 5581 5603 5582 view.set({ 5583 cancel: { 5584 text: l10n.cancelGalleryTitle, 5585 priority: 20, 5586 click: function() { 5587 if ( previous ) { 5588 frame.setState( previous ); 5589 } else { 5590 frame.close(); 5591 } 5604 module.exports = PriorityList; 5592 5605 5593 // Keep focus inside media modal5594 // after canceling a gallery5595 this.controller.modal.focusManager.focus();5596 }5597 },5598 separateCancel: new wp.media.View({5599 className: 'separator',5600 priority: 405601 })5602 });5603 },5604 5606 5605 playlistMenu: function( view ) { 5606 var lastState = this.lastState(), 5607 previous = lastState && lastState.id, 5608 frame = this; 5607 /***/ }), 5608 /* 64 */ 5609 /***/ (function(module, exports) { 5609 5610 5610 view.set({ 5611 cancel: { 5612 text: l10n.cancelPlaylistTitle, 5613 priority: 20, 5614 click: function() { 5615 if ( previous ) { 5616 frame.setState( previous ); 5617 } else { 5618 frame.close(); 5619 } 5620 } 5621 }, 5622 separateCancel: new wp.media.View({ 5623 className: 'separator', 5624 priority: 40 5625 }) 5626 }); 5627 }, 5611 var $ = jQuery, 5612 MenuItem; 5628 5613 5629 videoPlaylistMenu: function( view ) { 5630 var lastState = this.lastState(), 5631 previous = lastState && lastState.id, 5632 frame = this; 5614 /** 5615 * wp.media.view.MenuItem 5616 * 5617 * @memberOf wp.media.view 5618 * 5619 * @class 5620 * @augments wp.media.View 5621 * @augments wp.Backbone.View 5622 * @augments Backbone.View 5623 */ 5624 MenuItem = wp.media.View.extend(/** @lends wp.media.view.MenuItem.prototype */{ 5625 tagName: 'a', 5626 className: 'media-menu-item', 5633 5627 5634 view.set({ 5635 cancel: { 5636 text: l10n.cancelVideoPlaylistTitle, 5637 priority: 20, 5638 click: function() { 5639 if ( previous ) { 5640 frame.setState( previous ); 5641 } else { 5642 frame.close(); 5643 } 5644 } 5645 }, 5646 separateCancel: new wp.media.View({ 5647 className: 'separator', 5648 priority: 40 5649 }) 5650 }); 5628 attributes: { 5629 href: '#' 5630 }, 5631 5632 events: { 5633 'click': '_click' 5651 5634 }, 5635 /** 5636 * @param {Object} event 5637 */ 5638 _click: function( event ) { 5639 var clickOverride = this.options.click; 5652 5640 5653 // Content 5654 embedContent: function() { 5655 var view = new wp.media.view.Embed({ 5656 controller: this, 5657 model: this.state() 5658 }).render(); 5641 if ( event ) { 5642 event.preventDefault(); 5643 } 5659 5644 5660 this.content.set( view ); 5645 if ( clickOverride ) { 5646 clickOverride.call( this ); 5647 } else { 5648 this.click(); 5649 } 5661 5650 5651 // When selecting a tab along the left side, 5652 // focus should be transferred into the main panel 5662 5653 if ( ! wp.media.isTouchDevice ) { 5663 view.url.focus();5654 $('.media-frame-content input').first().focus(); 5664 5655 } 5665 5656 }, 5666 5657 5667 editSelectionContent: function() { 5668 var state = this.state(), 5669 selection = state.get('selection'), 5670 view; 5671 5672 view = new wp.media.view.AttachmentsBrowser({ 5673 controller: this, 5674 collection: selection, 5675 selection: selection, 5676 model: state, 5677 sortable: true, 5678 search: false, 5679 date: false, 5680 dragInfo: true, 5658 click: function() { 5659 var state = this.options.state; 5681 5660 5682 AttachmentView: wp.media.view.Attachments.EditSelection 5683 }).render(); 5661 if ( state ) { 5662 this.controller.setState( state ); 5663 this.views.parent.$el.removeClass( 'visible' ); // TODO: or hide on any click, see below 5664 } 5665 }, 5666 /** 5667 * @returns {wp.media.view.MenuItem} returns itself to allow chaining 5668 */ 5669 render: function() { 5670 var options = this.options; 5684 5671 5685 view.toolbar.set( 'backToLibrary', { 5686 text: l10n.returnToLibrary, 5687 priority: -100, 5672 if ( options.text ) { 5673 this.$el.text( options.text ); 5674 } else if ( options.html ) { 5675 this.$el.html( options.html ); 5676 } 5688 5677 5689 click: function() { 5690 this.controller.content.mode('browse'); 5691 } 5692 }); 5678 return this; 5679 } 5680 }); 5693 5681 5694 // Browse our library of attachments. 5695 this.content.set( view ); 5682 module.exports = MenuItem; 5696 5683 5697 // Trigger the controller to set focus5698 this.trigger( 'edit:selection', this );5699 },5700 5684 5701 editImageContent: function() { 5702 var image = this.state().get('image'), 5703 view = new wp.media.view.EditImage( { model: image, controller: this } ).render(); 5685 /***/ }), 5686 /* 65 */ 5687 /***/ (function(module, exports) { 5704 5688 5705 this.content.set( view ); 5689 var MenuItem = wp.media.view.MenuItem, 5690 PriorityList = wp.media.view.PriorityList, 5691 Menu; 5706 5692 5707 // after creating the wrapper view, load the actual editor via an ajax call 5708 view.loadEditor(); 5693 /** 5694 * wp.media.view.Menu 5695 * 5696 * @memberOf wp.media.view 5697 * 5698 * @class 5699 * @augments wp.media.view.PriorityList 5700 * @augments wp.media.View 5701 * @augments wp.Backbone.View 5702 * @augments Backbone.View 5703 */ 5704 Menu = PriorityList.extend(/** @lends wp.media.view.Menu.prototype */{ 5705 tagName: 'div', 5706 className: 'media-menu', 5707 property: 'state', 5708 ItemView: MenuItem, 5709 region: 'menu', 5709 5710 5711 /* TODO: alternatively hide on any click anywhere 5712 events: { 5713 'click': 'click' 5710 5714 }, 5711 5715 5712 // Toolbars 5716 click: function() { 5717 this.$el.removeClass( 'visible' ); 5718 }, 5719 */ 5713 5720 5714 5721 /** 5715 * @param {wp.Backbone.View} view 5722 * @param {Object} options 5723 * @param {string} id 5724 * @returns {wp.media.View} 5716 5725 */ 5717 selectionStatusToolbar: function( view ) { 5718 var editable = this.state().get('editable'); 5726 toView: function( options, id ) { 5727 options = options || {}; 5728 options[ this.property ] = options[ this.property ] || id; 5729 return new this.ItemView( options ).render(); 5730 }, 5719 5731 5720 view.set( 'selection', new wp.media.view.Selection({ 5721 controller: this, 5722 collection: this.state().get('selection'), 5723 priority: -40, 5732 ready: function() { 5733 /** 5734 * call 'ready' directly on the parent class 5735 */ 5736 PriorityList.prototype.ready.apply( this, arguments ); 5737 this.visibility(); 5738 }, 5724 5739 5725 // If the selection is editable, pass the callback to 5726 // switch the content mode. 5727 editable: editable && function() { 5728 this.controller.content.mode('edit-selection'); 5729 } 5730 }).render() ); 5740 set: function() { 5741 /** 5742 * call 'set' directly on the parent class 5743 */ 5744 PriorityList.prototype.set.apply( this, arguments ); 5745 this.visibility(); 5746 }, 5747 5748 unset: function() { 5749 /** 5750 * call 'unset' directly on the parent class 5751 */ 5752 PriorityList.prototype.unset.apply( this, arguments ); 5753 this.visibility(); 5731 5754 }, 5732 5755 5756 visibility: function() { 5757 var region = this.region, 5758 view = this.controller[ region ].get(), 5759 views = this.views.get(), 5760 hide = ! views || views.length < 2; 5761 5762 if ( this === view ) { 5763 this.controller.$el.toggleClass( 'hide-' + region, hide ); 5764 } 5765 }, 5733 5766 /** 5734 * @param { wp.Backbone.View} view5767 * @param {string} id 5735 5768 */ 5736 mainInsertToolbar: function( view) {5737 var controller = this;5769 select: function( id ) { 5770 var view = this.get( id ); 5738 5771 5739 this.selectionStatusToolbar( view ); 5772 if ( ! view ) { 5773 return; 5774 } 5740 5775 5741 view.set( 'insert', { 5742 style: 'primary', 5743 priority: 80, 5744 text: l10n.insertIntoPost, 5745 requires: { selection: true }, 5776 this.deselect(); 5777 view.$el.addClass('active'); 5778 }, 5746 5779 5747 /** 5748 * @callback 5749 * @fires wp.media.controller.State#insert 5750 */ 5751 click: function() { 5752 var state = controller.state(), 5753 selection = state.get('selection'); 5780 deselect: function() { 5781 this.$el.children().removeClass('active'); 5782 }, 5754 5783 5755 controller.close(); 5756 state.trigger( 'insert', selection ).reset(); 5757 } 5758 }); 5784 hide: function( id ) { 5785 var view = this.get( id ); 5786 5787 if ( ! view ) { 5788 return; 5789 } 5790 5791 view.$el.addClass('hidden'); 5759 5792 }, 5760 5793 5794 show: function( id ) { 5795 var view = this.get( id ); 5796 5797 if ( ! view ) { 5798 return; 5799 } 5800 5801 view.$el.removeClass('hidden'); 5802 } 5803 }); 5804 5805 module.exports = Menu; 5806 5807 5808 /***/ }), 5809 /* 66 */ 5810 /***/ (function(module, exports) { 5811 5812 /** 5813 * wp.media.view.RouterItem 5814 * 5815 * @memberOf wp.media.view 5816 * 5817 * @class 5818 * @augments wp.media.view.MenuItem 5819 * @augments wp.media.View 5820 * @augments wp.Backbone.View 5821 * @augments Backbone.View 5822 */ 5823 var RouterItem = wp.media.view.MenuItem.extend(/** @lends wp.media.view.RouterItem.prototype */{ 5761 5824 /** 5762 * @param {wp.Backbone.View} view5825 * On click handler to activate the content region's corresponding mode. 5763 5826 */ 5764 mainGalleryToolbar: function( view ) { 5765 var controller = this; 5827 click: function() { 5828 var contentMode = this.options.contentMode; 5829 if ( contentMode ) { 5830 this.controller.content.mode( contentMode ); 5831 } 5832 } 5833 }); 5766 5834 5767 this.selectionStatusToolbar( view ); 5835 module.exports = RouterItem; 5836 5837 5838 /***/ }), 5839 /* 67 */ 5840 /***/ (function(module, exports) { 5841 5842 var Menu = wp.media.view.Menu, 5843 Router; 5844 5845 /** 5846 * wp.media.view.Router 5847 * 5848 * @memberOf wp.media.view 5849 * 5850 * @class 5851 * @augments wp.media.view.Menu 5852 * @augments wp.media.view.PriorityList 5853 * @augments wp.media.View 5854 * @augments wp.Backbone.View 5855 * @augments Backbone.View 5856 */ 5857 Router = Menu.extend(/** @lends wp.media.view.Router.prototype */{ 5858 tagName: 'div', 5859 className: 'media-router', 5860 property: 'contentMode', 5861 ItemView: wp.media.view.RouterItem, 5862 region: 'router', 5768 5863 5769 view.set( 'gallery',{5770 style: 'primary',5771 text: l10n.createNewGallery,5772 priority: 60,5773 requires: { selection: true},5864 initialize: function() { 5865 this.controller.on( 'content:render', this.update, this ); 5866 // Call 'initialize' directly on the parent class. 5867 Menu.prototype.initialize.apply( this, arguments ); 5868 }, 5774 5869 5775 click: function() { 5776 var selection = controller.state().get('selection'), 5777 edit = controller.state('gallery-edit'), 5778 models = selection.where({ type: 'image' }); 5870 update: function() { 5871 var mode = this.controller.content.mode(); 5872 if ( mode ) { 5873 this.select( mode ); 5874 } 5875 } 5876 }); 5779 5877 5780 edit.set( 'library', new wp.media.model.Selection( models, { 5781 props: selection.props.toJSON(), 5782 multiple: true 5783 }) ); 5878 module.exports = Router; 5784 5879 5785 this.controller.setState('gallery-edit');5786 5880 5787 // Keep focus inside media modal 5788 // after jumping to gallery view 5789 this.controller.modal.focusManager.focus(); 5790 } 5791 }); 5792 }, 5881 /***/ }), 5882 /* 68 */ 5883 /***/ (function(module, exports) { 5793 5884 5794 mainPlaylistToolbar: function( view ) { 5795 var controller = this; 5885 /** 5886 * wp.media.view.Sidebar 5887 * 5888 * @memberOf wp.media.view 5889 * 5890 * @class 5891 * @augments wp.media.view.PriorityList 5892 * @augments wp.media.View 5893 * @augments wp.Backbone.View 5894 * @augments Backbone.View 5895 */ 5896 var Sidebar = wp.media.view.PriorityList.extend(/** @lends wp.media.view.Sidebar.prototype */{ 5897 className: 'media-sidebar' 5898 }); 5796 5899 5797 this.selectionStatusToolbar( view );5900 module.exports = Sidebar; 5798 5901 5799 view.set( 'playlist', {5800 style: 'primary',5801 text: l10n.createNewPlaylist,5802 priority: 100,5803 requires: { selection: true },5804 5902 5805 click: function() { 5806 var selection = controller.state().get('selection'), 5807 edit = controller.state('playlist-edit'), 5808 models = selection.where({ type: 'audio' }); 5903 /***/ }), 5904 /* 69 */ 5905 /***/ (function(module, exports) { 5809 5906 5810 edit.set( 'library', new wp.media.model.Selection( models, { 5811 props: selection.props.toJSON(), 5812 multiple: true 5813 }) ); 5907 var View = wp.media.View, 5908 $ = jQuery, 5909 Attachment; 5814 5910 5815 this.controller.setState('playlist-edit'); 5911 /** 5912 * wp.media.view.Attachment 5913 * 5914 * @memberOf wp.media.view 5915 * 5916 * @class 5917 * @augments wp.media.View 5918 * @augments wp.Backbone.View 5919 * @augments Backbone.View 5920 */ 5921 Attachment = View.extend(/** @lends wp.media.view.Attachment.prototype */{ 5922 tagName: 'li', 5923 className: 'attachment', 5924 template: wp.template('attachment'), 5816 5925 5817 // Keep focus inside media modal 5818 // after jumping to playlist view 5819 this.controller.modal.focusManager.focus(); 5820 } 5821 }); 5926 attributes: function() { 5927 return { 5928 'tabIndex': 0, 5929 'role': 'checkbox', 5930 'aria-label': this.model.get( 'title' ), 5931 'aria-checked': false, 5932 'data-id': this.model.get( 'id' ) 5933 }; 5822 5934 }, 5823 5935 5824 mainVideoPlaylistToolbar: function( view ) { 5825 var controller = this; 5826 5827 this.selectionStatusToolbar( view ); 5936 events: { 5937 'click': 'toggleSelectionHandler', 5938 'change [data-setting]': 'updateSetting', 5939 'change [data-setting] input': 'updateSetting', 5940 'change [data-setting] select': 'updateSetting', 5941 'change [data-setting] textarea': 'updateSetting', 5942 'click .attachment-close': 'removeFromLibrary', 5943 'click .check': 'checkClickHandler', 5944 'keydown': 'toggleSelectionHandler' 5945 }, 5828 5946 5829 view.set( 'video-playlist', { 5830 style: 'primary', 5831 text: l10n.createNewVideoPlaylist, 5832 priority: 100, 5833 requires: { selection: true }, 5947 buttons: {}, 5834 5948 5835 click: function() { 5836 var selection = controller.state().get('selection'), 5837 edit = controller.state('video-playlist-edit'), 5838 models = selection.where({ type: 'video' }); 5949 initialize: function() { 5950 var selection = this.options.selection, 5951 options = _.defaults( this.options, { 5952 rerenderOnModelChange: true 5953 } ); 5839 5954 5840 edit.set( 'library', new wp.media.model.Selection( models, { 5841 props: selection.props.toJSON(), 5842 multiple: true 5843 }) ); 5955 if ( options.rerenderOnModelChange ) { 5956 this.listenTo( this.model, 'change', this.render ); 5957 } else { 5958 this.listenTo( this.model, 'change:percent', this.progress ); 5959 } 5960 this.listenTo( this.model, 'change:title', this._syncTitle ); 5961 this.listenTo( this.model, 'change:caption', this._syncCaption ); 5962 this.listenTo( this.model, 'change:artist', this._syncArtist ); 5963 this.listenTo( this.model, 'change:album', this._syncAlbum ); 5844 5964 5845 this.controller.setState('video-playlist-edit'); 5965 // Update the selection. 5966 this.listenTo( this.model, 'add', this.select ); 5967 this.listenTo( this.model, 'remove', this.deselect ); 5968 if ( selection ) { 5969 selection.on( 'reset', this.updateSelect, this ); 5970 // Update the model's details view. 5971 this.listenTo( this.model, 'selection:single selection:unsingle', this.details ); 5972 this.details( this.model, this.controller.state().get('selection') ); 5973 } 5846 5974 5847 // Keep focus inside media modal 5848 // after jumping to video playlist view 5849 this.controller.modal.focusManager.focus(); 5850 } 5851 }); 5975 this.listenTo( this.controller, 'attachment:compat:waiting attachment:compat:ready', this.updateSave ); 5852 5976 }, 5977 /** 5978 * @returns {wp.media.view.Attachment} Returns itself to allow chaining 5979 */ 5980 dispose: function() { 5981 var selection = this.options.selection; 5853 5982 5854 featuredImageToolbar: function( toolbar ) { 5855 this.createSelectToolbar( toolbar, { 5856 text: l10n.setFeaturedImage, 5857 state: this.options.state 5858 }); 5859 }, 5983 // Make sure all settings are saved before removing the view. 5984 this.updateAll(); 5860 5985 5861 mainEmbedToolbar: function( toolbar ) { 5862 toolbar.view = new wp.media.view.Toolbar.Embed({ 5863 controller: this 5864 }); 5986 if ( selection ) { 5987 selection.off( null, null, this ); 5988 } 5989 /** 5990 * call 'dispose' directly on the parent class 5991 */ 5992 View.prototype.dispose.apply( this, arguments ); 5993 return this; 5865 5994 }, 5995 /** 5996 * @returns {wp.media.view.Attachment} Returns itself to allow chaining 5997 */ 5998 render: function() { 5999 var options = _.defaults( this.model.toJSON(), { 6000 orientation: 'landscape', 6001 uploading: false, 6002 type: '', 6003 subtype: '', 6004 icon: '', 6005 filename: '', 6006 caption: '', 6007 title: '', 6008 dateFormatted: '', 6009 width: '', 6010 height: '', 6011 compat: false, 6012 alt: '', 6013 description: '' 6014 }, this.options ); 5866 6015 5867 galleryEditToolbar: function() { 5868 var editing = this.state().get('editing'); 5869 this.toolbar.set( new wp.media.view.Toolbar({ 5870 controller: this, 5871 items: { 5872 insert: { 5873 style: 'primary', 5874 text: editing ? l10n.updateGallery : l10n.insertGallery, 5875 priority: 80, 5876 requires: { library: true }, 6016 options.buttons = this.buttons; 6017 options.describe = this.controller.state().get('describe'); 5877 6018 5878 /** 5879 * @fires wp.media.controller.State#update 5880 */ 5881 click: function() { 5882 var controller = this.controller, 5883 state = controller.state(); 6019 if ( 'image' === options.type ) { 6020 options.size = this.imageSize(); 6021 } 5884 6022 5885 controller.close(); 5886 state.trigger( 'update', state.get('library') ); 6023 options.can = {}; 6024 if ( options.nonces ) { 6025 options.can.remove = !! options.nonces['delete']; 6026 options.can.save = !! options.nonces.update; 6027 } 5887 6028 5888 // Restore and reset the default state. 5889 controller.setState( controller.options.state ); 5890 controller.reset(); 5891 } 5892 } 5893 } 5894 }) ); 5895 }, 6029 if ( this.controller.state().get('allowLocalEdits') ) { 6030 options.allowLocalEdits = true; 6031 } 5896 6032 5897 galleryAddToolbar: function() { 5898 this.toolbar.set( new wp.media.view.Toolbar({ 5899 controller: this, 5900 items: { 5901 insert: { 5902 style: 'primary', 5903 text: l10n.addToGallery, 5904 priority: 80, 5905 requires: { selection: true }, 6033 if ( options.uploading && ! options.percent ) { 6034 options.percent = 0; 6035 } 5906 6036 5907 /** 5908 * @fires wp.media.controller.State#reset 5909 */ 5910 click: function() { 5911 var controller = this.controller, 5912 state = controller.state(), 5913 edit = controller.state('gallery-edit'); 6037 this.views.detach(); 6038 this.$el.html( this.template( options ) ); 5914 6039 5915 edit.get('library').add( state.get('selection').models ); 5916 state.trigger('reset'); 5917 controller.setState('gallery-edit'); 5918 } 5919 } 5920 } 5921 }) ); 5922 }, 6040 this.$el.toggleClass( 'uploading', options.uploading ); 5923 6041 5924 playlistEditToolbar: function() { 5925 var editing = this.state().get('editing'); 5926 this.toolbar.set( new wp.media.view.Toolbar({ 5927 controller: this, 5928 items: { 5929 insert: { 5930 style: 'primary', 5931 text: editing ? l10n.updatePlaylist : l10n.insertPlaylist, 5932 priority: 80, 5933 requires: { library: true }, 6042 if ( options.uploading ) { 6043 this.$bar = this.$('.media-progress-bar div'); 6044 } else { 6045 delete this.$bar; 6046 } 5934 6047 5935 /** 5936 * @fires wp.media.controller.State#update 5937 */ 5938 click: function() { 5939 var controller = this.controller, 5940 state = controller.state(); 6048 // Check if the model is selected. 6049 this.updateSelect(); 5941 6050 5942 controller.close();5943 state.trigger( 'update', state.get('library'));6051 // Update the save status. 6052 this.updateSave(); 5944 6053 5945 // Restore and reset the default state. 5946 controller.setState( controller.options.state ); 5947 controller.reset(); 5948 } 5949 } 5950 } 5951 }) ); 6054 this.views.render(); 6055 6056 return this; 5952 6057 }, 5953 6058 5954 playlistAddToolbar: function() { 5955 this.toolbar.set( new wp.media.view.Toolbar({ 5956 controller: this, 5957 items: { 5958 insert: { 5959 style: 'primary', 5960 text: l10n.addToPlaylist, 5961 priority: 80, 5962 requires: { selection: true }, 6059 progress: function() { 6060 if ( this.$bar && this.$bar.length ) { 6061 this.$bar.width( this.model.get('percent') + '%' ); 6062 } 6063 }, 5963 6064 5964 /** 5965 * @fires wp.media.controller.State#reset 5966 */ 5967 click: function() { 5968 var controller = this.controller, 5969 state = controller.state(), 5970 edit = controller.state('playlist-edit'); 6065 /** 6066 * @param {Object} event 6067 */ 6068 toggleSelectionHandler: function( event ) { 6069 var method; 5971 6070 5972 edit.get('library').add( state.get('selection').models ); 5973 state.trigger('reset'); 5974 controller.setState('playlist-edit'); 5975 } 5976 } 5977 } 5978 }) ); 5979 }, 6071 // Don't do anything inside inputs and on the attachment check and remove buttons. 6072 if ( 'INPUT' === event.target.nodeName || 'BUTTON' === event.target.nodeName ) { 6073 return; 6074 } 5980 6075 5981 videoPlaylistEditToolbar: function() { 5982 var editing = this.state().get('editing'); 5983 this.toolbar.set( new wp.media.view.Toolbar({ 5984 controller: this, 5985 items: { 5986 insert: { 5987 style: 'primary', 5988 text: editing ? l10n.updateVideoPlaylist : l10n.insertVideoPlaylist, 5989 priority: 140, 5990 requires: { library: true }, 6076 // Catch arrow events 6077 if ( 37 === event.keyCode || 38 === event.keyCode || 39 === event.keyCode || 40 === event.keyCode ) { 6078 this.controller.trigger( 'attachment:keydown:arrow', event ); 6079 return; 6080 } 5991 6081 5992 click: function() {5993 var controller = this.controller,5994 state = controller.state(),5995 library = state.get('library');6082 // Catch enter and space events 6083 if ( 'keydown' === event.type && 13 !== event.keyCode && 32 !== event.keyCode ) { 6084 return; 6085 } 5996 6086 5997 library.type = 'video';6087 event.preventDefault(); 5998 6088 5999 controller.close(); 6000 state.trigger( 'update', library ); 6089 // In the grid view, bubble up an edit:attachment event to the controller. 6090 if ( this.controller.isModeActive( 'grid' ) ) { 6091 if ( this.controller.isModeActive( 'edit' ) ) { 6092 // Pass the current target to restore focus when closing 6093 this.controller.trigger( 'edit:attachment', this.model, event.currentTarget ); 6094 return; 6095 } 6001 6096 6002 // Restore and reset the default state. 6003 controller.setState( controller.options.state ); 6004 controller.reset(); 6005 } 6006 } 6097 if ( this.controller.isModeActive( 'select' ) ) { 6098 method = 'toggle'; 6007 6099 } 6008 }) ); 6100 } 6101 6102 if ( event.shiftKey ) { 6103 method = 'between'; 6104 } else if ( event.ctrlKey || event.metaKey ) { 6105 method = 'toggle'; 6106 } 6107 6108 this.toggleSelection({ 6109 method: method 6110 }); 6111 6112 this.controller.trigger( 'selection:toggle' ); 6009 6113 }, 6114 /** 6115 * @param {Object} options 6116 */ 6117 toggleSelection: function( options ) { 6118 var collection = this.collection, 6119 selection = this.options.selection, 6120 model = this.model, 6121 method = options && options.method, 6122 single, models, singleIndex, modelIndex; 6010 6123 6011 videoPlaylistAddToolbar: function() { 6012 this.toolbar.set( new wp.media.view.Toolbar({ 6013 controller: this, 6014 items: { 6015 insert: { 6016 style: 'primary', 6017 text: l10n.addToVideoPlaylist, 6018 priority: 140, 6019 requires: { selection: true }, 6124 if ( ! selection ) { 6125 return; 6126 } 6020 6127 6021 click: function() { 6022 var controller = this.controller, 6023 state = controller.state(), 6024 edit = controller.state('video-playlist-edit'); 6128 single = selection.single(); 6129 method = _.isUndefined( method ) ? selection.multiple : method; 6025 6130 6026 edit.get('library').add( state.get('selection').models ); 6027 state.trigger('reset'); 6028 controller.setState('video-playlist-edit'); 6029 } 6030 } 6131 // If the `method` is set to `between`, select all models that 6132 // exist between the current and the selected model. 6133 if ( 'between' === method && single && selection.multiple ) { 6134 // If the models are the same, short-circuit. 6135 if ( single === model ) { 6136 return; 6031 6137 } 6032 }) );6033 }6034 });6035 6138 6036 module.exports = Post; 6139 singleIndex = collection.indexOf( single ); 6140 modelIndex = collection.indexOf( this.model ); 6037 6141 6038 },{}],46:[function(require,module,exports){ 6039 var MediaFrame = wp.media.view.MediaFrame, 6040 l10n = wp.media.view.l10n, 6041 Select; 6142 if ( singleIndex < modelIndex ) { 6143 models = collection.models.slice( singleIndex, modelIndex + 1 ); 6144 } else { 6145 models = collection.models.slice( modelIndex, singleIndex + 1 ); 6146 } 6042 6147 6043 /** 6044 * wp.media.view.MediaFrame.Select 6045 * 6046 * A frame for selecting an item or items from the media library. 6047 * 6048 * @memberOf wp.media.view.MediaFrame 6049 * 6050 * @class 6051 * @augments wp.media.view.MediaFrame 6052 * @augments wp.media.view.Frame 6053 * @augments wp.media.View 6054 * @augments wp.Backbone.View 6055 * @augments Backbone.View 6056 * @mixes wp.media.controller.StateMachine 6057 */ 6058 Select = MediaFrame.extend(/** @lends wp.media.view.MediaFrame.Select.prototype */{ 6059 initialize: function() { 6060 // Call 'initialize' directly on the parent class. 6061 MediaFrame.prototype.initialize.apply( this, arguments ); 6148 selection.add( models ); 6149 selection.single( model ); 6150 return; 6062 6151 6063 _.defaults( this.options, { 6064 selection: [], 6065 library: {}, 6066 multiple: false, 6067 state: 'library' 6068 }); 6152 // If the `method` is set to `toggle`, just flip the selection 6153 // status, regardless of whether the model is the single model. 6154 } else if ( 'toggle' === method ) { 6155 selection[ this.selected() ? 'remove' : 'add' ]( model ); 6156 selection.single( model ); 6157 return; 6158 } else if ( 'add' === method ) { 6159 selection.add( model ); 6160 selection.single( model ); 6161 return; 6162 } 6069 6163 6070 this.createSelection(); 6071 this.createStates(); 6072 this.bindHandlers(); 6164 // Fixes bug that loses focus when selecting a featured image 6165 if ( ! method ) { 6166 method = 'add'; 6167 } 6168 6169 if ( method !== 'add' ) { 6170 method = 'reset'; 6171 } 6172 6173 if ( this.selected() ) { 6174 // If the model is the single model, remove it. 6175 // If it is not the same as the single model, 6176 // it now becomes the single model. 6177 selection[ single === model ? 'remove' : 'single' ]( model ); 6178 } else { 6179 // If the model is not selected, run the `method` on the 6180 // selection. By default, we `reset` the selection, but the 6181 // `method` can be set to `add` the model to the selection. 6182 selection[ method ]( model ); 6183 selection.single( model ); 6184 } 6073 6185 }, 6074 6186 6187 updateSelect: function() { 6188 this[ this.selected() ? 'select' : 'deselect' ](); 6189 }, 6075 6190 /** 6076 * Attach a selection collection to the frame. 6077 * 6078 * A selection is a collection of attachments used for a specific purpose 6079 * by a media frame. e.g. Selecting an attachment (or many) to insert into 6080 * post content. 6081 * 6082 * @see media.model.Selection 6191 * @returns {unresolved|Boolean} 6083 6192 */ 6084 createSelection: function() {6193 selected: function() { 6085 6194 var selection = this.options.selection; 6086 6087 if ( ! (selection instanceof wp.media.model.Selection) ) { 6088 this.options.selection = new wp.media.model.Selection( selection, { 6089 multiple: this.options.multiple 6090 }); 6195 if ( selection ) { 6196 return !! selection.get( this.model.cid ); 6091 6197 } 6092 6093 this._selection = {6094 attachments: new wp.media.model.Attachments(),6095 difference: []6096 };6097 6198 }, 6098 6099 6199 /** 6100 * Create the default states on the frame. 6200 * @param {Backbone.Model} model 6201 * @param {Backbone.Collection} collection 6101 6202 */ 6102 createStates: function() { 6103 var options = this.options; 6203 select: function( model, collection ) { 6204 var selection = this.options.selection, 6205 controller = this.controller; 6104 6206 6105 if ( this.options.states ) { 6207 // Check if a selection exists and if it's the collection provided. 6208 // If they're not the same collection, bail; we're in another 6209 // selection's event loop. 6210 if ( ! selection || ( collection && collection !== selection ) ) { 6106 6211 return; 6107 6212 } 6108 6213 6109 // Add the default states. 6110 this.states.add([ 6111 // Main states. 6112 new wp.media.controller.Library({ 6113 library: wp.media.query( options.library ), 6114 multiple: options.multiple, 6115 title: options.title, 6116 priority: 20 6117 }) 6118 ]); 6119 }, 6214 // Bail if the model is already selected. 6215 if ( this.$el.hasClass( 'selected' ) ) { 6216 return; 6217 } 6120 6218 6121 /** 6122 * Bind region mode event callbacks. 6123 * 6124 * @see media.controller.Region.render 6125 */ 6126 bindHandlers: function() { 6127 this.on( 'router:create:browse', this.createRouter, this ); 6128 this.on( 'router:render:browse', this.browseRouter, this ); 6129 this.on( 'content:create:browse', this.browseContent, this ); 6130 this.on( 'content:render:upload', this.uploadContent, this ); 6131 this.on( 'toolbar:create:select', this.createSelectToolbar, this ); 6219 // Add 'selected' class to model, set aria-checked to true. 6220 this.$el.addClass( 'selected' ).attr( 'aria-checked', true ); 6221 // Make the checkbox tabable, except in media grid (bulk select mode). 6222 if ( ! ( controller.isModeActive( 'grid' ) && controller.isModeActive( 'select' ) ) ) { 6223 this.$( '.check' ).attr( 'tabindex', '0' ); 6224 } 6132 6225 }, 6133 6134 6226 /** 6135 * Render callback for the router region in the `browse` mode. 6136 * 6137 * @param {wp.media.view.Router} routerView 6227 * @param {Backbone.Model} model 6228 * @param {Backbone.Collection} collection 6138 6229 */ 6139 browseRouter: function( routerView ) { 6140 routerView.set({ 6141 upload: { 6142 text: l10n.uploadFilesTitle, 6143 priority: 20 6144 }, 6145 browse: { 6146 text: l10n.mediaLibraryTitle, 6147 priority: 40 6148 } 6149 }); 6150 }, 6230 deselect: function( model, collection ) { 6231 var selection = this.options.selection; 6151 6232 6233 // Check if a selection exists and if it's the collection provided. 6234 // If they're not the same collection, bail; we're in another 6235 // selection's event loop. 6236 if ( ! selection || ( collection && collection !== selection ) ) { 6237 return; 6238 } 6239 this.$el.removeClass( 'selected' ).attr( 'aria-checked', false ) 6240 .find( '.check' ).attr( 'tabindex', '-1' ); 6241 }, 6152 6242 /** 6153 * Render callback for the content region in the `browse` mode. 6154 * 6155 * @param {wp.media.controller.Region} contentRegion 6243 * @param {Backbone.Model} model 6244 * @param {Backbone.Collection} collection 6156 6245 */ 6157 browseContent: function( contentRegion ) { 6158 var state = this.state(); 6159 6160 this.$el.removeClass('hide-toolbar'); 6161 6162 // Browse our library of attachments. 6163 contentRegion.view = new wp.media.view.AttachmentsBrowser({ 6164 controller: this, 6165 collection: state.get('library'), 6166 selection: state.get('selection'), 6167 model: state, 6168 sortable: state.get('sortable'), 6169 search: state.get('searchable'), 6170 filters: state.get('filterable'), 6171 date: state.get('date'), 6172 display: state.has('display') ? state.get('display') : state.get('displaySettings'), 6173 dragInfo: state.get('dragInfo'), 6246 details: function( model, collection ) { 6247 var selection = this.options.selection, 6248 details; 6174 6249 6175 idealColumnWidth: state.get('idealColumnWidth'),6176 suggestedWidth: state.get('suggestedWidth'),6177 suggestedHeight: state.get('suggestedHeight'),6250 if ( selection !== collection ) { 6251 return; 6252 } 6178 6253 6179 AttachmentView: state.get('AttachmentView')6180 });6254 details = selection.single(); 6255 this.$el.toggleClass( 'details', details === this.model ); 6181 6256 }, 6182 6183 6257 /** 6184 * Render callback for the content region in the `upload` mode. 6258 * @param {string} size 6259 * @returns {Object} 6185 6260 */ 6186 uploadContent: function() { 6187 this.$el.removeClass( 'hide-toolbar' ); 6188 this.content.set( new wp.media.view.UploaderInline({ 6189 controller: this 6190 }) ); 6191 }, 6261 imageSize: function( size ) { 6262 var sizes = this.model.get('sizes'), matched = false; 6192 6263 6193 /** 6194 * Toolbars 6195 * 6196 * @param {Object} toolbar 6197 * @param {Object} [options={}] 6198 * @this wp.media.controller.Region 6199 */ 6200 createSelectToolbar: function( toolbar, options ) { 6201 options = options || this.options.button || {}; 6202 options.controller = this; 6264 size = size || 'medium'; 6203 6265 6204 toolbar.view = new wp.media.view.Toolbar.Select( options ); 6205 } 6206 }); 6266 // Use the provided image size if possible. 6267 if ( sizes ) { 6268 if ( sizes[ size ] ) { 6269 matched = sizes[ size ]; 6270 } else if ( sizes.large ) { 6271 matched = sizes.large; 6272 } else if ( sizes.thumbnail ) { 6273 matched = sizes.thumbnail; 6274 } else if ( sizes.full ) { 6275 matched = sizes.full; 6276 } 6207 6277 6208 module.exports = Select; 6278 if ( matched ) { 6279 return _.clone( matched ); 6280 } 6281 } 6209 6282 6210 },{}],47:[function(require,module,exports){ 6211 /** 6212 * wp.media.view.Iframe 6213 * 6214 * @memberOf wp.media.view 6215 * 6216 * @class 6217 * @augments wp.media.View 6218 * @augments wp.Backbone.View 6219 * @augments Backbone.View 6220 */ 6221 var Iframe = wp.media.View.extend(/** @lends wp.media.view.Iframe.prototype */{ 6222 className: 'media-iframe', 6283 return { 6284 url: this.model.get('url'), 6285 width: this.model.get('width'), 6286 height: this.model.get('height'), 6287 orientation: this.model.get('orientation') 6288 }; 6289 }, 6223 6290 /** 6224 * @ returns {wp.media.view.Iframe} Returns itself to allow chaining6291 * @param {Object} event 6225 6292 */ 6226 render: function() { 6227 this.views.detach(); 6228 this.$el.html( '<iframe src="' + this.controller.state().get('src') + '" />' ); 6229 this.views.render(); 6230 return this; 6231 } 6232 }); 6233 6234 module.exports = Iframe; 6235 6236 },{}],48:[function(require,module,exports){ 6237 var AttachmentDisplay = wp.media.view.Settings.AttachmentDisplay, 6238 $ = jQuery, 6239 ImageDetails; 6240 6241 /** 6242 * wp.media.view.ImageDetails 6243 * 6244 * @memberOf wp.media.view 6245 * 6246 * @class 6247 * @augments wp.media.view.Settings.AttachmentDisplay 6248 * @augments wp.media.view.Settings 6249 * @augments wp.media.View 6250 * @augments wp.Backbone.View 6251 * @augments Backbone.View 6252 */ 6253 ImageDetails = AttachmentDisplay.extend(/** @lends wp.media.view.ImageDetails.prototype */{ 6254 className: 'image-details', 6255 template: wp.template('image-details'), 6256 events: _.defaults( AttachmentDisplay.prototype.events, { 6257 'click .edit-attachment': 'editAttachment', 6258 'click .replace-attachment': 'replaceAttachment', 6259 'click .advanced-toggle': 'onToggleAdvanced', 6260 'change [data-setting="customWidth"]': 'onCustomSize', 6261 'change [data-setting="customHeight"]': 'onCustomSize', 6262 'keyup [data-setting="customWidth"]': 'onCustomSize', 6263 'keyup [data-setting="customHeight"]': 'onCustomSize' 6264 } ), 6265 initialize: function() { 6266 // used in AttachmentDisplay.prototype.updateLinkTo 6267 this.options.attachment = this.model.attachment; 6268 this.listenTo( this.model, 'change:url', this.updateUrl ); 6269 this.listenTo( this.model, 'change:link', this.toggleLinkSettings ); 6270 this.listenTo( this.model, 'change:size', this.toggleCustomSize ); 6293 updateSetting: function( event ) { 6294 var $setting = $( event.target ).closest('[data-setting]'), 6295 setting, value; 6271 6296 6272 AttachmentDisplay.prototype.initialize.apply( this, arguments ); 6273 }, 6297 if ( ! $setting.length ) { 6298 return; 6299 } 6274 6300 6275 prepare: function() {6276 va r attachment = false;6301 setting = $setting.data('setting'); 6302 value = event.target.value; 6277 6303 6278 if ( this.model. attachment) {6279 attachment = this.model.attachment.toJSON();6304 if ( this.model.get( setting ) !== value ) { 6305 this.save( setting, value ); 6280 6306 } 6281 return _.defaults({6282 model: this.model.toJSON(),6283 attachment: attachment6284 }, this.options );6285 6307 }, 6286 6308 6287 render: function() { 6288 var args = arguments; 6309 /** 6310 * Pass all the arguments to the model's save method. 6311 * 6312 * Records the aggregate status of all save requests and updates the 6313 * view's classes accordingly. 6314 */ 6315 save: function() { 6316 var view = this, 6317 save = this._save = this._save || { status: 'ready' }, 6318 request = this.model.save.apply( this.model, arguments ), 6319 requests = save.requests ? $.when( request, save.requests ) : request; 6289 6320 6290 if ( this.model.attachment && 'pending' === this.model.dfd.state() ) { 6291 this.model.dfd 6292 .done( _.bind( function() { 6293 AttachmentDisplay.prototype.render.apply( this, args ); 6294 this.postRender(); 6295 }, this ) ) 6296 .fail( _.bind( function() { 6297 this.model.attachment = false; 6298 AttachmentDisplay.prototype.render.apply( this, args ); 6299 this.postRender(); 6300 }, this ) ); 6301 } else { 6302 AttachmentDisplay.prototype.render.apply( this, arguments ); 6303 this.postRender(); 6321 // If we're waiting to remove 'Saved.', stop. 6322 if ( save.savedTimer ) { 6323 clearTimeout( save.savedTimer ); 6304 6324 } 6305 6325 6306 return this; 6326 this.updateSave('waiting'); 6327 save.requests = requests; 6328 requests.always( function() { 6329 // If we've performed another request since this one, bail. 6330 if ( save.requests !== requests ) { 6331 return; 6332 } 6333 6334 view.updateSave( requests.state() === 'resolved' ? 'complete' : 'error' ); 6335 save.savedTimer = setTimeout( function() { 6336 view.updateSave('ready'); 6337 delete save.savedTimer; 6338 }, 2000 ); 6339 }); 6307 6340 }, 6341 /** 6342 * @param {string} status 6343 * @returns {wp.media.view.Attachment} Returns itself to allow chaining 6344 */ 6345 updateSave: function( status ) { 6346 var save = this._save = this._save || { status: 'ready' }; 6308 6347 6309 postRender: function() { 6310 setTimeout( _.bind( this.resetFocus, this ), 10 ); 6311 this.toggleLinkSettings(); 6312 if ( window.getUserSetting( 'advImgDetails' ) === 'show' ) { 6313 this.toggleAdvanced( true ); 6348 if ( status && status !== save.status ) { 6349 this.$el.removeClass( 'save-' + save.status ); 6350 save.status = status; 6314 6351 } 6315 this.trigger( 'post-render' );6316 },6317 6352 6318 resetFocus: function() { 6319 this.$( '.link-to-custom' ).blur(); 6320 this.$( '.embed-media-settings' ).scrollTop( 0 ); 6353 this.$el.addClass( 'save-' + save.status ); 6354 return this; 6321 6355 }, 6322 6356 6323 update Url: function() {6324 this.$( '.image img' ).attr( 'src', this.model.get( 'url' ) );6325 this.$( '.url' ).val( this.model.get( 'url' ) );6326 },6357 updateAll: function() { 6358 var $settings = this.$('[data-setting]'), 6359 model = this.model, 6360 changed; 6327 6361 6328 toggleLinkSettings: function() { 6329 if ( this.model.get( 'link' ) === 'none' ) { 6330 this.$( '.link-settings' ).addClass('hidden'); 6331 } else { 6332 this.$( '.link-settings' ).removeClass('hidden'); 6333 } 6334 }, 6362 changed = _.chain( $settings ).map( function( el ) { 6363 var $input = $('input, textarea, select, [value]', el ), 6364 setting, value; 6335 6365 6336 toggleCustomSize: function() { 6337 if ( this.model.get( 'size' ) !== 'custom' ) { 6338 this.$( '.custom-size' ).addClass('hidden'); 6339 } else { 6340 this.$( '.custom-size' ).removeClass('hidden'); 6366 if ( ! $input.length ) { 6367 return; 6368 } 6369 6370 setting = $(el).data('setting'); 6371 value = $input.val(); 6372 6373 // Record the value if it changed. 6374 if ( model.get( setting ) !== value ) { 6375 return [ setting, value ]; 6376 } 6377 }).compact().object().value(); 6378 6379 if ( ! _.isEmpty( changed ) ) { 6380 model.save( changed ); 6341 6381 } 6342 6382 }, 6383 /** 6384 * @param {Object} event 6385 */ 6386 removeFromLibrary: function( event ) { 6387 // Catch enter and space events 6388 if ( 'keydown' === event.type && 13 !== event.keyCode && 32 !== event.keyCode ) { 6389 return; 6390 } 6343 6391 6344 onCustomSize: function( event ) { 6345 var dimension = $( event.target ).data('setting'), 6346 num = $( event.target ).val(), 6347 value; 6392 // Stop propagation so the model isn't selected. 6393 event.stopPropagation(); 6348 6394 6349 // Ignore bogus input 6350 if ( ! /^\d+/.test( num ) || parseInt( num, 10 ) < 1 ) { 6351 event.preventDefault(); 6395 this.collection.remove( this.model ); 6396 }, 6397 6398 /** 6399 * Add the model if it isn't in the selection, if it is in the selection, 6400 * remove it. 6401 * 6402 * @param {[type]} event [description] 6403 * @return {[type]} [description] 6404 */ 6405 checkClickHandler: function ( event ) { 6406 var selection = this.options.selection; 6407 if ( ! selection ) { 6352 6408 return; 6353 6409 } 6354 6355 if ( dimension === 'customWidth') {6356 value = Math.round( 1 / this.model.get( 'aspectRatio' ) * num);6357 this.model.set( 'customHeight', value, { silent: true } );6358 this.$ ( '[data-setting="customHeight"]' ).val( value);6410 event.stopPropagation(); 6411 if ( selection.where( { id: this.model.get( 'id' ) } ).length ) { 6412 selection.remove( this.model ); 6413 // Move focus back to the attachment tile (from the check). 6414 this.$el.focus(); 6359 6415 } else { 6360 value = Math.round( this.model.get( 'aspectRatio' ) * num ); 6361 this.model.set( 'customWidth', value, { silent: true } ); 6362 this.$( '[data-setting="customWidth"]' ).val( value ); 6416 selection.add( this.model ); 6363 6417 } 6364 }, 6418 } 6419 }); 6365 6420 6366 onToggleAdvanced: function( event ) { 6367 event.preventDefault(); 6368 this.toggleAdvanced(); 6369 }, 6421 // Ensure settings remain in sync between attachment views. 6422 _.each({ 6423 caption: '_syncCaption', 6424 title: '_syncTitle', 6425 artist: '_syncArtist', 6426 album: '_syncAlbum' 6427 }, function( method, setting ) { 6428 /** 6429 * @function _syncCaption 6430 * @memberOf wp.media.view.Attachment 6431 * @instance 6432 * 6433 * @param {Backbone.Model} model 6434 * @param {string} value 6435 * @returns {wp.media.view.Attachment} Returns itself to allow chaining 6436 */ 6437 /** 6438 * @function _syncTitle 6439 * @memberOf wp.media.view.Attachment 6440 * @instance 6441 * 6442 * @param {Backbone.Model} model 6443 * @param {string} value 6444 * @returns {wp.media.view.Attachment} Returns itself to allow chaining 6445 */ 6446 /** 6447 * @function _syncArtist 6448 * @memberOf wp.media.view.Attachment 6449 * @instance 6450 * 6451 * @param {Backbone.Model} model 6452 * @param {string} value 6453 * @returns {wp.media.view.Attachment} Returns itself to allow chaining 6454 */ 6455 /** 6456 * @function _syncAlbum 6457 * @memberOf wp.media.view.Attachment 6458 * @instance 6459 * 6460 * @param {Backbone.Model} model 6461 * @param {string} value 6462 * @returns {wp.media.view.Attachment} Returns itself to allow chaining 6463 */ 6464 Attachment.prototype[ method ] = function( model, value ) { 6465 var $setting = this.$('[data-setting="' + setting + '"]'); 6370 6466 6371 toggleAdvanced: function( show) {6372 var $advanced = this.$el.find( '.advanced-section' ),6373 mode;6467 if ( ! $setting.length ) { 6468 return this; 6469 } 6374 6470 6375 if ( $advanced.hasClass('advanced-visible') || show === false ) { 6376 $advanced.removeClass('advanced-visible'); 6377 $advanced.find('.advanced-settings').addClass('hidden'); 6378 mode = 'hide'; 6379 } else { 6380 $advanced.addClass('advanced-visible'); 6381 $advanced.find('.advanced-settings').removeClass('hidden'); 6382 mode = 'show'; 6471 // If the updated value is in sync with the value in the DOM, there 6472 // is no need to re-render. If we're currently editing the value, 6473 // it will automatically be in sync, suppressing the re-render for 6474 // the view we're editing, while updating any others. 6475 if ( value === $setting.find('input, textarea, select, [value]').val() ) { 6476 return this; 6383 6477 } 6384 6478 6385 window.setUserSetting( 'advImgDetails', mode ); 6386 }, 6479 return this.render(); 6480 }; 6481 }); 6387 6482 6388 editAttachment: function( event ) { 6389 var editState = this.controller.states.get( 'edit-image' ); 6483 module.exports = Attachment; 6390 6484 6391 if ( window.imageEdit && editState ) {6392 event.preventDefault();6393 editState.set( 'image', this.model.attachment );6394 this.controller.setState( 'edit-image' );6395 }6396 },6397 6485 6398 replaceAttachment: function( event ) { 6399 event.preventDefault(); 6400 this.controller.setState( 'replace-image' ); 6486 /***/ }), 6487 /* 70 */ 6488 /***/ (function(module, exports) { 6489 6490 /** 6491 * wp.media.view.Attachment.Library 6492 * 6493 * @memberOf wp.media.view.Attachment 6494 * 6495 * @class 6496 * @augments wp.media.view.Attachment 6497 * @augments wp.media.View 6498 * @augments wp.Backbone.View 6499 * @augments Backbone.View 6500 */ 6501 var Library = wp.media.view.Attachment.extend(/** @lends wp.media.view.Attachment.Library.prototype */{ 6502 buttons: { 6503 check: true 6401 6504 } 6402 6505 }); 6403 6506 6404 module.exports = ImageDetails; 6507 module.exports = Library; 6508 6509 6510 /***/ }), 6511 /* 71 */ 6512 /***/ (function(module, exports) { 6405 6513 6406 },{}],49:[function(require,module,exports){6407 6514 /** 6408 * wp.media.view. Label6515 * wp.media.view.Attachment.EditLibrary 6409 6516 * 6410 * @memberOf wp.media.view 6517 * @memberOf wp.media.view.Attachment 6411 6518 * 6412 6519 * @class 6520 * @augments wp.media.view.Attachment 6413 6521 * @augments wp.media.View 6414 6522 * @augments wp.Backbone.View 6415 6523 * @augments Backbone.View 6416 6524 */ 6417 var Label = wp.media.View.extend(/** @lends wp.media.view.Label.prototype */{ 6418 tagName: 'label', 6419 className: 'screen-reader-text', 6420 6421 initialize: function() { 6422 this.value = this.options.value; 6423 }, 6424 6425 render: function() { 6426 this.$el.html( this.value ); 6427 6428 return this; 6525 var EditLibrary = wp.media.view.Attachment.extend(/** @lends wp.media.view.Attachment.EditLibrary.prototype */{ 6526 buttons: { 6527 close: true 6429 6528 } 6430 6529 }); 6431 6530 6432 module.exports = Label;6531 module.exports = EditLibrary; 6433 6532 6434 },{}],50:[function(require,module,exports){ 6435 var Frame = wp.media.view.Frame, 6533 6534 /***/ }), 6535 /* 72 */ 6536 /***/ (function(module, exports) { 6537 6538 var View = wp.media.View, 6436 6539 $ = jQuery, 6437 MediaFrame;6540 Attachments; 6438 6541 6439 6542 /** 6440 * wp.media.view.MediaFrame 6441 * 6442 * The frame used to create the media modal. 6543 * wp.media.view.Attachments 6443 6544 * 6444 6545 * @memberOf wp.media.view 6445 6546 * 6446 6547 * @class 6447 * @augments wp.media.view.Frame6448 6548 * @augments wp.media.View 6449 6549 * @augments wp.Backbone.View 6450 6550 * @augments Backbone.View 6451 * @mixes wp.media.controller.StateMachine6452 6551 */ 6453 MediaFrame = Frame.extend(/** @lends wp.media.view.MediaFrame.prototype */{ 6454 className: 'media-frame', 6455 template: wp.template('media-frame'), 6456 regions: ['menu','title','content','toolbar','router'], 6552 Attachments = View.extend(/** @lends wp.media.view.Attachments.prototype */{ 6553 tagName: 'ul', 6554 className: 'attachments', 6457 6555 6458 events: {6459 'click div.media-frame-title h1': 'toggleMenu'6556 attributes: { 6557 tabIndex: -1 6460 6558 }, 6461 6559 6462 /**6463 * @constructs6464 */6465 6560 initialize: function() { 6466 Frame.prototype.initialize.apply( this, arguments);6561 this.el.id = _.uniqueId('__attachments-view-'); 6467 6562 6468 6563 _.defaults( this.options, { 6469 title: '', 6470 modal: true, 6471 uploader: true 6564 refreshSensitivity: wp.media.isTouchDevice ? 300 : 200, 6565 refreshThreshold: 3, 6566 AttachmentView: wp.media.view.Attachment, 6567 sortable: false, 6568 resize: true, 6569 idealColumnWidth: $( window ).width() < 640 ? 135 : 150 6472 6570 }); 6473 6571 6474 // Ensure core UI is enabled. 6475 this.$el.addClass('wp-core-ui'); 6572 this._viewsByCid = {}; 6573 this.$window = $( window ); 6574 this.resizeEvent = 'resize.media-modal-columns'; 6476 6575 6477 // Initialize modal container view. 6478 if ( this.options.modal ) { 6479 this.modal = new wp.media.view.Modal({ 6480 controller: this, 6481 title: this.options.title 6576 this.collection.on( 'add', function( attachment ) { 6577 this.views.add( this.createAttachmentView( attachment ), { 6578 at: this.collection.indexOf( attachment ) 6482 6579 }); 6580 }, this ); 6483 6581 6484 this.modal.content( this ); 6582 this.collection.on( 'remove', function( attachment ) { 6583 var view = this._viewsByCid[ attachment.cid ]; 6584 delete this._viewsByCid[ attachment.cid ]; 6585 6586 if ( view ) { 6587 view.remove(); 6588 } 6589 }, this ); 6590 6591 this.collection.on( 'reset', this.render, this ); 6592 6593 this.listenTo( this.controller, 'library:selection:add', this.attachmentFocus ); 6594 6595 // Throttle the scroll handler and bind this. 6596 this.scroll = _.chain( this.scroll ).bind( this ).throttle( this.options.refreshSensitivity ).value(); 6597 6598 this.options.scrollElement = this.options.scrollElement || this.el; 6599 $( this.options.scrollElement ).on( 'scroll', this.scroll ); 6600 6601 this.initSortable(); 6602 6603 _.bindAll( this, 'setColumns' ); 6604 6605 if ( this.options.resize ) { 6606 this.on( 'ready', this.bindEvents ); 6607 this.controller.on( 'open', this.setColumns ); 6608 6609 // Call this.setColumns() after this view has been rendered in the DOM so 6610 // attachments get proper width applied. 6611 _.defer( this.setColumns, this ); 6485 6612 } 6613 }, 6486 6614 6487 // Force the uploader off if the upload limit has been exceeded or 6488 // if the browser isn't supported. 6489 if ( wp.Uploader.limitExceeded || ! wp.Uploader.browser.supported ) { 6490 this.options.uploader = false; 6615 bindEvents: function() { 6616 this.$window.off( this.resizeEvent ).on( this.resizeEvent, _.debounce( this.setColumns, 50 ) ); 6617 }, 6618 6619 attachmentFocus: function() { 6620 this.$( 'li:first' ).focus(); 6621 }, 6622 6623 restoreFocus: function() { 6624 this.$( 'li.selected:first' ).focus(); 6625 }, 6626 6627 arrowEvent: function( event ) { 6628 var attachments = this.$el.children( 'li' ), 6629 perRow = this.columns, 6630 index = attachments.filter( ':focus' ).index(), 6631 row = ( index + 1 ) <= perRow ? 1 : Math.ceil( ( index + 1 ) / perRow ); 6632 6633 if ( index === -1 ) { 6634 return; 6491 6635 } 6492 6636 6493 // Initialize window-wide uploader. 6494 if ( this.options.uploader ) { 6495 this.uploader = new wp.media.view.UploaderWindow({ 6496 controller: this, 6497 uploader: { 6498 dropzone: this.modal ? this.modal.$el : this.$el, 6499 container: this.$el 6500 } 6501 }); 6502 this.views.set( '.media-frame-uploader', this.uploader ); 6637 // Left arrow 6638 if ( 37 === event.keyCode ) { 6639 if ( 0 === index ) { 6640 return; 6641 } 6642 attachments.eq( index - 1 ).focus(); 6503 6643 } 6504 6644 6505 this.on( 'attach', _.bind( this.views.ready, this.views ), this ); 6645 // Up arrow 6646 if ( 38 === event.keyCode ) { 6647 if ( 1 === row ) { 6648 return; 6649 } 6650 attachments.eq( index - perRow ).focus(); 6651 } 6506 6652 6507 // Bind default title creation. 6508 this.on( 'title:create:default', this.createTitle, this ); 6509 this.title.mode('default'); 6653 // Right arrow 6654 if ( 39 === event.keyCode ) { 6655 if ( attachments.length === index ) { 6656 return; 6657 } 6658 attachments.eq( index + 1 ).focus(); 6659 } 6510 6660 6511 this.on( 'title:render', function( view ) { 6512 view.$el.append( '<span class="dashicons dashicons-arrow-down"></span>' ); 6513 }); 6661 // Down arrow 6662 if ( 40 === event.keyCode ) { 6663 if ( Math.ceil( attachments.length / perRow ) === row ) { 6664 return; 6665 } 6666 attachments.eq( index + perRow ).focus(); 6667 } 6668 }, 6669 6670 dispose: function() { 6671 this.collection.props.off( null, null, this ); 6672 if ( this.options.resize ) { 6673 this.$window.off( this.resizeEvent ); 6674 } 6675 6676 /** 6677 * call 'dispose' directly on the parent class 6678 */ 6679 View.prototype.dispose.apply( this, arguments ); 6680 }, 6681 6682 setColumns: function() { 6683 var prev = this.columns, 6684 width = this.$el.width(); 6685 6686 if ( width ) { 6687 this.columns = Math.min( Math.round( width / this.options.idealColumnWidth ), 12 ) || 1; 6688 6689 if ( ! prev || prev !== this.columns ) { 6690 this.$el.closest( '.media-frame-content' ).attr( 'data-columns', this.columns ); 6691 } 6692 } 6693 }, 6694 6695 initSortable: function() { 6696 var collection = this.collection; 6697 6698 if ( ! this.options.sortable || ! $.fn.sortable ) { 6699 return; 6700 } 6701 6702 this.$el.sortable( _.extend({ 6703 // If the `collection` has a `comparator`, disable sorting. 6704 disabled: !! collection.comparator, 6705 6706 // Change the position of the attachment as soon as the 6707 // mouse pointer overlaps a thumbnail. 6708 tolerance: 'pointer', 6709 6710 // Record the initial `index` of the dragged model. 6711 start: function( event, ui ) { 6712 ui.item.data('sortableIndexStart', ui.item.index()); 6713 }, 6714 6715 // Update the model's index in the collection. 6716 // Do so silently, as the view is already accurate. 6717 update: function( event, ui ) { 6718 var model = collection.at( ui.item.data('sortableIndexStart') ), 6719 comparator = collection.comparator; 6720 6721 // Temporarily disable the comparator to prevent `add` 6722 // from re-sorting. 6723 delete collection.comparator; 6724 6725 // Silently shift the model to its new index. 6726 collection.remove( model, { 6727 silent: true 6728 }); 6729 collection.add( model, { 6730 silent: true, 6731 at: ui.item.index() 6732 }); 6733 6734 // Restore the comparator. 6735 collection.comparator = comparator; 6736 6737 // Fire the `reset` event to ensure other collections sync. 6738 collection.trigger( 'reset', collection ); 6514 6739 6515 // Bind default menu. 6516 this.on( 'menu:create:default', this.createMenu, this ); 6517 }, 6518 /** 6519 * @returns {wp.media.view.MediaFrame} Returns itself to allow chaining 6520 */ 6521 render: function() { 6522 // Activate the default state if no active state exists. 6523 if ( ! this.state() && this.options.state ) { 6524 this.setState( this.options.state ); 6525 } 6526 /** 6527 * call 'render' directly on the parent class 6528 */ 6529 return Frame.prototype.render.apply( this, arguments ); 6530 }, 6531 /** 6532 * @param {Object} title 6533 * @this wp.media.controller.Region 6534 */ 6535 createTitle: function( title ) { 6536 title.view = new wp.media.View({ 6537 controller: this, 6538 tagName: 'h1' 6539 }); 6540 }, 6541 /** 6542 * @param {Object} menu 6543 * @this wp.media.controller.Region 6544 */ 6545 createMenu: function( menu ) { 6546 menu.view = new wp.media.view.Menu({ 6547 controller: this 6548 }); 6549 }, 6740 // If the collection is sorted by menu order, 6741 // update the menu order. 6742 collection.saveMenuOrder(); 6743 } 6744 }, this.options.sortable ) ); 6550 6745 6551 toggleMenu: function() { 6552 this.$el.find( '.media-menu' ).toggleClass( 'visible' ); 6553 }, 6746 // If the `orderby` property is changed on the `collection`, 6747 // check to see if we have a `comparator`. If so, disable sorting. 6748 collection.props.on( 'change:orderby', function() { 6749 this.$el.sortable( 'option', 'disabled', !! collection.comparator ); 6750 }, this ); 6554 6751 6555 /** 6556 * @param {Object} toolbar 6557 * @this wp.media.controller.Region 6558 */ 6559 createToolbar: function( toolbar ) { 6560 toolbar.view = new wp.media.view.Toolbar({ 6561 controller: this 6562 }); 6563 }, 6564 /** 6565 * @param {Object} router 6566 * @this wp.media.controller.Region 6567 */ 6568 createRouter: function( router ) { 6569 router.view = new wp.media.view.Router({ 6570 controller: this 6571 }); 6752 this.collection.props.on( 'change:orderby', this.refreshSortable, this ); 6753 this.refreshSortable(); 6572 6754 }, 6573 /**6574 * @param {Object} options6575 */6576 createIframeStates: function( options ) {6577 var settings = wp.media.view.settings,6578 tabs = settings.tabs,6579 tabUrl = settings.tabUrl,6580 $postId;6581 6755 6582 if ( ! tabs || ! tabUrl ) { 6756 refreshSortable: function() { 6757 if ( ! this.options.sortable || ! $.fn.sortable ) { 6583 6758 return; 6584 6759 } 6585 6760 6586 // Add the post ID to the tab URL if it exists. 6587 $postId = $('#post_ID'); 6588 if ( $postId.length ) { 6589 tabUrl += '&post_id=' + $postId.val(); 6590 } 6591 6592 // Generate the tab states. 6593 _.each( tabs, function( title, id ) { 6594 this.state( 'iframe:' + id ).set( _.defaults({ 6595 tab: id, 6596 src: tabUrl + '&tab=' + id, 6597 title: title, 6598 content: 'iframe', 6599 menu: 'default' 6600 }, options ) ); 6601 }, this ); 6761 // If the `collection` has a `comparator`, disable sorting. 6762 var collection = this.collection, 6763 orderby = collection.props.get('orderby'), 6764 enabled = 'menuOrder' === orderby || ! collection.comparator; 6602 6765 6603 this.on( 'content:create:iframe', this.iframeContent, this ); 6604 this.on( 'content:deactivate:iframe', this.iframeContentCleanup, this ); 6605 this.on( 'menu:render:default', this.iframeMenu, this ); 6606 this.on( 'open', this.hijackThickbox, this ); 6607 this.on( 'close', this.restoreThickbox, this ); 6766 this.$el.sortable( 'option', 'disabled', ! enabled ); 6608 6767 }, 6609 6768 6610 6769 /** 6611 * @param { Object} content6612 * @ this wp.media.controller.Region6770 * @param {wp.media.model.Attachment} attachment 6771 * @returns {wp.media.View} 6613 6772 */ 6614 iframeContent: function( content ) { 6615 this.$el.addClass('hide-toolbar'); 6616 content.view = new wp.media.view.Iframe({ 6617 controller: this 6773 createAttachmentView: function( attachment ) { 6774 var view = new this.options.AttachmentView({ 6775 controller: this.controller, 6776 model: attachment, 6777 collection: this.collection, 6778 selection: this.options.selection 6618 6779 }); 6619 },6620 6780 6621 iframeContentCleanup: function() { 6622 this.$el.removeClass('hide-toolbar'); 6781 return this._viewsByCid[ attachment.cid ] = view; 6623 6782 }, 6624 6783 6625 iframeMenu: function( view ) { 6626 var views = {}; 6784 prepare: function() { 6785 // Create all of the Attachment views, and replace 6786 // the list in a single DOM operation. 6787 if ( this.collection.length ) { 6788 this.views.set( this.collection.map( this.createAttachmentView, this ) ); 6627 6789 6628 if ( ! view ) { 6629 return; 6790 // If there are no elements, clear the views and load some. 6791 } else { 6792 this.views.unset(); 6793 this.collection.more().done( this.scroll ); 6630 6794 } 6795 }, 6631 6796 6632 _.each( wp.media.view.settings.tabs, function( title, id ) { 6633 views[ 'iframe:' + id ] = { 6634 text: this.state( 'iframe:' + id ).get('title'), 6635 priority: 200 6636 }; 6637 }, this ); 6638 6639 view.set( views ); 6797 ready: function() { 6798 // Trigger the scroll event to check if we're within the 6799 // threshold to query for additional attachments. 6800 this.scroll(); 6640 6801 }, 6641 6802 6642 hijackThickbox: function() { 6643 var frame = this; 6803 scroll: function() { 6804 var view = this, 6805 el = this.options.scrollElement, 6806 scrollTop = el.scrollTop, 6807 toolbar; 6644 6808 6645 if ( ! window.tb_remove || this._tb_remove ) { 6809 // The scroll event occurs on the document, but the element 6810 // that should be checked is the document body. 6811 if ( el === document ) { 6812 el = document.body; 6813 scrollTop = $(document).scrollTop(); 6814 } 6815 6816 if ( ! $(el).is(':visible') || ! this.collection.hasMore() ) { 6646 6817 return; 6647 6818 } 6648 6819 6649 this._tb_remove = window.tb_remove; 6650 window.tb_remove = function() { 6651 frame.close(); 6652 frame.reset(); 6653 frame.setState( frame.options.state ); 6654 frame._tb_remove.call( window ); 6655 }; 6656 }, 6820 toolbar = this.views.parent.toolbar; 6657 6821 6658 restoreThickbox: function() {6659 if ( ! this._tb_remove) {6660 return;6822 // Show the spinner only if we are close to the bottom. 6823 if ( el.scrollHeight - ( scrollTop + el.clientHeight ) < el.clientHeight / 3 ) { 6824 toolbar.get('spinner').show(); 6661 6825 } 6662 6826 6663 window.tb_remove = this._tb_remove; 6664 delete this._tb_remove; 6827 if ( el.scrollHeight < scrollTop + ( el.clientHeight * this.options.refreshThreshold ) ) { 6828 this.collection.more().done(function() { 6829 view.scroll(); 6830 toolbar.get('spinner').hide(); 6831 }); 6832 } 6665 6833 } 6666 6834 }); 6667 6835 6668 // Map some of the modal's methods to the frame. 6669 _.each(['open','close','attach','detach','escape'], function( method ) { 6670 /** 6671 * @function open 6672 * @memberOf wp.media.view.MediaFrame 6673 * @instance 6674 * 6675 * @returns {wp.media.view.MediaFrame} Returns itself to allow chaining 6676 */ 6677 /** 6678 * @function close 6679 * @memberOf wp.media.view.MediaFrame 6680 * @instance 6681 * 6682 * @returns {wp.media.view.MediaFrame} Returns itself to allow chaining 6683 */ 6684 /** 6685 * @function attach 6686 * @memberOf wp.media.view.MediaFrame 6687 * @instance 6688 * 6689 * @returns {wp.media.view.MediaFrame} Returns itself to allow chaining 6690 */ 6691 /** 6692 * @function detach 6693 * @memberOf wp.media.view.MediaFrame 6694 * @instance 6695 * 6696 * @returns {wp.media.view.MediaFrame} Returns itself to allow chaining 6697 */ 6698 /** 6699 * @function escape 6700 * @memberOf wp.media.view.MediaFrame 6701 * @instance 6702 * 6703 * @returns {wp.media.view.MediaFrame} Returns itself to allow chaining 6836 module.exports = Attachments; 6837 6838 6839 /***/ }), 6840 /* 73 */ 6841 /***/ (function(module, exports) { 6842 6843 var l10n = wp.media.view.l10n, 6844 Search; 6845 6846 /** 6847 * wp.media.view.Search 6848 * 6849 * @memberOf wp.media.view 6850 * 6851 * @class 6852 * @augments wp.media.View 6853 * @augments wp.Backbone.View 6854 * @augments Backbone.View 6855 */ 6856 Search = wp.media.View.extend(/** @lends wp.media.view.Search.prototype */{ 6857 tagName: 'input', 6858 className: 'search', 6859 id: 'media-search-input', 6860 6861 attributes: { 6862 type: 'search', 6863 placeholder: l10n.searchMediaPlaceholder 6864 }, 6865 6866 events: { 6867 'input': 'search', 6868 'keyup': 'search' 6869 }, 6870 6871 /** 6872 * @returns {wp.media.view.Search} Returns itself to allow chaining 6704 6873 */ 6705 MediaFrame.prototype[ method ] = function() { 6706 if ( this.modal ) { 6707 this.modal[ method ].apply( this.modal, arguments ); 6708 } 6874 render: function() { 6875 this.el.value = this.model.escape('search'); 6709 6876 return this; 6710 }; 6877 }, 6878 6879 search: _.debounce( function( event ) { 6880 if ( event.target.value ) { 6881 this.model.set( 'search', event.target.value ); 6882 } else { 6883 this.model.unset('search'); 6884 } 6885 }, 300 ) 6711 6886 }); 6712 6887 6713 module.exports = MediaFrame; 6888 module.exports = Search; 6889 6890 6891 /***/ }), 6892 /* 74 */ 6893 /***/ (function(module, exports) { 6714 6894 6715 },{}],51:[function(require,module,exports){6716 6895 var $ = jQuery, 6717 MenuItem;6896 AttachmentFilters; 6718 6897 6719 6898 /** 6720 * wp.media.view. MenuItem6899 * wp.media.view.AttachmentFilters 6721 6900 * 6722 6901 * @memberOf wp.media.view 6723 6902 * … … var $ = jQuery, 6726 6905 * @augments wp.Backbone.View 6727 6906 * @augments Backbone.View 6728 6907 */ 6729 MenuItem = wp.media.View.extend(/** @lends wp.media.view.MenuItem.prototype */{ 6730 tagName: 'a', 6731 className: 'media-menu-item', 6732 6733 attributes: { 6734 href: '#' 6735 }, 6908 AttachmentFilters = wp.media.View.extend(/** @lends wp.media.view.AttachmentFilters.prototype */{ 6909 tagName: 'select', 6910 className: 'attachment-filters', 6911 id: 'media-attachment-filters', 6736 6912 6737 6913 events: { 6738 'click': '_click'6914 change: 'change' 6739 6915 }, 6740 /**6741 * @param {Object} event6742 */6743 _click: function( event ) {6744 var clickOverride = this.options.click;6745 6746 if ( event ) {6747 event.preventDefault();6748 }6749 6916 6750 if ( clickOverride ) { 6751 clickOverride.call( this ); 6752 } else { 6753 this.click(); 6754 } 6917 keys: [], 6755 6918 6756 // When selecting a tab along the left side, 6757 // focus should be transferred into the main panel 6758 if ( ! wp.media.isTouchDevice ) { 6759 $('.media-frame-content input').first().focus(); 6760 } 6761 }, 6919 initialize: function() { 6920 this.createFilters(); 6921 _.extend( this.filters, this.options.filters ); 6762 6922 6763 click: function() { 6764 var state = this.options.state; 6923 // Build `<option>` elements. 6924 this.$el.html( _.chain( this.filters ).map( function( filter, value ) { 6925 return { 6926 el: $( '<option></option>' ).val( value ).html( filter.text )[0], 6927 priority: filter.priority || 50 6928 }; 6929 }, this ).sortBy('priority').pluck('el').value() ); 6765 6930 6766 if ( state ) { 6767 this.controller.setState( state ); 6768 this.views.parent.$el.removeClass( 'visible' ); // TODO: or hide on any click, see below 6769 } 6931 this.listenTo( this.model, 'change', this.select ); 6932 this.select(); 6770 6933 }, 6934 6771 6935 /** 6772 * @ returns {wp.media.view.MenuItem} returns itself to allow chaining6936 * @abstract 6773 6937 */ 6774 render: function() { 6775 var options = this.options; 6938 createFilters: function() { 6939 this.filters = {}; 6940 }, 6776 6941 6777 if ( options.text ) { 6778 this.$el.text( options.text ); 6779 } else if ( options.html ) { 6780 this.$el.html( options.html ); 6942 /** 6943 * When the selected filter changes, update the Attachment Query properties to match. 6944 */ 6945 change: function() { 6946 var filter = this.filters[ this.el.value ]; 6947 if ( filter ) { 6948 this.model.set( filter.props ); 6781 6949 } 6950 }, 6782 6951 6783 return this; 6952 select: function() { 6953 var model = this.model, 6954 value = 'all', 6955 props = model.toJSON(); 6956 6957 _.find( this.filters, function( filter, id ) { 6958 var equal = _.all( filter.props, function( prop, key ) { 6959 return prop === ( _.isUndefined( props[ key ] ) ? null : props[ key ] ); 6960 }); 6961 6962 if ( equal ) { 6963 return value = id; 6964 } 6965 }); 6966 6967 this.$el.val( value ); 6784 6968 } 6785 6969 }); 6786 6970 6787 module.exports = MenuItem;6971 module.exports = AttachmentFilters; 6788 6972 6789 },{}],52:[function(require,module,exports){ 6790 var MenuItem = wp.media.view.MenuItem, 6791 PriorityList = wp.media.view.PriorityList, 6792 Menu; 6973 6974 /***/ }), 6975 /* 75 */ 6976 /***/ (function(module, exports) { 6977 6978 var l10n = wp.media.view.l10n, 6979 DateFilter; 6793 6980 6794 6981 /** 6795 * wp.media.view.Menu6982 * A filter dropdown for month/dates. 6796 6983 * 6797 * @memberOf wp.media.view 6984 * @memberOf wp.media.view.AttachmentFilters 6798 6985 * 6799 6986 * @class 6800 * @augments wp.media.view. PriorityList6987 * @augments wp.media.view.AttachmentFilters 6801 6988 * @augments wp.media.View 6802 6989 * @augments wp.Backbone.View 6803 6990 * @augments Backbone.View 6804 6991 */ 6805 Menu = PriorityList.extend(/** @lends wp.media.view.Menu.prototype */{ 6806 tagName: 'div', 6807 className: 'media-menu', 6808 property: 'state', 6809 ItemView: MenuItem, 6810 region: 'menu', 6992 DateFilter = wp.media.view.AttachmentFilters.extend(/** @lends wp.media.view.AttachmentFilters.Date.prototype */{ 6993 id: 'media-attachment-date-filters', 6811 6994 6812 /* TODO: alternatively hide on any click anywhere 6813 events: { 6814 'click': 'click' 6815 }, 6995 createFilters: function() { 6996 var filters = {}; 6997 _.each( wp.media.view.settings.months || {}, function( value, index ) { 6998 filters[ index ] = { 6999 text: value.text, 7000 props: { 7001 year: value.year, 7002 monthnum: value.month 7003 } 7004 }; 7005 }); 7006 filters.all = { 7007 text: l10n.allDates, 7008 props: { 7009 monthnum: false, 7010 year: false 7011 }, 7012 priority: 10 7013 }; 7014 this.filters = filters; 7015 } 7016 }); 6816 7017 6817 click: function() { 6818 this.$el.removeClass( 'visible' ); 6819 }, 6820 */ 7018 module.exports = DateFilter; 6821 7019 6822 /**6823 * @param {Object} options6824 * @param {string} id6825 * @returns {wp.media.View}6826 */6827 toView: function( options, id ) {6828 options = options || {};6829 options[ this.property ] = options[ this.property ] || id;6830 return new this.ItemView( options ).render();6831 },6832 7020 6833 ready: function() { 6834 /** 6835 * call 'ready' directly on the parent class 6836 */ 6837 PriorityList.prototype.ready.apply( this, arguments ); 6838 this.visibility(); 6839 }, 7021 /***/ }), 7022 /* 76 */ 7023 /***/ (function(module, exports) { 6840 7024 6841 set: function() { 6842 /** 6843 * call 'set' directly on the parent class 6844 */ 6845 PriorityList.prototype.set.apply( this, arguments ); 6846 this.visibility(); 6847 }, 7025 var l10n = wp.media.view.l10n, 7026 Uploaded; 6848 7027 6849 unset: function() { 6850 /** 6851 * call 'unset' directly on the parent class 6852 */ 6853 PriorityList.prototype.unset.apply( this, arguments ); 6854 this.visibility(); 6855 }, 7028 /** 7029 * wp.media.view.AttachmentFilters.Uploaded 7030 * 7031 * @memberOf wp.media.view.AttachmentFilters 7032 * 7033 * @class 7034 * @augments wp.media.view.AttachmentFilters 7035 * @augments wp.media.View 7036 * @augments wp.Backbone.View 7037 * @augments Backbone.View 7038 */ 7039 Uploaded = wp.media.view.AttachmentFilters.extend(/** @lends wp.media.view.AttachmentFilters.Uploaded.prototype */{ 7040 createFilters: function() { 7041 var type = this.model.get('type'), 7042 types = wp.media.view.settings.mimeTypes, 7043 text; 7044 7045 if ( types && type ) { 7046 text = types[ type ]; 7047 } 7048 7049 this.filters = { 7050 all: { 7051 text: text || l10n.allMediaItems, 7052 props: { 7053 uploadedTo: null, 7054 orderby: 'date', 7055 order: 'DESC' 7056 }, 7057 priority: 10 7058 }, 7059 7060 uploaded: { 7061 text: l10n.uploadedToThisPost, 7062 props: { 7063 uploadedTo: wp.media.view.settings.post.id, 7064 orderby: 'menuOrder', 7065 order: 'ASC' 7066 }, 7067 priority: 20 7068 }, 7069 7070 unattached: { 7071 text: l10n.unattached, 7072 props: { 7073 uploadedTo: 0, 7074 orderby: 'menuOrder', 7075 order: 'ASC' 7076 }, 7077 priority: 50 7078 } 7079 }; 7080 } 7081 }); 6856 7082 6857 visibility: function() { 6858 var region = this.region, 6859 view = this.controller[ region ].get(), 6860 views = this.views.get(), 6861 hide = ! views || views.length < 2; 7083 module.exports = Uploaded; 6862 7084 6863 if ( this === view ) {6864 this.controller.$el.toggleClass( 'hide-' + region, hide );6865 }6866 },6867 /**6868 * @param {string} id6869 */6870 select: function( id ) {6871 var view = this.get( id );6872 7085 6873 if ( ! view ) { 6874 return; 6875 } 7086 /***/ }), 7087 /* 77 */ 7088 /***/ (function(module, exports) { 6876 7089 6877 this.deselect(); 6878 view.$el.addClass('active'); 6879 }, 7090 var l10n = wp.media.view.l10n, 7091 All; 6880 7092 6881 deselect: function() { 6882 this.$el.children().removeClass('active'); 6883 }, 7093 /** 7094 * wp.media.view.AttachmentFilters.All 7095 * 7096 * @memberOf wp.media.view.AttachmentFilters 7097 * 7098 * @class 7099 * @augments wp.media.view.AttachmentFilters 7100 * @augments wp.media.View 7101 * @augments wp.Backbone.View 7102 * @augments Backbone.View 7103 */ 7104 All = wp.media.view.AttachmentFilters.extend(/** @lends wp.media.view.AttachmentFilters.All.prototype */{ 7105 createFilters: function() { 7106 var filters = {}; 6884 7107 6885 hide: function( id ) { 6886 var view = this.get( id ); 7108 _.each( wp.media.view.settings.mimeTypes || {}, function( text, key ) { 7109 filters[ key ] = { 7110 text: text, 7111 props: { 7112 status: null, 7113 type: key, 7114 uploadedTo: null, 7115 orderby: 'date', 7116 order: 'DESC' 7117 } 7118 }; 7119 }); 6887 7120 6888 if ( ! view ) { 6889 return; 7121 filters.all = { 7122 text: l10n.allMediaItems, 7123 props: { 7124 status: null, 7125 type: null, 7126 uploadedTo: null, 7127 orderby: 'date', 7128 order: 'DESC' 7129 }, 7130 priority: 10 7131 }; 7132 7133 if ( wp.media.view.settings.post.id ) { 7134 filters.uploaded = { 7135 text: l10n.uploadedToThisPost, 7136 props: { 7137 status: null, 7138 type: null, 7139 uploadedTo: wp.media.view.settings.post.id, 7140 orderby: 'menuOrder', 7141 order: 'ASC' 7142 }, 7143 priority: 20 7144 }; 6890 7145 } 6891 7146 6892 view.$el.addClass('hidden'); 6893 }, 7147 filters.unattached = { 7148 text: l10n.unattached, 7149 props: { 7150 status: null, 7151 uploadedTo: 0, 7152 type: null, 7153 orderby: 'menuOrder', 7154 order: 'ASC' 7155 }, 7156 priority: 50 7157 }; 6894 7158 6895 show: function( id ) {6896 var view = this.get( id );7159 if ( wp.media.view.settings.mediaTrash && 7160 this.controller.isModeActive( 'grid' ) ) { 6897 7161 6898 if ( ! view ) { 6899 return; 7162 filters.trash = { 7163 text: l10n.trash, 7164 props: { 7165 uploadedTo: null, 7166 status: 'trash', 7167 type: null, 7168 orderby: 'date', 7169 order: 'DESC' 7170 }, 7171 priority: 50 7172 }; 6900 7173 } 6901 7174 6902 view.$el.removeClass('hidden');7175 this.filters = filters; 6903 7176 } 6904 7177 }); 6905 7178 6906 module.exports = Menu;7179 module.exports = All; 6907 7180 6908 },{}],53:[function(require,module,exports){ 6909 var $ = jQuery, 6910 Modal; 7181 7182 /***/ }), 7183 /* 78 */ 7184 /***/ (function(module, exports) { 7185 7186 var View = wp.media.View, 7187 mediaTrash = wp.media.view.settings.mediaTrash, 7188 l10n = wp.media.view.l10n, 7189 $ = jQuery, 7190 AttachmentsBrowser; 6911 7191 6912 7192 /** 6913 * wp.media.view.Modal 6914 * 6915 * A modal view, which the media modal uses as its default container. 7193 * wp.media.view.AttachmentsBrowser 6916 7194 * 6917 7195 * @memberOf wp.media.view 6918 7196 * … … var $ = jQuery, 6920 7198 * @augments wp.media.View 6921 7199 * @augments wp.Backbone.View 6922 7200 * @augments Backbone.View 7201 * 7202 * @param {object} [options] The options hash passed to the view. 7203 * @param {boolean|string} [options.filters=false] Which filters to show in the browser's toolbar. 7204 * Accepts 'uploaded' and 'all'. 7205 * @param {boolean} [options.search=true] Whether to show the search interface in the 7206 * browser's toolbar. 7207 * @param {boolean} [options.date=true] Whether to show the date filter in the 7208 * browser's toolbar. 7209 * @param {boolean} [options.display=false] Whether to show the attachments display settings 7210 * view in the sidebar. 7211 * @param {boolean|string} [options.sidebar=true] Whether to create a sidebar for the browser. 7212 * Accepts true, false, and 'errors'. 6923 7213 */ 6924 Modal = wp.media.View.extend(/** @lends wp.media.view.Modal.prototype */{ 6925 tagName: 'div', 6926 template: wp.template('media-modal'), 6927 6928 attributes: { 6929 tabindex: 0 6930 }, 6931 6932 events: { 6933 'click .media-modal-backdrop, .media-modal-close': 'escapeHandler', 6934 'keydown': 'keydown' 6935 }, 6936 6937 clickedOpenerEl: null, 7214 AttachmentsBrowser = View.extend(/** @lends wp.media.view.AttachmentsBrowser.prototype */{ 7215 tagName: 'div', 7216 className: 'attachments-browser', 6938 7217 6939 7218 initialize: function() { 6940 7219 _.defaults( this.options, { 6941 container: document.body, 6942 title: '', 6943 propagate: true, 6944 freeze: true 7220 filters: false, 7221 search: true, 7222 date: true, 7223 display: false, 7224 sidebar: true, 7225 AttachmentView: wp.media.view.Attachment.Library 6945 7226 }); 6946 7227 6947 this.focusManager = new wp.media.view.FocusManager({ 6948 el: this.el 6949 }); 6950 }, 6951 /** 6952 * @returns {Object} 6953 */ 6954 prepare: function() { 6955 return { 6956 title: this.options.title 6957 }; 6958 }, 7228 this.controller.on( 'toggle:upload:attachment', this.toggleUploader, this ); 7229 this.controller.on( 'edit:selection', this.editSelection ); 6959 7230 6960 /** 6961 * @returns {wp.media.view.Modal} Returns itself to allow chaining 6962 */ 6963 attach: function() { 6964 if ( this.views.attached ) { 6965 return this; 7231 // In the Media Library, the sidebar is used to display errors before the attachments grid. 7232 if ( this.options.sidebar && 'errors' === this.options.sidebar ) { 7233 this.createSidebar(); 6966 7234 } 6967 7235 6968 if ( ! this.views.rendered ) { 6969 this.render(); 7236 /* 7237 * For accessibility reasons, place the Inline Uploader before other sections. 7238 * This way, in the Media Library, it's right after the Add New button, see ticket #37188. 7239 */ 7240 this.createUploader(); 7241 7242 /* 7243 * Create a multi-purpose toolbar. Used as main toolbar in the Media Library 7244 * and also for other things, for example the "Drag and drop to reorder" and 7245 * "Suggested dimensions" info in the media modal. 7246 */ 7247 this.createToolbar(); 7248 7249 // Create the list of attachments. 7250 this.createAttachments(); 7251 7252 // For accessibility reasons, place the normal sidebar after the attachments, see ticket #36909. 7253 if ( this.options.sidebar && 'errors' !== this.options.sidebar ) { 7254 this.createSidebar(); 6970 7255 } 6971 7256 6972 this. $el.appendTo( this.options.container);7257 this.updateContent(); 6973 7258 6974 // Manually mark the view as attached and trigger ready. 6975 this.views.attached = true; 6976 this.views.ready(); 7259 if ( ! this.options.sidebar || 'errors' === this.options.sidebar ) { 7260 this.$el.addClass( 'hide-sidebar' ); 6977 7261 6978 return this.propagate('attach'); 7262 if ( 'errors' === this.options.sidebar ) { 7263 this.$el.addClass( 'sidebar-for-errors' ); 7264 } 7265 } 7266 7267 this.collection.on( 'add remove reset', this.updateContent, this ); 7268 }, 7269 7270 editSelection: function( modal ) { 7271 modal.$( '.media-button-backToLibrary' ).focus(); 6979 7272 }, 6980 7273 6981 7274 /** 6982 * @returns {wp.media.view. Modal} Returns itself to allow chaining7275 * @returns {wp.media.view.AttachmentsBrowser} Returns itself to allow chaining 6983 7276 */ 6984 detach: function() { 6985 if ( this.$el.is(':visible') ) { 6986 this.close(); 7277 dispose: function() { 7278 this.options.selection.off( null, null, this ); 7279 View.prototype.dispose.apply( this, arguments ); 7280 return this; 7281 }, 7282 7283 createToolbar: function() { 7284 var LibraryViewSwitcher, Filters, toolbarOptions; 7285 7286 toolbarOptions = { 7287 controller: this.controller 7288 }; 7289 7290 if ( this.controller.isModeActive( 'grid' ) ) { 7291 toolbarOptions.className = 'media-toolbar wp-filter'; 6987 7292 } 6988 7293 6989 this.$el.detach();6990 this.views.attached = false;6991 return this.propagate('detach');6992 },7294 /** 7295 * @member {wp.media.view.Toolbar} 7296 */ 7297 this.toolbar = new wp.media.view.Toolbar( toolbarOptions ); 6993 7298 6994 /** 6995 * @returns {wp.media.view.Modal} Returns itself to allow chaining 6996 */ 6997 open: function() { 6998 var $el = this.$el, 6999 options = this.options, 7000 mceEditor; 7299 this.views.add( this.toolbar ); 7001 7300 7002 if ( $el.is(':visible') ){7003 return this;7004 } 7301 this.toolbar.set( 'spinner', new wp.media.view.Spinner({ 7302 priority: -60 7303 }) ); 7005 7304 7006 this.clickedOpenerEl = document.activeElement; 7305 if ( -1 !== $.inArray( this.options.filters, [ 'uploaded', 'all' ] ) ) { 7306 // "Filters" will return a <select>, need to render 7307 // screen reader text before 7308 this.toolbar.set( 'filtersLabel', new wp.media.view.Label({ 7309 value: l10n.filterByType, 7310 attributes: { 7311 'for': 'media-attachment-filters' 7312 }, 7313 priority: -80 7314 }).render() ); 7007 7315 7008 if ( ! this.views.attached ) { 7009 this.attach(); 7010 } 7316 if ( 'uploaded' === this.options.filters ) { 7317 this.toolbar.set( 'filters', new wp.media.view.AttachmentFilters.Uploaded({ 7318 controller: this.controller, 7319 model: this.collection.props, 7320 priority: -80 7321 }).render() ); 7322 } else { 7323 Filters = new wp.media.view.AttachmentFilters.All({ 7324 controller: this.controller, 7325 model: this.collection.props, 7326 priority: -80 7327 }); 7011 7328 7012 // If the `freeze` option is set, record the window's scroll position. 7013 if ( options.freeze ) { 7014 this._freeze = { 7015 scrollTop: $( window ).scrollTop() 7016 }; 7329 this.toolbar.set( 'filters', Filters.render() ); 7330 } 7017 7331 } 7018 7332 7019 // Disable page scrolling. 7020 $( 'body' ).addClass( 'modal-open' ); 7333 // Feels odd to bring the global media library switcher into the Attachment 7334 // browser view. Is this a use case for doAction( 'add:toolbar-items:attachments-browser', this.toolbar ); 7335 // which the controller can tap into and add this view? 7336 if ( this.controller.isModeActive( 'grid' ) ) { 7337 LibraryViewSwitcher = View.extend({ 7338 className: 'view-switch media-grid-view-switch', 7339 template: wp.template( 'media-library-view-switcher') 7340 }); 7021 7341 7022 $el.show(); 7342 this.toolbar.set( 'libraryViewSwitcher', new LibraryViewSwitcher({ 7343 controller: this.controller, 7344 priority: -90 7345 }).render() ); 7023 7346 7024 // Try to close the onscreen keyboard 7025 if ( 'ontouchend' in document ) { 7026 if ( ( mceEditor = window.tinymce && window.tinymce.activeEditor ) && ! mceEditor.isHidden() && mceEditor.iframeElement ) { 7027 mceEditor.iframeElement.focus(); 7028 mceEditor.iframeElement.blur(); 7347 // DateFilter is a <select>, screen reader text needs to be rendered before 7348 this.toolbar.set( 'dateFilterLabel', new wp.media.view.Label({ 7349 value: l10n.filterByDate, 7350 attributes: { 7351 'for': 'media-attachment-date-filters' 7352 }, 7353 priority: -75 7354 }).render() ); 7355 this.toolbar.set( 'dateFilter', new wp.media.view.DateFilter({ 7356 controller: this.controller, 7357 model: this.collection.props, 7358 priority: -75 7359 }).render() ); 7029 7360 7030 setTimeout( function() { 7031 mceEditor.iframeElement.blur(); 7032 }, 100 ); 7033 } 7034 } 7361 // BulkSelection is a <div> with subviews, including screen reader text 7362 this.toolbar.set( 'selectModeToggleButton', new wp.media.view.SelectModeToggleButton({ 7363 text: l10n.bulkSelect, 7364 controller: this.controller, 7365 priority: -70 7366 }).render() ); 7035 7367 7036 this.$el.focus(); 7368 this.toolbar.set( 'deleteSelectedButton', new wp.media.view.DeleteSelectedButton({ 7369 filters: Filters, 7370 style: 'primary', 7371 disabled: true, 7372 text: mediaTrash ? l10n.trashSelected : l10n.deleteSelected, 7373 controller: this.controller, 7374 priority: -60, 7375 click: function() { 7376 var changed = [], removed = [], 7377 selection = this.controller.state().get( 'selection' ), 7378 library = this.controller.state().get( 'library' ); 7037 7379 7038 return this.propagate('open'); 7039 }, 7380 if ( ! selection.length ) { 7381 return; 7382 } 7040 7383 7041 /** 7042 * @param {Object} options 7043 * @returns {wp.media.view.Modal} Returns itself to allow chaining 7044 */ 7045 close: function( options ) { 7046 var freeze = this._freeze; 7384 if ( ! mediaTrash && ! window.confirm( l10n.warnBulkDelete ) ) { 7385 return; 7386 } 7047 7387 7048 if ( ! this.views.attached || ! this.$el.is(':visible') ) {7049 return this;7050 }7388 if ( mediaTrash && 7389 'trash' !== selection.at( 0 ).get( 'status' ) && 7390 ! window.confirm( l10n.warnBulkTrash ) ) { 7051 7391 7052 // Enable page scrolling.7053 $( 'body' ).removeClass( 'modal-open' );7392 return; 7393 } 7054 7394 7055 // Hide modal and remove restricted media modal tab focus once it's closed 7056 this.$el.hide().undelegate( 'keydown' ); 7395 selection.each( function( model ) { 7396 if ( ! model.get( 'nonces' )['delete'] ) { 7397 removed.push( model ); 7398 return; 7399 } 7057 7400 7058 // Put focus back in useful location once modal is closed. 7059 if ( null !== this.clickedOpenerEl ) { 7060 this.clickedOpenerEl.focus(); 7061 } else { 7062 $( '#wpbody-content' ).focus(); 7063 } 7401 if ( mediaTrash && 'trash' === model.get( 'status' ) ) { 7402 model.set( 'status', 'inherit' ); 7403 changed.push( model.save() ); 7404 removed.push( model ); 7405 } else if ( mediaTrash ) { 7406 model.set( 'status', 'trash' ); 7407 changed.push( model.save() ); 7408 removed.push( model ); 7409 } else { 7410 model.destroy({wait: true}); 7411 } 7412 } ); 7064 7413 7065 this.propagate('close'); 7414 if ( changed.length ) { 7415 selection.remove( removed ); 7066 7416 7067 // If the `freeze` option is set, restore the container's scroll position. 7068 if ( freeze ) { 7069 $( window ).scrollTop( freeze.scrollTop ); 7070 } 7417 $.when.apply( null, changed ).then( _.bind( function() { 7418 library._requery( true ); 7419 this.controller.trigger( 'selection:action:done' ); 7420 }, this ) ); 7421 } else { 7422 this.controller.trigger( 'selection:action:done' ); 7423 } 7424 } 7425 }).render() ); 7071 7426 7072 if ( options && options.escape ) { 7073 this.propagate('escape'); 7074 } 7427 if ( mediaTrash ) { 7428 this.toolbar.set( 'deleteSelectedPermanentlyButton', new wp.media.view.DeleteSelectedPermanentlyButton({ 7429 filters: Filters, 7430 style: 'primary', 7431 disabled: true, 7432 text: l10n.deleteSelected, 7433 controller: this.controller, 7434 priority: -55, 7435 click: function() { 7436 var removed = [], 7437 destroy = [], 7438 selection = this.controller.state().get( 'selection' ); 7075 7439 7076 return this; 7077 }, 7078 /** 7079 * @returns {wp.media.view.Modal} Returns itself to allow chaining 7080 */ 7081 escape: function() { 7082 return this.close({ escape: true }); 7083 }, 7084 /** 7085 * @param {Object} event 7086 */ 7087 escapeHandler: function( event ) { 7088 event.preventDefault(); 7089 this.escape(); 7090 }, 7440 if ( ! selection.length || ! window.confirm( l10n.warnBulkDelete ) ) { 7441 return; 7442 } 7091 7443 7092 /** 7093 * @param {Array|Object} content Views to register to '.media-modal-content' 7094 * @returns {wp.media.view.Modal} Returns itself to allow chaining 7095 */ 7096 content: function( content ) { 7097 this.views.set( '.media-modal-content', content ); 7098 return this; 7099 }, 7444 selection.each( function( model ) { 7445 if ( ! model.get( 'nonces' )['delete'] ) { 7446 removed.push( model ); 7447 return; 7448 } 7100 7449 7101 /** 7102 * Triggers a modal event and if the `propagate` option is set, 7103 * forwards events to the modal's controller. 7104 * 7105 * @param {string} id 7106 * @returns {wp.media.view.Modal} Returns itself to allow chaining 7107 */ 7108 propagate: function( id ) { 7109 this.trigger( id ); 7450 destroy.push( model ); 7451 } ); 7110 7452 7111 if ( this.options.propagate ) { 7112 this.controller.trigger( id ); 7453 if ( removed.length ) { 7454 selection.remove( removed ); 7455 } 7456 7457 if ( destroy.length ) { 7458 $.when.apply( null, destroy.map( function (item) { 7459 return item.destroy(); 7460 } ) ).then( _.bind( function() { 7461 this.controller.trigger( 'selection:action:done' ); 7462 }, this ) ); 7463 } 7464 } 7465 }).render() ); 7466 } 7467 7468 } else if ( this.options.date ) { 7469 // DateFilter is a <select>, screen reader text needs to be rendered before 7470 this.toolbar.set( 'dateFilterLabel', new wp.media.view.Label({ 7471 value: l10n.filterByDate, 7472 attributes: { 7473 'for': 'media-attachment-date-filters' 7474 }, 7475 priority: -75 7476 }).render() ); 7477 this.toolbar.set( 'dateFilter', new wp.media.view.DateFilter({ 7478 controller: this.controller, 7479 model: this.collection.props, 7480 priority: -75 7481 }).render() ); 7113 7482 } 7114 7483 7115 return this; 7116 }, 7117 /** 7118 * @param {Object} event 7119 */ 7120 keydown: function( event ) { 7121 // Close the modal when escape is pressed. 7122 if ( 27 === event.which && this.$el.is(':visible') ) { 7123 this.escape(); 7124 event.stopImmediatePropagation(); 7484 if ( this.options.search ) { 7485 // Search is an input, screen reader text needs to be rendered before 7486 this.toolbar.set( 'searchLabel', new wp.media.view.Label({ 7487 value: l10n.searchMediaLabel, 7488 attributes: { 7489 'for': 'media-search-input' 7490 }, 7491 priority: 60 7492 }).render() ); 7493 this.toolbar.set( 'search', new wp.media.view.Search({ 7494 controller: this.controller, 7495 model: this.collection.props, 7496 priority: 60 7497 }).render() ); 7125 7498 } 7126 }7127 });7128 7499 7129 module.exports = Modal; 7500 if ( this.options.dragInfo ) { 7501 this.toolbar.set( 'dragInfo', new View({ 7502 el: $( '<div class="instructions">' + l10n.dragInfo + '</div>' )[0], 7503 priority: -40 7504 }) ); 7505 } 7130 7506 7131 },{}],54:[function(require,module,exports){ 7132 /** 7133 * wp.media.view.PriorityList 7134 * 7135 * @memberOf wp.media.view 7136 * 7137 * @class 7138 * @augments wp.media.View 7139 * @augments wp.Backbone.View 7140 * @augments Backbone.View 7141 */ 7142 var PriorityList = wp.media.View.extend(/** @lends wp.media.view.PriorityList.prototype */{ 7143 tagName: 'div', 7507 if ( this.options.suggestedWidth && this.options.suggestedHeight ) { 7508 this.toolbar.set( 'suggestedDimensions', new View({ 7509 el: $( '<div class="instructions">' + l10n.suggestedDimensions.replace( '%1$s', this.options.suggestedWidth ).replace( '%2$s', this.options.suggestedHeight ) + '</div>' )[0], 7510 priority: -40 7511 }) ); 7512 } 7513 }, 7144 7514 7145 initialize: function() { 7146 this._views = {}; 7515 updateContent: function() { 7516 var view = this, 7517 noItemsView; 7147 7518 7148 this.set( _.extend( {}, this._views, this.options.views ), { silent: true }); 7149 delete this.options.views; 7519 if ( this.controller.isModeActive( 'grid' ) ) { 7520 noItemsView = view.attachmentsNoResults; 7521 } else { 7522 noItemsView = view.uploader; 7523 } 7150 7524 7151 if ( ! this.options.silent ) { 7152 this.render(); 7525 if ( ! this.collection.length ) { 7526 this.toolbar.get( 'spinner' ).show(); 7527 this.dfd = this.collection.more().done( function() { 7528 if ( ! view.collection.length ) { 7529 noItemsView.$el.removeClass( 'hidden' ); 7530 } else { 7531 noItemsView.$el.addClass( 'hidden' ); 7532 } 7533 view.toolbar.get( 'spinner' ).hide(); 7534 } ); 7535 } else { 7536 noItemsView.$el.addClass( 'hidden' ); 7537 view.toolbar.get( 'spinner' ).hide(); 7153 7538 } 7154 7539 }, 7155 /**7156 * @param {string} id7157 * @param {wp.media.View|Object} view7158 * @param {Object} options7159 * @returns {wp.media.view.PriorityList} Returns itself to allow chaining7160 */7161 set: function( id, view, options ) {7162 var priority, views, index;7163 7540 7164 options = options || {}; 7541 createUploader: function() { 7542 this.uploader = new wp.media.view.UploaderInline({ 7543 controller: this.controller, 7544 status: false, 7545 message: this.controller.isModeActive( 'grid' ) ? '' : l10n.noItemsFound, 7546 canClose: this.controller.isModeActive( 'grid' ) 7547 }); 7165 7548 7166 // Accept an object with an `id` : `view` mapping. 7167 if ( _.isObject( id ) ) { 7168 _.each( id, function( view, id ) { 7169 this.set( id, view ); 7170 }, this ); 7171 return this; 7172 } 7549 this.uploader.$el.addClass( 'hidden' ); 7550 this.views.add( this.uploader ); 7551 }, 7173 7552 7174 if ( ! (view instanceof Backbone.View) ) { 7175 view = this.toView( view, id, options ); 7553 toggleUploader: function() { 7554 if ( this.uploader.$el.hasClass( 'hidden' ) ) { 7555 this.uploader.show(); 7556 } else { 7557 this.uploader.hide(); 7176 7558 } 7177 view.controller = view.controller || this.controller; 7178 7179 this.unset( id ); 7559 }, 7180 7560 7181 priority = view.options.priority || 10; 7182 views = this.views.get() || []; 7561 createAttachments: function() { 7562 this.attachments = new wp.media.view.Attachments({ 7563 controller: this.controller, 7564 collection: this.collection, 7565 selection: this.options.selection, 7566 model: this.model, 7567 sortable: this.options.sortable, 7568 scrollElement: this.options.scrollElement, 7569 idealColumnWidth: this.options.idealColumnWidth, 7183 7570 7184 _.find( views, function( existing, i ) { 7185 if ( existing.options.priority > priority ) { 7186 index = i; 7187 return true; 7188 } 7571 // The single `Attachment` view to be used in the `Attachments` view. 7572 AttachmentView: this.options.AttachmentView 7189 7573 }); 7190 7574 7191 this._views[ id ] = view; 7192 this.views.add( view, { 7193 at: _.isNumber( index ) ? index : views.length || 0 7194 }); 7575 // Add keydown listener to the instance of the Attachments view 7576 this.controller.on( 'attachment:keydown:arrow', _.bind( this.attachments.arrowEvent, this.attachments ) ); 7577 this.controller.on( 'attachment:details:shift-tab', _.bind( this.attachments.restoreFocus, this.attachments ) ); 7195 7578 7196 return this; 7197 }, 7198 /** 7199 * @param {string} id 7200 * @returns {wp.media.View} 7201 */ 7202 get: function( id ) { 7203 return this._views[ id ]; 7204 }, 7205 /** 7206 * @param {string} id 7207 * @returns {wp.media.view.PriorityList} 7208 */ 7209 unset: function( id ) { 7210 var view = this.get( id ); 7579 this.views.add( this.attachments ); 7211 7580 7212 if ( view ) {7213 view.remove();7214 }7215 7581 7216 delete this._views[ id ]; 7217 return this; 7218 }, 7219 /** 7220 * @param {Object} options 7221 * @returns {wp.media.View} 7222 */ 7223 toView: function( options ) { 7224 return new wp.media.View( options ); 7225 } 7226 }); 7582 if ( this.controller.isModeActive( 'grid' ) ) { 7583 this.attachmentsNoResults = new View({ 7584 controller: this.controller, 7585 tagName: 'p' 7586 }); 7227 7587 7228 module.exports = PriorityList; 7588 this.attachmentsNoResults.$el.addClass( 'hidden no-media' ); 7589 this.attachmentsNoResults.$el.html( l10n.noMedia ); 7229 7590 7230 },{}],55:[function(require,module,exports){ 7231 /** 7232 * wp.media.view.RouterItem 7233 * 7234 * @memberOf wp.media.view 7235 * 7236 * @class 7237 * @augments wp.media.view.MenuItem 7238 * @augments wp.media.View 7239 * @augments wp.Backbone.View 7240 * @augments Backbone.View 7241 */ 7242 var RouterItem = wp.media.view.MenuItem.extend(/** @lends wp.media.view.RouterItem.prototype */{ 7243 /** 7244 * On click handler to activate the content region's corresponding mode. 7245 */ 7246 click: function() { 7247 var contentMode = this.options.contentMode; 7248 if ( contentMode ) { 7249 this.controller.content.mode( contentMode ); 7591 this.views.add( this.attachmentsNoResults ); 7250 7592 } 7251 } 7252 }); 7593 }, 7253 7594 7254 module.exports = RouterItem; 7595 createSidebar: function() { 7596 var options = this.options, 7597 selection = options.selection, 7598 sidebar = this.sidebar = new wp.media.view.Sidebar({ 7599 controller: this.controller 7600 }); 7255 7601 7256 },{}],56:[function(require,module,exports){ 7257 var Menu = wp.media.view.Menu, 7258 Router; 7602 this.views.add( sidebar ); 7259 7603 7260 /** 7261 * wp.media.view.Router 7262 * 7263 * @memberOf wp.media.view 7264 * 7265 * @class 7266 * @augments wp.media.view.Menu 7267 * @augments wp.media.view.PriorityList 7268 * @augments wp.media.View 7269 * @augments wp.Backbone.View 7270 * @augments Backbone.View 7271 */ 7272 Router = Menu.extend(/** @lends wp.media.view.Router.prototype */{ 7273 tagName: 'div', 7274 className: 'media-router', 7275 property: 'contentMode', 7276 ItemView: wp.media.view.RouterItem, 7277 region: 'router', 7604 if ( this.controller.uploader ) { 7605 sidebar.set( 'uploads', new wp.media.view.UploaderStatus({ 7606 controller: this.controller, 7607 priority: 40 7608 }) ); 7609 } 7278 7610 7279 initialize: function() { 7280 this.controller.on( 'content:render', this.update, this ); 7281 // Call 'initialize' directly on the parent class. 7282 Menu.prototype.initialize.apply( this, arguments ); 7283 }, 7611 selection.on( 'selection:single', this.createSingle, this ); 7612 selection.on( 'selection:unsingle', this.disposeSingle, this ); 7284 7613 7285 update: function() { 7286 var mode = this.controller.content.mode(); 7287 if ( mode ) { 7288 this.select( mode ); 7614 if ( selection.single() ) { 7615 this.createSingle(); 7289 7616 } 7290 } 7291 }); 7617 }, 7618 7619 createSingle: function() { 7620 var sidebar = this.sidebar, 7621 single = this.options.selection.single(); 7292 7622 7293 module.exports = Router; 7623 sidebar.set( 'details', new wp.media.view.Attachment.Details({ 7624 controller: this.controller, 7625 model: single, 7626 priority: 80 7627 }) ); 7294 7628 7295 },{}],57:[function(require,module,exports){ 7296 var l10n = wp.media.view.l10n, 7297 Search; 7629 sidebar.set( 'compat', new wp.media.view.AttachmentCompat({ 7630 controller: this.controller, 7631 model: single, 7632 priority: 120 7633 }) ); 7298 7634 7299 /** 7300 * wp.media.view.Search 7301 * 7302 * @memberOf wp.media.view 7303 * 7304 * @class 7305 * @augments wp.media.View 7306 * @augments wp.Backbone.View 7307 * @augments Backbone.View 7308 */ 7309 Search = wp.media.View.extend(/** @lends wp.media.view.Search.prototype */{ 7310 tagName: 'input', 7311 className: 'search', 7312 id: 'media-search-input', 7635 if ( this.options.display ) { 7636 sidebar.set( 'display', new wp.media.view.Settings.AttachmentDisplay({ 7637 controller: this.controller, 7638 model: this.model.display( single ), 7639 attachment: single, 7640 priority: 160, 7641 userSettings: this.model.get('displayUserSettings') 7642 }) ); 7643 } 7313 7644 7314 attributes: { 7315 type: 'search', 7316 placeholder: l10n.searchMediaPlaceholder 7645 // Show the sidebar on mobile 7646 if ( this.model.id === 'insert' ) { 7647 sidebar.$el.addClass( 'visible' ); 7648 } 7317 7649 }, 7318 7650 7319 events: { 7320 'input': 'search', 7321 'keyup': 'search' 7322 }, 7651 disposeSingle: function() { 7652 var sidebar = this.sidebar; 7653 sidebar.unset('details'); 7654 sidebar.unset('compat'); 7655 sidebar.unset('display'); 7656 // Hide the sidebar on mobile 7657 sidebar.$el.removeClass( 'visible' ); 7658 } 7659 }); 7323 7660 7324 /** 7325 * @returns {wp.media.view.Search} Returns itself to allow chaining 7326 */ 7327 render: function() { 7328 this.el.value = this.model.escape('search'); 7329 return this; 7330 }, 7661 module.exports = AttachmentsBrowser; 7331 7662 7332 search: _.debounce( function( event ) {7333 if ( event.target.value ) {7334 this.model.set( 'search', event.target.value );7335 } else {7336 this.model.unset('search');7337 }7338 }, 300 )7339 });7340 7663 7341 module.exports = Search; 7664 /***/ }), 7665 /* 79 */ 7666 /***/ (function(module, exports) { 7342 7667 7343 },{}],58:[function(require,module,exports){7344 7668 var l10n = wp.media.view.l10n, 7345 7669 Selection; 7346 7670 … … Selection = wp.media.View.extend(/** @lends wp.media.view.Selection.prototype */ 7425 7749 7426 7750 module.exports = Selection; 7427 7751 7428 },{}],59:[function(require,module,exports){ 7752 7753 /***/ }), 7754 /* 80 */ 7755 /***/ (function(module, exports) { 7756 7757 /** 7758 * wp.media.view.Attachment.Selection 7759 * 7760 * @memberOf wp.media.view.Attachment 7761 * 7762 * @class 7763 * @augments wp.media.view.Attachment 7764 * @augments wp.media.View 7765 * @augments wp.Backbone.View 7766 * @augments Backbone.View 7767 */ 7768 var Selection = wp.media.view.Attachment.extend(/** @lends wp.media.view.Attachment.Selection.prototype */{ 7769 className: 'attachment selection', 7770 7771 // On click, just select the model, instead of removing the model from 7772 // the selection. 7773 toggleSelection: function() { 7774 this.options.selection.single( this.model ); 7775 } 7776 }); 7777 7778 module.exports = Selection; 7779 7780 7781 /***/ }), 7782 /* 81 */ 7783 /***/ (function(module, exports) { 7784 7785 var Attachments = wp.media.view.Attachments, 7786 Selection; 7787 7788 /** 7789 * wp.media.view.Attachments.Selection 7790 * 7791 * @memberOf wp.media.view.Attachments 7792 * 7793 * @class 7794 * @augments wp.media.view.Attachments 7795 * @augments wp.media.View 7796 * @augments wp.Backbone.View 7797 * @augments Backbone.View 7798 */ 7799 Selection = Attachments.extend(/** @lends wp.media.view.Attachments.Selection.prototype */{ 7800 events: {}, 7801 initialize: function() { 7802 _.defaults( this.options, { 7803 sortable: false, 7804 resize: false, 7805 7806 // The single `Attachment` view to be used in the `Attachments` view. 7807 AttachmentView: wp.media.view.Attachment.Selection 7808 }); 7809 // Call 'initialize' directly on the parent class. 7810 return Attachments.prototype.initialize.apply( this, arguments ); 7811 } 7812 }); 7813 7814 module.exports = Selection; 7815 7816 7817 /***/ }), 7818 /* 82 */ 7819 /***/ (function(module, exports) { 7820 7821 /** 7822 * wp.media.view.Attachment.EditSelection 7823 * 7824 * @memberOf wp.media.view.Attachment 7825 * 7826 * @class 7827 * @augments wp.media.view.Attachment.Selection 7828 * @augments wp.media.view.Attachment 7829 * @augments wp.media.View 7830 * @augments wp.Backbone.View 7831 * @augments Backbone.View 7832 */ 7833 var EditSelection = wp.media.view.Attachment.Selection.extend(/** @lends wp.media.view.Attachment.EditSelection.prototype */{ 7834 buttons: { 7835 close: true 7836 } 7837 }); 7838 7839 module.exports = EditSelection; 7840 7841 7842 /***/ }), 7843 /* 83 */ 7844 /***/ (function(module, exports) { 7845 7429 7846 var View = wp.media.View, 7430 7847 $ = Backbone.$, 7431 7848 Settings; … … Settings = View.extend(/** @lends wp.media.view.Settings.prototype */{ 7548 7965 7549 7966 module.exports = Settings; 7550 7967 7551 },{}],60:[function(require,module,exports){ 7968 7969 /***/ }), 7970 /* 84 */ 7971 /***/ (function(module, exports) { 7972 7552 7973 var Settings = wp.media.view.Settings, 7553 7974 AttachmentDisplay; 7554 7975 … … AttachmentDisplay = Settings.extend(/** @lends wp.media.view.Settings.Attachment 7625 8046 if ( 'post' === linkTo ) { 7626 8047 $input.val( attachment.get('link') ); 7627 8048 } else if ( 'file' === linkTo ) { 7628 $input.val( attachment.get('url') ); 7629 } else if ( ! this.model.get('linkUrl') ) { 7630 $input.val('http://'); 7631 } 7632 7633 $input.prop( 'readonly', 'custom' !== linkTo ); 7634 } 7635 7636 $input.removeClass( 'hidden' ); 7637 7638 // If the input is visible, focus and select its contents. 7639 if ( ! wp.media.isTouchDevice && $input.is(':visible') ) { 7640 $input.focus()[0].select(); 7641 } 7642 } 7643 }); 7644 7645 module.exports = AttachmentDisplay; 7646 7647 },{}],61:[function(require,module,exports){ 7648 /** 7649 * wp.media.view.Settings.Gallery 7650 * 7651 * @memberOf wp.media.view.Settings 7652 * 7653 * @class 7654 * @augments wp.media.view.Settings 7655 * @augments wp.media.View 7656 * @augments wp.Backbone.View 7657 * @augments Backbone.View 7658 */ 7659 var Gallery = wp.media.view.Settings.extend(/** @lends wp.media.view.Settings.Gallery.prototype */{ 7660 className: 'collection-settings gallery-settings', 7661 template: wp.template('gallery-settings') 7662 }); 7663 7664 module.exports = Gallery; 7665 7666 },{}],62:[function(require,module,exports){ 7667 /** 7668 * wp.media.view.Settings.Playlist 7669 * 7670 * @memberOf wp.media.view.Settings 7671 * 7672 * @class 7673 * @augments wp.media.view.Settings 7674 * @augments wp.media.View 7675 * @augments wp.Backbone.View 7676 * @augments Backbone.View 7677 */ 7678 var Playlist = wp.media.view.Settings.extend(/** @lends wp.media.view.Settings.Playlist.prototype */{ 7679 className: 'collection-settings playlist-settings', 7680 template: wp.template('playlist-settings') 7681 }); 7682 7683 module.exports = Playlist; 7684 7685 },{}],63:[function(require,module,exports){ 7686 /** 7687 * wp.media.view.Sidebar 7688 * 7689 * @memberOf wp.media.view 7690 * 7691 * @class 7692 * @augments wp.media.view.PriorityList 7693 * @augments wp.media.View 7694 * @augments wp.Backbone.View 7695 * @augments Backbone.View 7696 */ 7697 var Sidebar = wp.media.view.PriorityList.extend(/** @lends wp.media.view.Sidebar.prototype */{ 7698 className: 'media-sidebar' 7699 }); 7700 7701 module.exports = Sidebar; 7702 7703 },{}],64:[function(require,module,exports){ 7704 var View = wp.media.view, 7705 SiteIconCropper; 7706 7707 /** 7708 * wp.media.view.SiteIconCropper 7709 * 7710 * Uses the imgAreaSelect plugin to allow a user to crop a Site Icon. 7711 * 7712 * Takes imgAreaSelect options from 7713 * wp.customize.SiteIconControl.calculateImageSelectOptions. 7714 * 7715 * @memberOf wp.media.view 7716 * 7717 * @class 7718 * @augments wp.media.view.Cropper 7719 * @augments wp.media.View 7720 * @augments wp.Backbone.View 7721 * @augments Backbone.View 7722 */ 7723 SiteIconCropper = View.Cropper.extend(/** @lends wp.media.view.SiteIconCropper.prototype */{ 7724 className: 'crop-content site-icon', 7725 7726 ready: function () { 7727 View.Cropper.prototype.ready.apply( this, arguments ); 7728 7729 this.$( '.crop-image' ).on( 'load', _.bind( this.addSidebar, this ) ); 7730 }, 8049 $input.val( attachment.get('url') ); 8050 } else if ( ! this.model.get('linkUrl') ) { 8051 $input.val('http://'); 8052 } 7731 8053 7732 addSidebar: function() { 7733 this.sidebar = new wp.media.view.Sidebar({ 7734 controller: this.controller 7735 }); 8054 $input.prop( 'readonly', 'custom' !== linkTo ); 8055 } 7736 8056 7737 this.sidebar.set( 'preview', new wp.media.view.SiteIconPreview({ 7738 controller: this.controller, 7739 attachment: this.options.attachment 7740 }) ); 8057 $input.removeClass( 'hidden' ); 7741 8058 7742 this.controller.cropperView.views.add( this.sidebar ); 8059 // If the input is visible, focus and select its contents. 8060 if ( ! wp.media.isTouchDevice && $input.is(':visible') ) { 8061 $input.focus()[0].select(); 8062 } 7743 8063 } 7744 8064 }); 7745 8065 7746 module.exports = SiteIconCropper;8066 module.exports = AttachmentDisplay; 7747 8067 7748 },{}],65:[function(require,module,exports){ 7749 var View = wp.media.View,7750 $ = jQuery, 7751 SiteIconPreview; 8068 8069 /***/ }), 8070 /* 85 */ 8071 /***/ (function(module, exports) { 7752 8072 7753 8073 /** 7754 * wp.media.view.SiteIconPreview 7755 * 7756 * Shows a preview of the Site Icon as a favicon and app icon while cropping. 8074 * wp.media.view.Settings.Gallery 7757 8075 * 7758 * @memberOf wp.media.view 8076 * @memberOf wp.media.view.Settings 7759 8077 * 7760 8078 * @class 8079 * @augments wp.media.view.Settings 7761 8080 * @augments wp.media.View 7762 8081 * @augments wp.Backbone.View 7763 8082 * @augments Backbone.View 7764 8083 */ 7765 SiteIconPreview = View.extend(/** @lends wp.media.view.SiteIconPreview.prototype */{ 7766 className: 'site-icon-preview', 7767 template: wp.template( 'site-icon-preview' ), 7768 7769 ready: function() { 7770 this.controller.imgSelect.setOptions({ 7771 onInit: this.updatePreview, 7772 onSelectChange: this.updatePreview 7773 }); 7774 }, 7775 7776 prepare: function() { 7777 return { 7778 url: this.options.attachment.get( 'url' ) 7779 }; 7780 }, 7781 7782 updatePreview: function( img, coords ) { 7783 var rx = 64 / coords.width, 7784 ry = 64 / coords.height, 7785 preview_rx = 16 / coords.width, 7786 preview_ry = 16 / coords.height; 8084 var Gallery = wp.media.view.Settings.extend(/** @lends wp.media.view.Settings.Gallery.prototype */{ 8085 className: 'collection-settings gallery-settings', 8086 template: wp.template('gallery-settings') 8087 }); 7787 8088 7788 $( '#preview-app-icon' ).css({ 7789 width: Math.round(rx * this.imageWidth ) + 'px', 7790 height: Math.round(ry * this.imageHeight ) + 'px', 7791 marginLeft: '-' + Math.round(rx * coords.x1) + 'px', 7792 marginTop: '-' + Math.round(ry * coords.y1) + 'px' 7793 }); 8089 module.exports = Gallery; 7794 8090 7795 $( '#preview-favicon' ).css({7796 width: Math.round( preview_rx * this.imageWidth ) + 'px',7797 height: Math.round( preview_ry * this.imageHeight ) + 'px',7798 marginLeft: '-' + Math.round( preview_rx * coords.x1 ) + 'px',7799 marginTop: '-' + Math.floor( preview_ry* coords.y1 ) + 'px'7800 });7801 }7802 });7803 8091 7804 module.exports = SiteIconPreview; 8092 /***/ }), 8093 /* 86 */ 8094 /***/ (function(module, exports) { 7805 8095 7806 },{}],66:[function(require,module,exports){7807 8096 /** 7808 * wp.media.view.S pinner8097 * wp.media.view.Settings.Playlist 7809 8098 * 7810 * @memberOf wp.media.view 8099 * @memberOf wp.media.view.Settings 7811 8100 * 7812 8101 * @class 8102 * @augments wp.media.view.Settings 7813 8103 * @augments wp.media.View 7814 8104 * @augments wp.Backbone.View 7815 8105 * @augments Backbone.View 7816 8106 */ 7817 var Spinner = wp.media.View.extend(/** @lends wp.media.view.Spinner.prototype */{ 7818 tagName: 'span', 7819 className: 'spinner', 7820 spinnerTimeout: false, 7821 delay: 400, 7822 7823 show: function() { 7824 if ( ! this.spinnerTimeout ) { 7825 this.spinnerTimeout = _.delay(function( $el ) { 7826 $el.addClass( 'is-active' ); 7827 }, this.delay, this.$el ); 7828 } 7829 7830 return this; 7831 }, 8107 var Playlist = wp.media.view.Settings.extend(/** @lends wp.media.view.Settings.Playlist.prototype */{ 8108 className: 'collection-settings playlist-settings', 8109 template: wp.template('playlist-settings') 8110 }); 7832 8111 7833 hide: function() { 7834 this.$el.removeClass( 'is-active' ); 7835 this.spinnerTimeout = clearTimeout( this.spinnerTimeout ); 8112 module.exports = Playlist; 7836 8113 7837 return this;7838 }7839 });7840 8114 7841 module.exports = Spinner; 8115 /***/ }), 8116 /* 87 */ 8117 /***/ (function(module, exports) { 7842 8118 7843 },{}],67:[function(require,module,exports){ 7844 var View = wp.media.View,7845 Toolbar;8119 var Attachment = wp.media.view.Attachment, 8120 l10n = wp.media.view.l10n, 8121 Details; 7846 8122 7847 8123 /** 7848 * wp.media.view.Toolbar 7849 * 7850 * A toolbar which consists of a primary and a secondary section. Each sections 7851 * can be filled with views. 8124 * wp.media.view.Attachment.Details 7852 8125 * 7853 * @memberOf wp.media.view 8126 * @memberOf wp.media.view.Attachment 7854 8127 * 7855 8128 * @class 8129 * @augments wp.media.view.Attachment 7856 8130 * @augments wp.media.View 7857 8131 * @augments wp.Backbone.View 7858 8132 * @augments Backbone.View 7859 8133 */ 7860 Toolbar = View.extend(/** @lends wp.media.view.Toolbar.prototype */{8134 Details = Attachment.extend(/** @lends wp.media.view.Attachment.Details.prototype */{ 7861 8135 tagName: 'div', 7862 className: 'media-toolbar', 8136 className: 'attachment-details', 8137 template: wp.template('attachment-details'), 8138 8139 attributes: function() { 8140 return { 8141 'tabIndex': 0, 8142 'data-id': this.model.get( 'id' ) 8143 }; 8144 }, 8145 8146 events: { 8147 'change [data-setting]': 'updateSetting', 8148 'change [data-setting] input': 'updateSetting', 8149 'change [data-setting] select': 'updateSetting', 8150 'change [data-setting] textarea': 'updateSetting', 8151 'click .delete-attachment': 'deleteAttachment', 8152 'click .trash-attachment': 'trashAttachment', 8153 'click .untrash-attachment': 'untrashAttachment', 8154 'click .edit-attachment': 'editAttachment', 8155 'keydown': 'toggleSelectionHandler' 8156 }, 7863 8157 7864 8158 initialize: function() { 7865 var state = this.controller.state(),7866 selection = this.selection = state.get('selection'),7867 library = this.library = state.get('library');8159 this.options = _.defaults( this.options, { 8160 rerenderOnModelChange: false 8161 }); 7868 8162 7869 this._views = {}; 8163 this.on( 'ready', this.initialFocus ); 8164 // Call 'initialize' directly on the parent class. 8165 Attachment.prototype.initialize.apply( this, arguments ); 8166 }, 7870 8167 7871 // The toolbar is composed of two `PriorityList` views. 7872 this.primary = new wp.media.view.PriorityList(); 7873 this.secondary = new wp.media.view.PriorityList(); 7874 this.primary.$el.addClass('media-toolbar-primary search-form'); 7875 this.secondary.$el.addClass('media-toolbar-secondary'); 8168 initialFocus: function() { 8169 if ( ! wp.media.isTouchDevice ) { 8170 /* 8171 Previously focused the first ':input' (the readonly URL text field). 8172 Since the first ':input' is now a button (delete/trash): when pressing 8173 spacebar on an attachment, Firefox fires deleteAttachment/trashAttachment 8174 as soon as focus is moved. Explicitly target the first text field for now. 8175 @todo change initial focus logic, also for accessibility. 8176 */ 8177 this.$( 'input[type="text"]' ).eq( 0 ).focus(); 8178 } 8179 }, 8180 /** 8181 * @param {Object} event 8182 */ 8183 deleteAttachment: function( event ) { 8184 event.preventDefault(); 7876 8185 7877 this.views.set([ this.secondary, this.primary ]); 8186 if ( window.confirm( l10n.warnDelete ) ) { 8187 this.model.destroy(); 8188 // Keep focus inside media modal 8189 // after image is deleted 8190 this.controller.modal.focusManager.focus(); 8191 } 8192 }, 8193 /** 8194 * @param {Object} event 8195 */ 8196 trashAttachment: function( event ) { 8197 var library = this.controller.library; 8198 event.preventDefault(); 7878 8199 7879 if ( this.options.items ) { 7880 this.set( this.options.items, { silent: true }); 8200 if ( wp.media.view.settings.mediaTrash && 8201 'edit-metadata' === this.controller.content.mode() ) { 8202 8203 this.model.set( 'status', 'trash' ); 8204 this.model.save().done( function() { 8205 library._requery( true ); 8206 } ); 8207 } else { 8208 this.model.destroy(); 7881 8209 } 8210 }, 8211 /** 8212 * @param {Object} event 8213 */ 8214 untrashAttachment: function( event ) { 8215 var library = this.controller.library; 8216 event.preventDefault(); 7882 8217 7883 if ( ! this.options.silent ) { 7884 this.render(); 8218 this.model.set( 'status', 'inherit' ); 8219 this.model.save().done( function() { 8220 library._requery( true ); 8221 } ); 8222 }, 8223 /** 8224 * @param {Object} event 8225 */ 8226 editAttachment: function( event ) { 8227 var editState = this.controller.states.get( 'edit-image' ); 8228 if ( window.imageEdit && editState ) { 8229 event.preventDefault(); 8230 8231 editState.set( 'image', this.model ); 8232 this.controller.setState( 'edit-image' ); 8233 } else { 8234 this.$el.addClass('needs-refresh'); 8235 } 8236 }, 8237 /** 8238 * When reverse tabbing(shift+tab) out of the right details panel, deliver 8239 * the focus to the item in the list that was being edited. 8240 * 8241 * @param {Object} event 8242 */ 8243 toggleSelectionHandler: function( event ) { 8244 if ( 'keydown' === event.type && 9 === event.keyCode && event.shiftKey && event.target === this.$( ':tabbable' ).get( 0 ) ) { 8245 this.controller.trigger( 'attachment:details:shift-tab', event ); 8246 return false; 7885 8247 } 7886 8248 7887 if ( selection ) { 7888 selection.on( 'add remove reset', this.refresh, this ); 7889 } 8249 if ( 37 === event.keyCode || 38 === event.keyCode || 39 === event.keyCode || 40 === event.keyCode ) { 8250 this.controller.trigger( 'attachment:keydown:arrow', event ); 8251 return; 8252 } 8253 } 8254 }); 8255 8256 module.exports = Details; 8257 8258 8259 /***/ }), 8260 /* 88 */ 8261 /***/ (function(module, exports) { 8262 8263 var View = wp.media.View, 8264 AttachmentCompat; 8265 8266 /** 8267 * wp.media.view.AttachmentCompat 8268 * 8269 * A view to display fields added via the `attachment_fields_to_edit` filter. 8270 * 8271 * @memberOf wp.media.view 8272 * 8273 * @class 8274 * @augments wp.media.View 8275 * @augments wp.Backbone.View 8276 * @augments Backbone.View 8277 */ 8278 AttachmentCompat = View.extend(/** @lends wp.media.view.AttachmentCompat.prototype */{ 8279 tagName: 'form', 8280 className: 'compat-item', 8281 8282 events: { 8283 'submit': 'preventDefault', 8284 'change input': 'save', 8285 'change select': 'save', 8286 'change textarea': 'save' 8287 }, 7890 8288 7891 if ( library ) { 7892 library.on( 'add remove reset', this.refresh, this ); 7893 } 8289 initialize: function() { 8290 this.listenTo( this.model, 'change:compat', this.render ); 7894 8291 }, 7895 8292 /** 7896 * @returns {wp.media.view. Toolbar} Returns itsef to allow chaining8293 * @returns {wp.media.view.AttachmentCompat} Returns itself to allow chaining 7897 8294 */ 7898 8295 dispose: function() { 7899 if ( this.selection ) { 7900 this.selection.off( null, null, this ); 7901 } 7902 7903 if ( this.library ) { 7904 this.library.off( null, null, this ); 8296 if ( this.$(':focus').length ) { 8297 this.save(); 7905 8298 } 7906 8299 /** 7907 8300 * call 'dispose' directly on the parent class 7908 8301 */ 7909 8302 return View.prototype.dispose.apply( this, arguments ); 7910 8303 }, 7911 7912 ready: function() {7913 this.refresh();7914 },7915 7916 8304 /** 7917 * @param {string} id 7918 * @param {Backbone.View|Object} view 7919 * @param {Object} [options={}] 7920 * @returns {wp.media.view.Toolbar} Returns itself to allow chaining 8305 * @returns {wp.media.view.AttachmentCompat} Returns itself to allow chaining 7921 8306 */ 7922 set: function( id, view, options ) { 7923 var list; 7924 options = options || {}; 7925 7926 // Accept an object with an `id` : `view` mapping. 7927 if ( _.isObject( id ) ) { 7928 _.each( id, function( view, id ) { 7929 this.set( id, view, { silent: true }); 7930 }, this ); 7931 7932 } else { 7933 if ( ! ( view instanceof Backbone.View ) ) { 7934 view.classes = [ 'media-button-' + id ].concat( view.classes || [] ); 7935 view = new wp.media.view.Button( view ).render(); 7936 } 7937 7938 view.controller = view.controller || this.controller; 7939 7940 this._views[ id ] = view; 7941 7942 list = view.options.priority < 0 ? 'secondary' : 'primary'; 7943 this[ list ].set( id, view, options ); 7944 } 7945 7946 if ( ! options.silent ) { 7947 this.refresh(); 8307 render: function() { 8308 var compat = this.model.get('compat'); 8309 if ( ! compat || ! compat.item ) { 8310 return; 7948 8311 } 7949 8312 8313 this.views.detach(); 8314 this.$el.html( compat.item ); 8315 this.views.render(); 7950 8316 return this; 7951 8317 }, 7952 8318 /** 7953 * @param {string} id 7954 * @returns {wp.media.view.Button} 8319 * @param {Object} event 7955 8320 */ 7956 get: function( id) {7957 return this._views[ id ];8321 preventDefault: function( event ) { 8322 event.preventDefault(); 7958 8323 }, 7959 8324 /** 7960 * @param {string} id 7961 * @param {Object} options 7962 * @returns {wp.media.view.Toolbar} Returns itself to allow chaining 8325 * @param {Object} event 7963 8326 */ 7964 unset: function( id, options ) { 7965 delete this._views[ id ]; 7966 this.primary.unset( id, options ); 7967 this.secondary.unset( id, options ); 8327 save: function( event ) { 8328 var data = {}; 7968 8329 7969 if ( ! options || ! options.silent ) {7970 this.refresh();8330 if ( event ) { 8331 event.preventDefault(); 7971 8332 } 7972 return this;7973 },7974 7975 refresh: function() {7976 var state = this.controller.state(),7977 library = state.get('library'),7978 selection = state.get('selection');7979 7980 _.each( this._views, function( button ) {7981 if ( ! button.model || ! button.options || ! button.options.requires ) {7982 return;7983 }7984 8333 7985 var requires = button.options.requires, 7986 disabled = false; 8334 _.each( this.$el.serializeArray(), function( pair ) { 8335 data[ pair.name ] = pair.value; 8336 }); 7987 8337 7988 // Prevent insertion of attachments if any of them are still uploading 7989 if ( selection && selection.models ) { 7990 disabled = _.some( selection.models, function( attachment ) { 7991 return attachment.get('uploading') === true; 7992 }); 7993 } 8338 this.controller.trigger( 'attachment:compat:waiting', ['waiting'] ); 8339 this.model.saveCompat( data ).always( _.bind( this.postSave, this ) ); 8340 }, 7994 8341 7995 if ( requires.selection && selection && ! selection.length ) { 7996 disabled = true; 7997 } else if ( requires.library && library && ! library.length ) { 7998 disabled = true; 7999 } 8000 button.model.set( 'disabled', disabled ); 8001 }); 8342 postSave: function() { 8343 this.controller.trigger( 'attachment:compat:ready', ['ready'] ); 8002 8344 } 8003 8345 }); 8004 8346 8005 module.exports = Toolbar;8347 module.exports = AttachmentCompat; 8006 8348 8007 },{}],68:[function(require,module,exports){ 8008 var Select = wp.media.view.Toolbar.Select,8009 l10n = wp.media.view.l10n, 8010 Embed; 8349 8350 /***/ }), 8351 /* 89 */ 8352 /***/ (function(module, exports) { 8011 8353 8012 8354 /** 8013 * wp.media.view. Toolbar.Embed8355 * wp.media.view.Iframe 8014 8356 * 8015 * @memberOf wp.media.view .Toolbar8357 * @memberOf wp.media.view 8016 8358 * 8017 8359 * @class 8018 * @augments wp.media.view.Toolbar.Select8019 * @augments wp.media.view.Toolbar8020 8360 * @augments wp.media.View 8021 8361 * @augments wp.Backbone.View 8022 8362 * @augments Backbone.View 8023 8363 */ 8024 Embed = Select.extend(/** @lends wp.media.view.Toolbar.Embed.prototype */{ 8025 initialize: function() { 8026 _.defaults( this.options, { 8027 text: l10n.insertIntoPost, 8028 requires: false 8029 }); 8030 // Call 'initialize' directly on the parent class. 8031 Select.prototype.initialize.apply( this, arguments ); 8032 }, 8033 8034 refresh: function() { 8035 var url = this.controller.state().props.get('url'); 8036 this.get('select').model.set( 'disabled', ! url || url === 'http://' ); 8037 /** 8038 * call 'refresh' directly on the parent class 8039 */ 8040 Select.prototype.refresh.apply( this, arguments ); 8364 var Iframe = wp.media.View.extend(/** @lends wp.media.view.Iframe.prototype */{ 8365 className: 'media-iframe', 8366 /** 8367 * @returns {wp.media.view.Iframe} Returns itself to allow chaining 8368 */ 8369 render: function() { 8370 this.views.detach(); 8371 this.$el.html( '<iframe src="' + this.controller.state().get('src') + '" />' ); 8372 this.views.render(); 8373 return this; 8041 8374 } 8042 8375 }); 8043 8376 8044 module.exports = Embed;8377 module.exports = Iframe; 8045 8378 8046 },{}],69:[function(require,module,exports){ 8047 var Toolbar = wp.media.view.Toolbar,8048 l10n = wp.media.view.l10n, 8049 Select; 8379 8380 /***/ }), 8381 /* 90 */ 8382 /***/ (function(module, exports) { 8050 8383 8051 8384 /** 8052 * wp.media.view. Toolbar.Select8385 * wp.media.view.Embed 8053 8386 * 8054 * @memberOf wp.media.view .Toolbar8387 * @memberOf wp.media.view 8055 8388 * 8056 8389 * @class 8057 * @augments wp.media.view.Toolbar8058 8390 * @augments wp.media.View 8059 8391 * @augments wp.Backbone.View 8060 8392 * @augments Backbone.View 8061 8393 */ 8062 Select = Toolbar.extend(/** @lends wp.media.view.Toolbar.Select.prototype */{ 8063 initialize: function() { 8064 var options = this.options; 8065 8066 _.bindAll( this, 'clickSelect' ); 8067 8068 _.defaults( options, { 8069 event: 'select', 8070 state: false, 8071 reset: true, 8072 close: true, 8073 text: l10n.select, 8394 var Embed = wp.media.View.extend(/** @lends wp.media.view.Ember.prototype */{ 8395 className: 'media-embed', 8074 8396 8075 // Does the button rely on the selection? 8076 requires: { 8077 selection: true 8078 } 8079 }); 8397 initialize: function() { 8398 /** 8399 * @member {wp.media.view.EmbedUrl} 8400 */ 8401 this.url = new wp.media.view.EmbedUrl({ 8402 controller: this.controller, 8403 model: this.model.props 8404 }).render(); 8080 8405 8081 options.items = _.defaults( options.items || {}, { 8082 select: { 8083 style: 'primary', 8084 text: options.text, 8085 priority: 80, 8086 click: this.clickSelect, 8087 requires: options.requires 8088 } 8089 }); 8090 // Call 'initialize' directly on the parent class. 8091 Toolbar.prototype.initialize.apply( this, arguments ); 8406 this.views.set([ this.url ]); 8407 this.refresh(); 8408 this.listenTo( this.model, 'change:type', this.refresh ); 8409 this.listenTo( this.model, 'change:loading', this.loading ); 8092 8410 }, 8093 8411 8094 clickSelect: function() {8095 var options = this.options,8096 controller = this.controller;8097 8098 if ( options.close) {8099 controller.close();8412 /** 8413 * @param {Object} view 8414 */ 8415 settings: function( view ) { 8416 if ( this._settings ) { 8417 this._settings.remove(); 8100 8418 } 8419 this._settings = view; 8420 this.views.add( view ); 8421 }, 8101 8422 8102 if ( options.event ) { 8103 controller.state().trigger( options.event ); 8423 refresh: function() { 8424 var type = this.model.get('type'), 8425 constructor; 8426 8427 if ( 'image' === type ) { 8428 constructor = wp.media.view.EmbedImage; 8429 } else if ( 'link' === type ) { 8430 constructor = wp.media.view.EmbedLink; 8431 } else { 8432 return; 8104 8433 } 8105 8434 8106 if ( options.state ) { 8107 controller.setState( options.state ); 8108 } 8435 this.settings( new constructor({ 8436 controller: this.controller, 8437 model: this.model.props, 8438 priority: 40 8439 }) ); 8440 }, 8109 8441 8110 if ( options.reset ) { 8111 controller.reset(); 8112 } 8442 loading: function() { 8443 this.$el.toggleClass( 'embed-loading', this.model.get('loading') ); 8113 8444 } 8114 8445 }); 8115 8446 8116 module.exports = Select;8447 module.exports = Embed; 8117 8448 8118 },{}],70:[function(require,module,exports){ 8119 var View = wp.media.View, 8120 l10n = wp.media.view.l10n, 8121 $ = jQuery, 8122 EditorUploader; 8449 8450 /***/ }), 8451 /* 91 */ 8452 /***/ (function(module, exports) { 8123 8453 8124 8454 /** 8125 * Creates a dropzone on WP editor instances (elements with .wp-editor-wrap) 8126 * and relays drag'n'dropped files to a media workflow. 8127 * 8128 * wp.media.view.EditorUploader 8455 * wp.media.view.Label 8129 8456 * 8130 8457 * @memberOf wp.media.view 8131 8458 * … … var View = wp.media.View, 8134 8461 * @augments wp.Backbone.View 8135 8462 * @augments Backbone.View 8136 8463 */ 8137 EditorUploader = View.extend(/** @lends wp.media.view.EditorUploader.prototype */{ 8138 tagName: 'div', 8139 className: 'uploader-editor', 8140 template: wp.template( 'uploader-editor' ), 8141 8142 localDrag: false, 8143 overContainer: false, 8144 overDropzone: false, 8145 draggingFile: null, 8464 var Label = wp.media.View.extend(/** @lends wp.media.view.Label.prototype */{ 8465 tagName: 'label', 8466 className: 'screen-reader-text', 8146 8467 8147 /**8148 * Bind drag'n'drop events to callbacks.8149 */8150 8468 initialize: function() { 8151 this.initialized = false; 8152 8153 // Bail if not enabled or UA does not support drag'n'drop or File API. 8154 if ( ! window.tinyMCEPreInit || ! window.tinyMCEPreInit.dragDropUpload || ! this.browserSupport() ) { 8155 return this; 8156 } 8469 this.value = this.options.value; 8470 }, 8157 8471 8158 this.$document = $(document); 8159 this.dropzones = []; 8160 this.files = []; 8472 render: function() { 8473 this.$el.html( this.value ); 8161 8474 8162 this.$document.on( 'drop', '.uploader-editor', _.bind( this.drop, this ) ); 8163 this.$document.on( 'dragover', '.uploader-editor', _.bind( this.dropzoneDragover, this ) ); 8164 this.$document.on( 'dragleave', '.uploader-editor', _.bind( this.dropzoneDragleave, this ) ); 8165 this.$document.on( 'click', '.uploader-editor', _.bind( this.click, this ) ); 8475 return this; 8476 } 8477 }); 8166 8478 8167 this.$document.on( 'dragover', _.bind( this.containerDragover, this ) ); 8168 this.$document.on( 'dragleave', _.bind( this.containerDragleave, this ) ); 8479 module.exports = Label; 8169 8480 8170 this.$document.on( 'dragstart dragend drop', _.bind( function( event ) {8171 this.localDrag = event.type === 'dragstart';8172 8481 8173 if ( event.type === 'drop' ) { 8174 this.containerDragleave(); 8175 } 8176 }, this ) ); 8482 /***/ }), 8483 /* 92 */ 8484 /***/ (function(module, exports) { 8177 8485 8178 this.initialized = true; 8179 return this;8180 },8486 var View = wp.media.View, 8487 $ = jQuery, 8488 EmbedUrl; 8181 8489 8182 /** 8183 * Check browser support for drag'n'drop. 8184 * 8185 * @return Boolean 8186 */ 8187 browserSupport: function() { 8188 var supports = false, div = document.createElement('div'); 8490 /** 8491 * wp.media.view.EmbedUrl 8492 * 8493 * @memberOf wp.media.view 8494 * 8495 * @class 8496 * @augments wp.media.View 8497 * @augments wp.Backbone.View 8498 * @augments Backbone.View 8499 */ 8500 EmbedUrl = View.extend(/** @lends wp.media.view.EmbedUrl.prototype */{ 8501 tagName: 'label', 8502 className: 'embed-url', 8189 8503 8190 supports = ( 'draggable' in div ) || ( 'ondragstart' in div && 'ondrop' in div ); 8191 supports = supports && !! ( window.File && window.FileList && window.FileReader ); 8192 return supports; 8504 events: { 8505 'input': 'url', 8506 'keyup': 'url', 8507 'change': 'url' 8193 8508 }, 8194 8509 8195 isDraggingFile: function( event ) { 8196 if ( this.draggingFile !== null ) { 8197 return this.draggingFile; 8198 } 8199 8200 if ( _.isUndefined( event.originalEvent ) || _.isUndefined( event.originalEvent.dataTransfer ) ) { 8201 return false; 8202 } 8203 8204 this.draggingFile = _.indexOf( event.originalEvent.dataTransfer.types, 'Files' ) > -1 && 8205 _.indexOf( event.originalEvent.dataTransfer.types, 'text/plain' ) === -1; 8510 initialize: function() { 8511 this.$input = $('<input id="embed-url-field" type="url" />').val( this.model.get('url') ); 8512 this.input = this.$input[0]; 8206 8513 8207 return this.draggingFile;8208 },8514 this.spinner = $('<span class="spinner" />')[0]; 8515 this.$el.append([ this.input, this.spinner ]); 8209 8516 8210 refresh: function( e ) { 8211 var dropzone_id; 8212 for ( dropzone_id in this.dropzones ) { 8213 // Hide the dropzones only if dragging has left the screen. 8214 this.dropzones[ dropzone_id ].toggle( this.overContainer || this.overDropzone ); 8215 } 8517 this.listenTo( this.model, 'change:url', this.render ); 8216 8518 8217 if ( ! _.isUndefined( e ) ) { 8218 $( e.target ).closest( '.uploader-editor' ).toggleClass( 'droppable', this.overDropzone ); 8519 if ( this.model.get( 'url' ) ) { 8520 _.delay( _.bind( function () { 8521 this.model.trigger( 'change:url' ); 8522 }, this ), 500 ); 8219 8523 } 8524 }, 8525 /** 8526 * @returns {wp.media.view.EmbedUrl} Returns itself to allow chaining 8527 */ 8528 render: function() { 8529 var $input = this.$input; 8220 8530 8221 if ( ! this.overContainer && ! this.overDropzone) {8222 this.draggingFile = null;8531 if ( $input.is(':focus') ) { 8532 return; 8223 8533 } 8224 8534 8535 this.input.value = this.model.get('url') || 'http://'; 8536 /** 8537 * Call `render` directly on parent class with passed arguments 8538 */ 8539 View.prototype.render.apply( this, arguments ); 8225 8540 return this; 8226 8541 }, 8227 8542 8228 re nder: function() {8229 if ( ! this.initialized) {8230 return this;8543 ready: function() { 8544 if ( ! wp.media.isTouchDevice ) { 8545 this.focus(); 8231 8546 } 8232 8233 View.prototype.render.apply( this, arguments );8234 $( '.wp-editor-wrap' ).each( _.bind( this.attach, this ) );8235 return this;8236 8547 }, 8237 8548 8238 attach: function( index, editor ) { 8239 // Attach a dropzone to an editor. 8240 var dropzone = this.$el.clone(); 8241 this.dropzones.push( dropzone ); 8242 $( editor ).append( dropzone ); 8243 return this; 8549 url: function( event ) { 8550 this.model.set( 'url', $.trim( event.target.value ) ); 8244 8551 }, 8245 8552 8246 8553 /** 8247 * When a file is dropped on the editor uploader, open up an editor media workflow 8248 * and upload the file immediately. 8249 * 8250 * @param {jQuery.Event} event The 'drop' event. 8554 * If the input is visible, focus and select its contents. 8251 8555 */ 8252 drop: function( event ) { 8253 var $wrap, uploadView; 8556 focus: function() { 8557 var $input = this.$input; 8558 if ( $input.is(':visible') ) { 8559 $input.focus()[0].select(); 8560 } 8561 } 8562 }); 8254 8563 8255 this.containerDragleave( event ); 8256 this.dropzoneDragleave( event ); 8564 module.exports = EmbedUrl; 8257 8565 8258 this.files = event.originalEvent.dataTransfer.files; 8259 if ( this.files.length < 1 ) { 8566 8567 /***/ }), 8568 /* 93 */ 8569 /***/ (function(module, exports) { 8570 8571 var $ = jQuery, 8572 EmbedLink; 8573 8574 /** 8575 * wp.media.view.EmbedLink 8576 * 8577 * @memberOf wp.media.view 8578 * 8579 * @class 8580 * @augments wp.media.view.Settings 8581 * @augments wp.media.View 8582 * @augments wp.Backbone.View 8583 * @augments Backbone.View 8584 */ 8585 EmbedLink = wp.media.view.Settings.extend(/** @lends wp.media.view.EmbedLink.prototype */{ 8586 className: 'embed-link-settings', 8587 template: wp.template('embed-link-settings'), 8588 8589 initialize: function() { 8590 this.listenTo( this.model, 'change:url', this.updateoEmbed ); 8591 }, 8592 8593 updateoEmbed: _.debounce( function() { 8594 var url = this.model.get( 'url' ); 8595 8596 // clear out previous results 8597 this.$('.embed-container').hide().find('.embed-preview').empty(); 8598 this.$( '.setting' ).hide(); 8599 8600 // only proceed with embed if the field contains more than 11 characters 8601 // Example: http://a.io is 11 chars 8602 if ( url && ( url.length < 11 || ! url.match(/^http(s)?:\/\//) ) ) { 8260 8603 return; 8261 8604 } 8262 8605 8263 // Set the active editor to the drop target. 8264 $wrap = $( event.target ).parents( '.wp-editor-wrap' ); 8265 if ( $wrap.length > 0 && $wrap[0].id ) { 8266 window.wpActiveEditor = $wrap[0].id.slice( 3, -5 ); 8267 } 8606 this.fetch(); 8607 }, wp.media.controller.Embed.sensitivity ), 8268 8608 8269 if ( ! this.workflow ) { 8270 this.workflow = wp.media.editor.open( window.wpActiveEditor, { 8271 frame: 'post', 8272 state: 'insert', 8273 title: l10n.addMedia, 8274 multiple: true 8275 }); 8609 fetch: function() { 8610 var url = this.model.get( 'url' ), re, youTubeEmbedMatch; 8276 8611 8277 uploadView = this.workflow.uploader; 8612 // check if they haven't typed in 500 ms 8613 if ( $('#embed-url-field').val() !== url ) { 8614 return; 8615 } 8278 8616 8279 if ( uploadView.uploader && uploadView.uploader.ready) {8280 this.addFiles.apply( this);8281 } else {8282 this.workflow.on( 'uploader:ready', this.addFiles, this ); 8283 }8284 } else {8285 this.workflow.state().reset();8286 this.addFiles.apply( this );8287 this.workflow.open();8617 if ( this.dfd && 'pending' === this.dfd.state() ) { 8618 this.dfd.abort(); 8619 } 8620 8621 // Support YouTube embed urls, since they work once in the editor. 8622 re = /https?:\/\/www\.youtube\.com\/embed\/([^/]+)/; 8623 youTubeEmbedMatch = re.exec( url ); 8624 if ( youTubeEmbedMatch ) { 8625 url = 'https://www.youtube.com/watch?v=' + youTubeEmbedMatch[ 1 ]; 8288 8626 } 8289 8627 8290 return false; 8628 this.dfd = wp.apiRequest({ 8629 url: wp.media.view.settings.oEmbedProxyUrl, 8630 data: { 8631 url: url, 8632 maxwidth: this.model.get( 'width' ), 8633 maxheight: this.model.get( 'height' ) 8634 }, 8635 type: 'GET', 8636 dataType: 'json', 8637 context: this 8638 }) 8639 .done( function( response ) { 8640 this.renderoEmbed( { 8641 data: { 8642 body: response.html || '' 8643 } 8644 } ); 8645 } ) 8646 .fail( this.renderFail ); 8291 8647 }, 8292 8648 8293 /** 8294 * Add the files to the uploader. 8295 */ 8296 addFiles: function() { 8297 if ( this.files.length ) { 8298 this.workflow.uploader.uploader.uploader.addFile( _.toArray( this.files ) ); 8299 this.files = []; 8649 renderFail: function ( response, status ) { 8650 if ( 'abort' === status ) { 8651 return; 8300 8652 } 8301 return this;8653 this.$( '.link-text' ).show(); 8302 8654 }, 8303 8655 8304 containerDragover: function( event ) { 8305 if ( this.localDrag || ! this.isDraggingFile( event ) ) { 8306 return; 8656 renderoEmbed: function( response ) { 8657 var html = ( response && response.data && response.data.body ) || ''; 8658 8659 if ( html ) { 8660 this.$('.embed-container').show().find('.embed-preview').html( html ); 8661 } else { 8662 this.renderFail(); 8307 8663 } 8664 } 8665 }); 8308 8666 8309 this.overContainer = true; 8310 this.refresh(); 8311 }, 8667 module.exports = EmbedLink; 8312 8668 8313 containerDragleave: function() {8314 this.overContainer = false;8315 8669 8316 // Throttle dragleave because it's called when bouncing from some elements to others. 8317 _.delay( _.bind( this.refresh, this ), 50 ); 8318 }, 8670 /***/ }), 8671 /* 94 */ 8672 /***/ (function(module, exports) { 8319 8673 8320 dropzoneDragover: function( event ) { 8321 if ( this.localDrag || ! this.isDraggingFile( event ) ) { 8322 return; 8323 } 8674 var AttachmentDisplay = wp.media.view.Settings.AttachmentDisplay, 8675 EmbedImage; 8324 8676 8325 this.overDropzone = true; 8326 this.refresh( event ); 8327 return false; 8328 }, 8677 /** 8678 * wp.media.view.EmbedImage 8679 * 8680 * @memberOf wp.media.view 8681 * 8682 * @class 8683 * @augments wp.media.view.Settings.AttachmentDisplay 8684 * @augments wp.media.view.Settings 8685 * @augments wp.media.View 8686 * @augments wp.Backbone.View 8687 * @augments Backbone.View 8688 */ 8689 EmbedImage = AttachmentDisplay.extend(/** @lends wp.media.view.EmbedImage.prototype */{ 8690 className: 'embed-media-settings', 8691 template: wp.template('embed-image-settings'), 8329 8692 8330 dropzoneDragleave: function( e ) { 8331 this.overDropzone = false; 8332 _.delay( _.bind( this.refresh, this, e ), 50 ); 8693 initialize: function() { 8694 /** 8695 * Call `initialize` directly on parent class with passed arguments 8696 */ 8697 AttachmentDisplay.prototype.initialize.apply( this, arguments ); 8698 this.listenTo( this.model, 'change:url', this.updateImage ); 8333 8699 }, 8334 8700 8335 click: function( e ) { 8336 // In the rare case where the dropzone gets stuck, hide it on click. 8337 this.containerDragleave( e ); 8338 this.dropzoneDragleave( e ); 8339 this.localDrag = false; 8701 updateImage: function() { 8702 this.$('img').attr( 'src', this.model.get('url') ); 8340 8703 } 8341 8704 }); 8342 8705 8343 module.exports = E ditorUploader;8706 module.exports = EmbedImage; 8344 8707 8345 },{}],71:[function(require,module,exports){ 8346 var View = wp.media.View, 8347 UploaderInline; 8708 8709 /***/ }), 8710 /* 95 */ 8711 /***/ (function(module, exports) { 8712 8713 var AttachmentDisplay = wp.media.view.Settings.AttachmentDisplay, 8714 $ = jQuery, 8715 ImageDetails; 8348 8716 8349 8717 /** 8350 * wp.media.view.UploaderInline 8351 * 8352 * The inline uploader that shows up in the 'Upload Files' tab. 8718 * wp.media.view.ImageDetails 8353 8719 * 8354 8720 * @memberOf wp.media.view 8355 8721 * 8356 8722 * @class 8723 * @augments wp.media.view.Settings.AttachmentDisplay 8724 * @augments wp.media.view.Settings 8357 8725 * @augments wp.media.View 8358 8726 * @augments wp.Backbone.View 8359 8727 * @augments Backbone.View 8360 8728 */ 8361 UploaderInline = View.extend(/** @lends wp.media.view.UploaderInline.prototype */{ 8362 tagName: 'div', 8363 className: 'uploader-inline', 8364 template: wp.template('uploader-inline'), 8729 ImageDetails = AttachmentDisplay.extend(/** @lends wp.media.view.ImageDetails.prototype */{ 8730 className: 'image-details', 8731 template: wp.template('image-details'), 8732 events: _.defaults( AttachmentDisplay.prototype.events, { 8733 'click .edit-attachment': 'editAttachment', 8734 'click .replace-attachment': 'replaceAttachment', 8735 'click .advanced-toggle': 'onToggleAdvanced', 8736 'change [data-setting="customWidth"]': 'onCustomSize', 8737 'change [data-setting="customHeight"]': 'onCustomSize', 8738 'keyup [data-setting="customWidth"]': 'onCustomSize', 8739 'keyup [data-setting="customHeight"]': 'onCustomSize' 8740 } ), 8741 initialize: function() { 8742 // used in AttachmentDisplay.prototype.updateLinkTo 8743 this.options.attachment = this.model.attachment; 8744 this.listenTo( this.model, 'change:url', this.updateUrl ); 8745 this.listenTo( this.model, 'change:link', this.toggleLinkSettings ); 8746 this.listenTo( this.model, 'change:size', this.toggleCustomSize ); 8365 8747 8366 events: { 8367 'click .close': 'hide' 8748 AttachmentDisplay.prototype.initialize.apply( this, arguments ); 8368 8749 }, 8369 8750 8370 initialize: function() { 8371 _.defaults( this.options, { 8372 message: '', 8373 status: true, 8374 canClose: false 8375 }); 8751 prepare: function() { 8752 var attachment = false; 8376 8753 8377 if ( ! this.options.$browser && this.controller.uploader) {8378 this.options.$browser = this.controller.uploader.$browser;8754 if ( this.model.attachment ) { 8755 attachment = this.model.attachment.toJSON(); 8379 8756 } 8757 return _.defaults({ 8758 model: this.model.toJSON(), 8759 attachment: attachment 8760 }, this.options ); 8761 }, 8380 8762 8381 if ( _.isUndefined( this.options.postId ) ) { 8382 this.options.postId = wp.media.view.settings.post.id; 8763 render: function() { 8764 var args = arguments; 8765 8766 if ( this.model.attachment && 'pending' === this.model.dfd.state() ) { 8767 this.model.dfd 8768 .done( _.bind( function() { 8769 AttachmentDisplay.prototype.render.apply( this, args ); 8770 this.postRender(); 8771 }, this ) ) 8772 .fail( _.bind( function() { 8773 this.model.attachment = false; 8774 AttachmentDisplay.prototype.render.apply( this, args ); 8775 this.postRender(); 8776 }, this ) ); 8777 } else { 8778 AttachmentDisplay.prototype.render.apply( this, arguments ); 8779 this.postRender(); 8383 8780 } 8384 8781 8385 if ( this.options.status ) { 8386 this.views.set( '.upload-inline-status', new wp.media.view.UploaderStatus({ 8387 controller: this.controller 8388 }) ); 8782 return this; 8783 }, 8784 8785 postRender: function() { 8786 setTimeout( _.bind( this.resetFocus, this ), 10 ); 8787 this.toggleLinkSettings(); 8788 if ( window.getUserSetting( 'advImgDetails' ) === 'show' ) { 8789 this.toggleAdvanced( true ); 8389 8790 } 8791 this.trigger( 'post-render' ); 8390 8792 }, 8391 8793 8392 prepare: function() {8393 var suggestedWidth = this.controller.state().get('suggestedWidth'),8394 suggestedHeight = this.controller.state().get('suggestedHeight'),8395 data = {};8794 resetFocus: function() { 8795 this.$( '.link-to-custom' ).blur(); 8796 this.$( '.embed-media-settings' ).scrollTop( 0 ); 8797 }, 8396 8798 8397 data.message = this.options.message; 8398 data.canClose = this.options.canClose; 8799 updateUrl: function() { 8800 this.$( '.image img' ).attr( 'src', this.model.get( 'url' ) ); 8801 this.$( '.url' ).val( this.model.get( 'url' ) ); 8802 }, 8399 8803 8400 if ( suggestedWidth && suggestedHeight ) { 8401 data.suggestedWidth = suggestedWidth; 8402 data.suggestedHeight = suggestedHeight; 8804 toggleLinkSettings: function() { 8805 if ( this.model.get( 'link' ) === 'none' ) { 8806 this.$( '.link-settings' ).addClass('hidden'); 8807 } else { 8808 this.$( '.link-settings' ).removeClass('hidden'); 8403 8809 } 8404 8405 return data;8406 8810 }, 8407 /**8408 * @returns {wp.media.view.UploaderInline} Returns itself to allow chaining8409 */8410 dispose: function() {8411 if ( this.disposing ) {8412 /**8413 * call 'dispose' directly on the parent class8414 */8415 return View.prototype.dispose.apply( this, arguments );8416 }8417 8811 8418 // Run remove on `dispose`, so we can be sure to refresh the 8419 // uploader with a view-less DOM. Track whether we're disposing 8420 // so we don't trigger an infinite loop. 8421 this.disposing = true; 8422 return this.remove(); 8812 toggleCustomSize: function() { 8813 if ( this.model.get( 'size' ) !== 'custom' ) { 8814 this.$( '.custom-size' ).addClass('hidden'); 8815 } else { 8816 this.$( '.custom-size' ).removeClass('hidden'); 8817 } 8423 8818 }, 8424 /**8425 * @returns {wp.media.view.UploaderInline} Returns itself to allow chaining8426 */8427 remove: function() {8428 /**8429 * call 'remove' directly on the parent class8430 */8431 var result = View.prototype.remove.apply( this, arguments );8432 8819 8433 _.defer( _.bind( this.refresh, this ) ); 8434 return result; 8435 }, 8820 onCustomSize: function( event ) { 8821 var dimension = $( event.target ).data('setting'), 8822 num = $( event.target ).val(), 8823 value; 8436 8824 8437 refresh: function() { 8438 var uploader = this.controller.uploader; 8825 // Ignore bogus input 8826 if ( ! /^\d+/.test( num ) || parseInt( num, 10 ) < 1 ) { 8827 event.preventDefault(); 8828 return; 8829 } 8439 8830 8440 if ( uploader ) { 8441 uploader.refresh(); 8831 if ( dimension === 'customWidth' ) { 8832 value = Math.round( 1 / this.model.get( 'aspectRatio' ) * num ); 8833 this.model.set( 'customHeight', value, { silent: true } ); 8834 this.$( '[data-setting="customHeight"]' ).val( value ); 8835 } else { 8836 value = Math.round( this.model.get( 'aspectRatio' ) * num ); 8837 this.model.set( 'customWidth', value, { silent: true } ); 8838 this.$( '[data-setting="customWidth"]' ).val( value ); 8442 8839 } 8443 8840 }, 8444 /**8445 * @returns {wp.media.view.UploaderInline}8446 */8447 ready: function() {8448 var $browser = this.options.$browser,8449 $placeholder;8450 8841 8451 if ( this.controller.uploader ) { 8452 $placeholder = this.$('.browser'); 8842 onToggleAdvanced: function( event ) { 8843 event.preventDefault(); 8844 this.toggleAdvanced(); 8845 }, 8453 8846 8454 // Check if we've already replaced the placeholder. 8455 if ( $placeholder[0] === $browser[0] ) { 8456 return; 8457 } 8847 toggleAdvanced: function( show ) { 8848 var $advanced = this.$el.find( '.advanced-section' ), 8849 mode; 8458 8850 8459 $browser.detach().text( $placeholder.text() ); 8460 $browser[0].className = $placeholder[0].className; 8461 $placeholder.replaceWith( $browser.show() ); 8851 if ( $advanced.hasClass('advanced-visible') || show === false ) { 8852 $advanced.removeClass('advanced-visible'); 8853 $advanced.find('.advanced-settings').addClass('hidden'); 8854 mode = 'hide'; 8855 } else { 8856 $advanced.addClass('advanced-visible'); 8857 $advanced.find('.advanced-settings').removeClass('hidden'); 8858 mode = 'show'; 8462 8859 } 8463 8860 8464 this.refresh(); 8465 return this; 8861 window.setUserSetting( 'advImgDetails', mode ); 8466 8862 }, 8467 show: function() { 8468 this.$el.removeClass( 'hidden' ); 8469 if ( this.controller.$uploaderToggler && this.controller.$uploaderToggler.length ) { 8470 this.controller.$uploaderToggler.attr( 'aria-expanded', 'true' ); 8863 8864 editAttachment: function( event ) { 8865 var editState = this.controller.states.get( 'edit-image' ); 8866 8867 if ( window.imageEdit && editState ) { 8868 event.preventDefault(); 8869 editState.set( 'image', this.model.attachment ); 8870 this.controller.setState( 'edit-image' ); 8471 8871 } 8472 8872 }, 8473 hide: function() {8474 this.$el.addClass( 'hidden' );8475 if ( this.controller.$uploaderToggler && this.controller.$uploaderToggler.length ) {8476 this.controller.$uploaderToggler8477 .attr( 'aria-expanded', 'false' )8478 // Move focus back to the toggle button when closing the uploader.8479 .focus();8480 }8481 }8482 8873 8874 replaceAttachment: function( event ) { 8875 event.preventDefault(); 8876 this.controller.setState( 'replace-image' ); 8877 } 8483 8878 }); 8484 8879 8485 module.exports = UploaderInline;8880 module.exports = ImageDetails; 8486 8881 8487 },{}],72:[function(require,module,exports){8488 /**8489 * wp.media.view.UploaderStatusError8490 *8491 * @memberOf wp.media.view8492 *8493 * @class8494 * @augments wp.media.View8495 * @augments wp.Backbone.View8496 * @augments Backbone.View8497 */8498 var UploaderStatusError = wp.media.View.extend(/** @lends wp.media.view.UploaderStatusError.prototype */{8499 className: 'upload-error',8500 template: wp.template('uploader-status-error')8501 });8502 8882 8503 module.exports = UploaderStatusError; 8883 /***/ }), 8884 /* 96 */ 8885 /***/ (function(module, exports) { 8504 8886 8505 },{}],73:[function(require,module,exports){8506 8887 var View = wp.media.View, 8507 UploaderStatus; 8888 UploaderStatus = wp.media.view.UploaderStatus, 8889 l10n = wp.media.view.l10n, 8890 $ = jQuery, 8891 Cropper; 8508 8892 8509 8893 /** 8510 * wp.media.view. UploaderStatus8894 * wp.media.view.Cropper 8511 8895 * 8512 * An uploader status for on-going uploads. 8896 * Uses the imgAreaSelect plugin to allow a user to crop an image. 8897 * 8898 * Takes imgAreaSelect options from 8899 * wp.customize.HeaderControl.calculateImageSelectOptions via 8900 * wp.customize.HeaderControl.openMM. 8513 8901 * 8514 8902 * @memberOf wp.media.view 8515 8903 * … … var View = wp.media.View, 8518 8906 * @augments wp.Backbone.View 8519 8907 * @augments Backbone.View 8520 8908 */ 8521 UploaderStatus = View.extend(/** @lends wp.media.view.UploaderStatus.prototype */{ 8522 className: 'media-uploader-status', 8523 template: wp.template('uploader-status'), 8524 8525 events: { 8526 'click .upload-dismiss-errors': 'dismiss' 8527 }, 8528 8909 Cropper = View.extend(/** @lends wp.media.view.Cropper.prototype */{ 8910 className: 'crop-content', 8911 template: wp.template('crop-content'), 8529 8912 initialize: function() { 8530 this.queue = wp.Uploader.queue; 8531 this.queue.on( 'add remove reset', this.visibility, this ); 8532 this.queue.on( 'add remove reset change:percent', this.progress, this ); 8533 this.queue.on( 'add remove reset change:uploading', this.info, this ); 8534 8535 this.errors = wp.Uploader.errors; 8536 this.errors.reset(); 8537 this.errors.on( 'add remove reset', this.visibility, this ); 8538 this.errors.on( 'add', this.error, this ); 8913 _.bindAll(this, 'onImageLoad'); 8539 8914 }, 8540 /** 8541 * @returns {wp.media.view.UploaderStatus} 8542 */ 8543 dispose: function() { 8544 wp.Uploader.queue.off( null, null, this ); 8545 /** 8546 * call 'dispose' directly on the parent class 8547 */ 8548 View.prototype.dispose.apply( this, arguments ); 8549 return this; 8915 ready: function() { 8916 this.controller.frame.on('content:error:crop', this.onError, this); 8917 this.$image = this.$el.find('.crop-image'); 8918 this.$image.on('load', this.onImageLoad); 8919 $(window).on('resize.cropper', _.debounce(this.onImageLoad, 250)); 8550 8920 }, 8551 8552 visibility: function() {8553 this.$el. toggleClass( 'uploading', !! this.queue.length);8554 this.$el. toggleClass( 'errors', !! this.errors.length);8555 this.$el.toggle( !! this.queue.length || !! this.errors.length);8921 remove: function() { 8922 $(window).off('resize.cropper'); 8923 this.$el.remove(); 8924 this.$el.off(); 8925 View.prototype.remove.apply(this, arguments); 8556 8926 }, 8557 8558 ready: function() { 8559 _.each({ 8560 '$bar': '.media-progress-bar div', 8561 '$index': '.upload-index', 8562 '$total': '.upload-total', 8563 '$filename': '.upload-filename' 8564 }, function( selector, key ) { 8565 this[ key ] = this.$( selector ); 8566 }, this ); 8567 8568 this.visibility(); 8569 this.progress(); 8570 this.info(); 8927 prepare: function() { 8928 return { 8929 title: l10n.cropYourImage, 8930 url: this.options.attachment.get('url') 8931 }; 8571 8932 }, 8933 onImageLoad: function() { 8934 var imgOptions = this.controller.get('imgSelectOptions'), 8935 imgSelect; 8572 8936 8573 progress: function() { 8574 var queue = this.queue, 8575 $bar = this.$bar; 8576 8577 if ( ! $bar || ! queue.length ) { 8578 return; 8937 if (typeof imgOptions === 'function') { 8938 imgOptions = imgOptions(this.options.attachment, this.controller); 8579 8939 } 8580 8940 8581 $bar.width( ( queue.reduce( function( memo, attachment ){8582 if ( ! attachment.get('uploading') ) {8583 return memo + 100;8584 }8941 imgOptions = _.extend(imgOptions, { 8942 parent: this.$el, 8943 onInit: function() { 8944 this.parent.children().on( 'mousedown touchstart', function( e ){ 8585 8945 8586 var percent = attachment.get('percent'); 8587 return memo + ( _.isNumber( percent ) ? percent : 100 ); 8588 }, 0 ) / queue.length ) + '%' ); 8946 if ( e.shiftKey ) { 8947 imgSelect.setOptions( { 8948 aspectRatio: '1:1' 8949 } ); 8950 } else { 8951 imgSelect.setOptions( { 8952 aspectRatio: false 8953 } ); 8954 } 8955 } ); 8956 } 8957 } ); 8958 this.trigger('image-loaded'); 8959 imgSelect = this.controller.imgSelect = this.$image.imgAreaSelect(imgOptions); 8589 8960 }, 8961 onError: function() { 8962 var filename = this.options.attachment.get('filename'); 8590 8963 8591 info: function() { 8592 var queue = this.queue, 8593 index = 0, active; 8964 this.views.add( '.upload-errors', new wp.media.view.UploaderStatusError({ 8965 filename: UploaderStatus.prototype.filename(filename), 8966 message: window._wpMediaViewsL10n.cropError 8967 }), { at: 0 }); 8968 } 8969 }); 8594 8970 8595 if ( ! queue.length ) { 8596 return; 8597 } 8971 module.exports = Cropper; 8598 8972 8599 active = this.queue.find( function( attachment, i ) {8600 index = i;8601 return attachment.get('uploading');8602 });8603 8973 8604 this.$index.text( index + 1 ); 8605 this.$total.text( queue.length ); 8606 this.$filename.html( active ? this.filename( active.get('filename') ) : '' ); 8607 }, 8608 /** 8609 * @param {string} filename 8610 * @returns {string} 8611 */ 8612 filename: function( filename ) { 8613 return _.escape( filename ); 8614 }, 8615 /** 8616 * @param {Backbone.Model} error 8617 */ 8618 error: function( error ) { 8619 this.views.add( '.upload-errors', new wp.media.view.UploaderStatusError({ 8620 filename: this.filename( error.get('file').name ), 8621 message: error.get('message') 8622 }), { at: 0 }); 8974 /***/ }), 8975 /* 97 */ 8976 /***/ (function(module, exports) { 8977 8978 var View = wp.media.view, 8979 SiteIconCropper; 8980 8981 /** 8982 * wp.media.view.SiteIconCropper 8983 * 8984 * Uses the imgAreaSelect plugin to allow a user to crop a Site Icon. 8985 * 8986 * Takes imgAreaSelect options from 8987 * wp.customize.SiteIconControl.calculateImageSelectOptions. 8988 * 8989 * @memberOf wp.media.view 8990 * 8991 * @class 8992 * @augments wp.media.view.Cropper 8993 * @augments wp.media.View 8994 * @augments wp.Backbone.View 8995 * @augments Backbone.View 8996 */ 8997 SiteIconCropper = View.Cropper.extend(/** @lends wp.media.view.SiteIconCropper.prototype */{ 8998 className: 'crop-content site-icon', 8999 9000 ready: function () { 9001 View.Cropper.prototype.ready.apply( this, arguments ); 9002 9003 this.$( '.crop-image' ).on( 'load', _.bind( this.addSidebar, this ) ); 8623 9004 }, 8624 9005 8625 /** 8626 * @param {Object} event 8627 */ 8628 dismiss: function( event ) { 8629 var errors = this.views.get('.upload-errors'); 9006 addSidebar: function() { 9007 this.sidebar = new wp.media.view.Sidebar({ 9008 controller: this.controller 9009 }); 8630 9010 8631 event.preventDefault(); 9011 this.sidebar.set( 'preview', new wp.media.view.SiteIconPreview({ 9012 controller: this.controller, 9013 attachment: this.options.attachment 9014 }) ); 8632 9015 8633 if ( errors ) { 8634 _.invoke( errors, 'remove' ); 8635 } 8636 wp.Uploader.errors.reset(); 9016 this.controller.cropperView.views.add( this.sidebar ); 8637 9017 } 8638 9018 }); 8639 9019 8640 module.exports = UploaderStatus;9020 module.exports = SiteIconCropper; 8641 9021 8642 },{}],74:[function(require,module,exports){ 8643 var $ = jQuery, 8644 UploaderWindow; 9022 9023 /***/ }), 9024 /* 98 */ 9025 /***/ (function(module, exports) { 9026 9027 var View = wp.media.View, 9028 $ = jQuery, 9029 SiteIconPreview; 8645 9030 8646 9031 /** 8647 * wp.media.view. UploaderWindow9032 * wp.media.view.SiteIconPreview 8648 9033 * 8649 * An uploader window that allows for dragging and dropping media.9034 * Shows a preview of the Site Icon as a favicon and app icon while cropping. 8650 9035 * 8651 9036 * @memberOf wp.media.view 8652 9037 * … … var $ = jQuery, 8654 9039 * @augments wp.media.View 8655 9040 * @augments wp.Backbone.View 8656 9041 * @augments Backbone.View 8657 *8658 * @param {object} [options] Options hash passed to the view.8659 * @param {object} [options.uploader] Uploader properties.8660 * @param {jQuery} [options.uploader.browser]8661 * @param {jQuery} [options.uploader.dropzone] jQuery collection of the dropzone.8662 * @param {object} [options.uploader.params]8663 9042 */ 8664 UploaderWindow = wp.media.View.extend(/** @lends wp.media.view.UploaderWindow.prototype */{ 8665 tagName: 'div', 8666 className: 'uploader-window', 8667 template: wp.template('uploader-window'), 9043 SiteIconPreview = View.extend(/** @lends wp.media.view.SiteIconPreview.prototype */{ 9044 className: 'site-icon-preview', 9045 template: wp.template( 'site-icon-preview' ), 8668 9046 8669 initialize: function() { 8670 var uploader; 9047 ready: function() { 9048 this.controller.imgSelect.setOptions({ 9049 onInit: this.updatePreview, 9050 onSelectChange: this.updatePreview 9051 }); 9052 }, 8671 9053 8672 this.$browser = $( '<button type="button" class="browser" />' ).hide().appendTo( 'body' ); 9054 prepare: function() { 9055 return { 9056 url: this.options.attachment.get( 'url' ) 9057 }; 9058 }, 8673 9059 8674 uploader = this.options.uploader = _.defaults( this.options.uploader || {},{8675 dropzone: this.$el,8676 browser: this.$browser,8677 p arams: {}8678 });9060 updatePreview: function( img, coords ) { 9061 var rx = 64 / coords.width, 9062 ry = 64 / coords.height, 9063 preview_rx = 16 / coords.width, 9064 preview_ry = 16 / coords.height; 8679 9065 8680 // Ensure the dropzone is a jQuery collection. 8681 if ( uploader.dropzone && ! (uploader.dropzone instanceof $) ) { 8682 uploader.dropzone = $( uploader.dropzone ); 8683 } 9066 $( '#preview-app-icon' ).css({ 9067 width: Math.round(rx * this.imageWidth ) + 'px', 9068 height: Math.round(ry * this.imageHeight ) + 'px', 9069 marginLeft: '-' + Math.round(rx * coords.x1) + 'px', 9070 marginTop: '-' + Math.round(ry * coords.y1) + 'px' 9071 }); 8684 9072 8685 this.controller.on( 'activate', this.refresh, this ); 9073 $( '#preview-favicon' ).css({ 9074 width: Math.round( preview_rx * this.imageWidth ) + 'px', 9075 height: Math.round( preview_ry * this.imageHeight ) + 'px', 9076 marginLeft: '-' + Math.round( preview_rx * coords.x1 ) + 'px', 9077 marginTop: '-' + Math.floor( preview_ry* coords.y1 ) + 'px' 9078 }); 9079 } 9080 }); 8686 9081 8687 this.controller.on( 'detach', function() { 8688 this.$browser.remove(); 8689 }, this ); 8690 }, 9082 module.exports = SiteIconPreview; 8691 9083 8692 refresh: function() {8693 if ( this.uploader ) {8694 this.uploader.refresh();8695 }8696 },8697 9084 8698 ready: function() { 8699 var postId = wp.media.view.settings.post.id, 8700 dropzone; 9085 /***/ }), 9086 /* 99 */ 9087 /***/ (function(module, exports) { 8701 9088 8702 // If the uploader already exists, bail. 8703 if ( this.uploader ) { 8704 return; 8705 } 9089 var View = wp.media.View, 9090 EditImage; 8706 9091 8707 if ( postId ) { 8708 this.options.uploader.params.post_id = postId; 8709 } 8710 this.uploader = new wp.Uploader( this.options.uploader ); 9092 /** 9093 * wp.media.view.EditImage 9094 * 9095 * @memberOf wp.media.view 9096 * 9097 * @class 9098 * @augments wp.media.View 9099 * @augments wp.Backbone.View 9100 * @augments Backbone.View 9101 */ 9102 EditImage = View.extend(/** @lends wp.media.view.EditImage.prototype */{ 9103 className: 'image-editor', 9104 template: wp.template('image-editor'), 8711 9105 8712 dropzone = this.uploader.dropzone; 8713 dropzone.on( 'dropzone:enter', _.bind( this.show, this ) ); 8714 dropzone.on( 'dropzone:leave', _.bind( this.hide, this ) ); 9106 initialize: function( options ) { 9107 this.editor = window.imageEdit; 9108 this.controller = options.controller; 9109 View.prototype.initialize.apply( this, arguments ); 9110 }, 8715 9111 8716 $( this.uploader ).on( 'uploader:ready', _.bind( this._ready, this ) ); 9112 prepare: function() { 9113 return this.model.toJSON(); 8717 9114 }, 8718 9115 8719 _ready: function() { 8720 this.controller.trigger( 'uploader:ready' ); 9116 loadEditor: function() { 9117 var dfd = this.editor.open( this.model.get('id'), this.model.get('nonces').edit, this ); 9118 dfd.done( _.bind( this.focus, this ) ); 8721 9119 }, 8722 9120 8723 show: function() { 8724 var $el = this.$el.show(); 9121 focus: function() { 9122 this.$( '.imgedit-submit .button' ).eq( 0 ).focus(); 9123 }, 8725 9124 8726 // Ensure that the animation is triggered by waiting until 8727 // the transparent element is painted into the DOM. 8728 _.defer( function() { 8729 $el.css({ opacity: 1 }); 8730 }); 9125 back: function() { 9126 var lastState = this.controller.lastState(); 9127 this.controller.setState( lastState ); 8731 9128 }, 8732 9129 8733 hide: function() { 8734 var $el = this.$el.css({ opacity: 0 }); 9130 refresh: function() { 9131 this.model.fetch(); 9132 }, 8735 9133 8736 wp.media.transition( $el ).done( function() { 8737 // Transition end events are subject to race conditions. 8738 // Make sure that the value is set as intended. 8739 if ( '0' === $el.css('opacity') ) { 8740 $el.hide(); 8741 } 8742 }); 9134 save: function() { 9135 var lastState = this.controller.lastState(); 8743 9136 8744 // https://core.trac.wordpress.org/ticket/27341 8745 _.delay( function() { 8746 if ( '0' === $el.css('opacity') && $el.is(':visible') ) { 8747 $el.hide(); 8748 } 8749 }, 500 ); 9137 this.model.fetch().done( _.bind( function() { 9138 this.controller.setState( lastState ); 9139 }, this ) ); 8750 9140 } 9141 8751 9142 }); 8752 9143 8753 module.exports = UploaderWindow; 9144 module.exports = EditImage; 9145 9146 9147 /***/ }), 9148 /* 100 */ 9149 /***/ (function(module, exports) { 8754 9150 8755 },{}],75:[function(require,module,exports){8756 9151 /** 8757 * wp.media.View 8758 * 8759 * The base view class for media. 8760 * 8761 * Undelegating events, removing events from the model, and 8762 * removing events from the controller mirror the code for 8763 * `Backbone.View.dispose` in Backbone 0.9.8 development. 8764 * 8765 * This behavior has since been removed, and should not be used 8766 * outside of the media manager. 9152 * wp.media.view.Spinner 8767 9153 * 8768 * @memberOf wp.media 9154 * @memberOf wp.media.view 8769 9155 * 8770 9156 * @class 9157 * @augments wp.media.View 8771 9158 * @augments wp.Backbone.View 8772 9159 * @augments Backbone.View 8773 9160 */ 8774 var View = wp.Backbone.View.extend(/** @lends wp.media.View.prototype */{ 8775 constructor: function( options ) { 8776 if ( options && options.controller ) { 8777 this.controller = options.controller; 8778 } 8779 wp.Backbone.View.apply( this, arguments ); 8780 }, 8781 /** 8782 * @todo The internal comment mentions this might have been a stop-gap 8783 * before Backbone 0.9.8 came out. Figure out if Backbone core takes 8784 * care of this in Backbone.View now. 8785 * 8786 * @returns {wp.media.View} Returns itself to allow chaining 8787 */ 8788 dispose: function() { 8789 // Undelegating events, removing events from the model, and 8790 // removing events from the controller mirror the code for 8791 // `Backbone.View.dispose` in Backbone 0.9.8 development. 8792 this.undelegateEvents(); 9161 var Spinner = wp.media.View.extend(/** @lends wp.media.view.Spinner.prototype */{ 9162 tagName: 'span', 9163 className: 'spinner', 9164 spinnerTimeout: false, 9165 delay: 400, 8793 9166 8794 if ( this.model && this.model.off ) { 8795 this.model.off( null, null, this ); 9167 show: function() { 9168 if ( ! this.spinnerTimeout ) { 9169 this.spinnerTimeout = _.delay(function( $el ) { 9170 $el.addClass( 'is-active' ); 9171 }, this.delay, this.$el ); 8796 9172 } 8797 9173 8798 if ( this.collection && this.collection.off ) { 8799 this.collection.off( null, null, this ); 8800 } 9174 return this; 9175 }, 8801 9176 8802 // Unbind controller events. 8803 if ( this.controller && this.controller.off ) { 8804 this.controller.off( null, null, this ); 8805 } 9177 hide: function() { 9178 this.$el.removeClass( 'is-active' ); 9179 this.spinnerTimeout = clearTimeout( this.spinnerTimeout ); 8806 9180 8807 9181 return this; 8808 },8809 /**8810 * @returns {wp.media.View} Returns itself to allow chaining8811 */8812 remove: function() {8813 this.dispose();8814 /**8815 * call 'remove' directly on the parent class8816 */8817 return wp.Backbone.View.prototype.remove.apply( this, arguments );8818 9182 } 8819 9183 }); 8820 9184 8821 module.exports = View; 9185 module.exports = Spinner; 9186 8822 9187 8823 },{}]},{},[19]); 9188 /***/ }) 9189 /******/ ])); 9190 No newline at end of file -
new file webpack-dev.config.js
diff --git webpack-dev.config.js webpack-dev.config.js new file mode 100644 index 0000000000..27c8e74b23
- + 1 var path = require( 'path' ), 2 SOURCE_DIR = 'src/', 3 mediaConfig = {}, 4 mediaBuilds = [ 'audiovideo', 'grid', 'models', 'views' ], 5 webpack = require( 'webpack' ); 6 7 8 mediaBuilds.forEach( function ( build ) { 9 var path = SOURCE_DIR + 'wp-includes/js/media'; 10 mediaConfig[ build ] = './' + path + '/' + build + '.manifest.js'; 11 } ); 12 13 module.exports = { 14 cache: true, 15 watch: true, 16 entry: mediaConfig, 17 output: { 18 path: path.join( __dirname, 'src/wp-includes/js' ), 19 filename: 'media-[name].js' 20 } 21 }; -
new file webpack.config.js
diff --git webpack.config.js webpack.config.js new file mode 100644 index 0000000000..8a0b1e4c5c
- + 1 var path = require( 'path' ), 2 SOURCE_DIR = 'src/', 3 mediaConfig = {}, 4 mediaBuilds = [ 'audiovideo', 'grid', 'models', 'views' ], 5 webpack = require( 'webpack' ); 6 7 8 mediaBuilds.forEach( function ( build ) { 9 var path = SOURCE_DIR + 'wp-includes/js/media'; 10 mediaConfig[ build ] = './' + path + '/' + build + '.manifest.js'; 11 } ); 12 13 module.exports = { 14 cache: true, 15 entry: mediaConfig, 16 output: { 17 path: path.join( __dirname, 'src/wp-includes/js' ), 18 filename: 'media-[name].js' 19 }, 20 plugins: [ 21 new webpack.optimize.ModuleConcatenationPlugin() 22 ] 23 };