Changeset 41752
- Timestamp:
- 10/04/2017 09:00:15 PM (6 years ago)
- Location:
- trunk
- Files:
-
- 2 added
- 7 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/.jshintrc
r39898 r41752 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 } -
trunk/Gruntfile.js
r41751 r41752 1 1 /* jshint node:true */ 2 2 /* globals Set */ 3 var webpackConfig = require( './webpack.config.prod' ); 4 var webpackDevConfig = require( './webpack.config.dev' ); 5 3 6 module.exports = function(grunt) { 4 7 var path = require('path'), … … 8 11 BUILD_DIR = 'build/', 9 12 BANNER_TEXT = '/*! This file is auto-generated */', 10 autoprefixer = require('autoprefixer'), 11 mediaConfig = {}, 12 mediaBuilds = ['audiovideo', 'grid', 'models', 'views']; 13 autoprefixer = require( 'autoprefixer' ); 13 14 14 15 // Load tasks. … … 16 17 // Load legacy utils 17 18 grunt.util = require('grunt-legacy-util'); 18 19 mediaBuilds.forEach( function ( build ) {20 var path = SOURCE_DIR + 'wp-includes/js/media';21 mediaConfig[ build ] = { files : {} };22 mediaConfig[ build ].files[ path + '-' + build + '.js' ] = [ path + '/' + build + '.manifest.js' ];23 } );24 19 25 20 // Project configuration. … … 178 173 } 179 174 }, 180 browserify: mediaConfig,181 175 sass: { 182 176 colors: { … … 339 333 }, 340 334 media: { 341 options: {342 browserify: true343 },344 335 src: [ 345 336 SOURCE_DIR + 'wp-includes/js/media/**/*.js' … … 554 545 } 555 546 }, 556 547 webpack: { 548 prod: webpackConfig, 549 dev: webpackDevConfig 550 }, 557 551 concat: { 558 552 tinymce: { … … 720 714 }, 721 715 config: { 722 files: 'Gruntfile.js' 716 files: [ 717 'Gruntfile.js', 718 'webpack-dev.config.js', 719 'webpack.config.js' 720 ] 723 721 }, 724 722 colors: { … … 757 755 758 756 // Register tasks. 757 758 // Webpack task. 759 grunt.loadNpmTasks( 'grunt-webpack' ); 759 760 760 761 // RTL task. … … 781 782 782 783 grunt.registerTask( 'watch', function() { 783 if ( ! this.args.length || this.args.indexOf( 'browserify' ) > -1 ) { 784 grunt.config( 'browserify.options', { 785 browserifyOptions: { 786 debug: true 787 }, 788 watch: true 789 } ); 790 791 grunt.task.run( 'browserify' ); 784 if ( ! this.args.length || this.args.indexOf( 'webpack' ) > -1 ) { 785 786 grunt.task.run( 'webpack:dev' ); 792 787 } 793 788 … … 800 795 801 796 grunt.registerTask( 'precommit:js', [ 802 ' browserify',797 'webpack:prod', 803 798 'jshint:corejs', 804 799 'uglify:masonry', … … 979 974 var src; 980 975 981 if ( [ 'all', 'rtl', ' browserify' ].indexOf( target ) === -1 ) {976 if ( [ 'all', 'rtl', 'webpack' ].indexOf( target ) === -1 ) { 982 977 return; 983 978 } -
trunk/package.json
r41351 r41752 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", … … 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.6.0", 41 "webpack-dev-server": "^2.9.1" 40 42 } 41 43 } -
trunk/src/wp-includes/js/media-audiovideo.js
r41351 r41752 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 || {}, … … 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){ 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 ); 348 349 350 /***/ }), 351 /* 1 */ 352 /***/ (function(module, exports) { 353 354 /** 355 * wp.media.model.PostMedia 356 * 357 * Shared model class for audio and video. Updates the model after 358 * "Add Audio|Video Source" and "Replace Audio|Video" states return 359 * 360 * @memberOf wp.media.model 361 * 362 * @class 363 * @augments Backbone.Model 364 */ 365 var PostMedia = Backbone.Model.extend(/** @lends wp.media.model.PostMedia.prototype */{ 366 initialize: function() { 367 this.attachment = false; 368 }, 369 370 setSource: function( attachment ) { 371 this.attachment = attachment; 372 this.extension = attachment.get( 'filename' ).split('.').pop(); 373 374 if ( this.get( 'src' ) && this.extension === this.get( 'src' ).split('.').pop() ) { 375 this.unset( 'src' ); 376 } 377 378 if ( _.contains( wp.media.view.settings.embedExts, this.extension ) ) { 379 this.set( this.extension, this.attachment.get( 'url' ) ); 380 } else { 381 this.unset( this.extension ); 382 } 383 }, 384 385 changeAttachment: function( attachment ) { 386 this.setSource( attachment ); 387 388 this.unset( 'src' ); 389 _.each( _.without( wp.media.view.settings.embedExts, this.extension ), function( ext ) { 390 this.unset( ext ); 391 }, this ); 392 } 393 }); 394 395 module.exports = PostMedia; 396 397 398 /***/ }), 399 /* 2 */ 400 /***/ (function(module, exports) { 401 282 402 var State = wp.media.controller.State, 283 403 l10n = wp.media.view.l10n, … … 314 434 module.exports = AudioDetails; 315 435 316 },{}],3:[function(require,module,exports){ 436 437 /***/ }), 438 /* 3 */ 439 /***/ (function(module, exports) { 440 317 441 /** 318 442 * wp.media.controller.VideoDetails … … 349 473 module.exports = VideoDetails; 350 474 351 },{}],4:[function(require,module,exports){ 352 /** 353 * wp.media.model.PostMedia 354 * 355 * Shared model class for audio and video. Updates the model after 356 * "Add Audio|Video Source" and "Replace Audio|Video" states return 357 * 358 * @memberOf wp.media.model 359 * 360 * @class 361 * @augments Backbone.Model 362 */ 363 var PostMedia = Backbone.Model.extend(/** @lends wp.media.model.PostMedia.prototype */{ 364 initialize: function() { 365 this.attachment = false; 366 }, 367 368 setSource: function( attachment ) { 369 this.attachment = attachment; 370 this.extension = attachment.get( 'filename' ).split('.').pop(); 371 372 if ( this.get( 'src' ) && this.extension === this.get( 'src' ).split('.').pop() ) { 373 this.unset( 'src' ); 374 } 375 376 if ( _.contains( wp.media.view.settings.embedExts, this.extension ) ) { 377 this.set( this.extension, this.attachment.get( 'url' ) ); 378 } else { 379 this.unset( this.extension ); 380 } 381 }, 382 383 changeAttachment: function( attachment ) { 384 this.setSource( attachment ); 385 386 this.unset( 'src' ); 387 _.each( _.without( wp.media.view.settings.embedExts, this.extension ), function( ext ) { 388 this.unset( ext ); 389 }, this ); 390 } 391 }); 392 393 module.exports = PostMedia; 394 395 },{}],5:[function(require,module,exports){ 396 var MediaDetails = wp.media.view.MediaDetails, 397 AudioDetails; 398 399 /** 400 * wp.media.view.AudioDetails 401 * 402 * @memberOf wp.media.view 403 * 404 * @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 411 */ 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 } 428 429 return this; 430 } 431 }); 432 433 module.exports = AudioDetails; 434 435 },{}],6:[function(require,module,exports){ 436 var MediaDetails = wp.media.view.MediaFrame.MediaDetails, 437 MediaLibrary = wp.media.controller.MediaLibrary, 438 439 l10n = wp.media.view.l10n, 440 AudioDetails; 441 442 /** 443 * wp.media.view.MediaFrame.AudioDetails 444 * 445 * @memberOf wp.media.view.MediaFrame 446 * 447 * @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 456 */ 457 AudioDetails = MediaDetails.extend(/** @lends wp.media.view.MediaFrame.AudioDetails.prototype */{ 458 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 467 }, 468 469 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 ]); 508 } 509 }); 510 511 module.exports = AudioDetails; 512 513 },{}],7:[function(require,module,exports){ 475 476 /***/ }), 477 /* 4 */ 478 /***/ (function(module, exports) { 479 514 480 var Select = wp.media.view.MediaFrame.Select, 515 481 l10n = wp.media.view.l10n, … … 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, … … 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, … … 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; … … 995 1099 module.exports = VideoDetails; 996 1100 997 },{}]},{},[1]); 1101 1102 /***/ }) 1103 /******/ ]); -
trunk/src/wp-includes/js/media-grid.js
r41351 r41752 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; … … 29 124 module.exports = EditAttachmentMetadata; 30 125 31 },{}],2:[function(require,module,exports){ 32 var media = wp.media; 33 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){ 126 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 137 /** 138 * wp.media.view.MediaFrame.Manage 139 * 140 * A generic management frame workflow. 141 * 142 * Used in the media grid view. 143 * 144 * @memberOf wp.media.view.MediaFrame 145 * 146 * @class 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 153 */ 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 }); 169 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 ) ); 177 178 this.$window.on( 'scroll resize', _.debounce( _.bind( this.fixPosition, this ), 15 ) ); 179 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; 187 } 188 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 ); 200 201 this.options.uploader = false; 202 } 203 204 this.gridRouter = new wp.media.view.MediaFrame.Manage.Router(); 205 206 // Call 'initialize' directly on the parent class. 207 MediaFrame.prototype.initialize.apply( this, arguments ); 208 209 // Append the frame view directly the supplied container. 210 this.$el.appendTo( this.options.container ); 211 212 this.createStates(); 213 this.bindRegionModeHandlers(); 214 this.render(); 215 this.bindSearchHandler(); 216 217 wp.media.frames.browse = this; 218 }, 219 220 bindSearchHandler: function() { 221 var search = this.$( '#media-search-input' ), 222 searchView = this.browserView.toolbar.get( 'search' ).$el, 223 listMode = this.$( '.view-list' ), 224 225 input = _.throttle( function (e) { 226 var val = $( e.currentTarget ).val(), 227 url = ''; 228 229 if ( val ) { 230 url += '?search=' + val; 231 this.gridRouter.navigate( this.gridRouter.baseUrl( url ), { replace: true } ); 232 } 233 }, 1000 ); 234 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 }); 252 }, 253 254 /** 255 * Create the default states for the frame. 256 */ 257 createStates: function() { 258 var options = this.options; 259 260 if ( this.options.states ) { 261 return; 262 } 263 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 }, 278 279 /** 280 * Bind region mode activation events to proper handlers. 281 */ 282 bindRegionModeHandlers: function() { 283 this.on( 'content:create:browse', this.browseContent, this ); 284 285 // Handle a frame-level event for editing an attachment. 286 this.on( 'edit:attachment', this.openEditAttachmentModal, this ); 287 288 this.on( 'select:activate', this.bindKeydown, this ); 289 this.on( 'select:deactivate', this.unbindKeydown, this ); 290 }, 291 292 handleKeydown: function( e ) { 293 if ( 27 === e.which ) { 294 e.preventDefault(); 295 this.deactivateMode( 'select' ).activateMode( 'edit' ); 296 } 297 }, 298 299 bindKeydown: function() { 300 this.$body.on( 'keydown.select', _.bind( this.handleKeydown, this ) ); 301 }, 302 303 unbindKeydown: function() { 304 this.$body.off( 'keydown.select' ); 305 }, 306 307 fixPosition: function() { 308 var $browser, $toolbar; 309 if ( ! this.isModeActive( 'select' ) ) { 310 return; 311 } 312 313 $browser = this.$('.attachments-browser'); 314 $toolbar = $browser.find('.media-toolbar'); 315 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', ''); 323 } 324 }, 325 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 ); 345 } else { 346 wp.media.frames.edit = wp.media( { 347 frame: 'edit-attachments', 348 controller: this, 349 library: this.state().get('library'), 350 model: model 351 } ); 352 } 353 }, 354 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 ); 390 }, 391 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 } ); 413 } 414 } 415 }); 416 417 module.exports = Manage; 418 419 420 /***/ }), 421 /* 13 */ 422 /***/ (function(module, exports) { 423 424 var Details = wp.media.view.Attachment.Details, 425 TwoColumn; 426 427 /** 428 * wp.media.view.Attachment.Details.TwoColumn 429 * 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 434 * 435 * @class 436 * @augments wp.media.view.Attachment.Details 437 * @augments wp.media.view.Attachment 438 * @augments wp.media.View 439 * @augments wp.Backbone.View 440 * @augments Backbone.View 441 */ 442 TwoColumn = Details.extend(/** @lends wp.media.view.Attachment.Details.TowColumn.prototype */{ 443 template: wp.template( 'attachment-details-two-column' ), 444 445 initialize: function() { 446 this.controller.on( 'content:activate:edit-details', _.bind( this.editAttachment, this ) ); 447 448 Details.prototype.initialize.apply( this, arguments ); 449 }, 450 451 editAttachment: function( event ) { 452 if ( event ) { 453 event.preventDefault(); 454 } 455 this.controller.content.mode( 'edit-image' ); 456 }, 457 458 /** 459 * Noop this from parent class, doesn't apply here. 460 */ 461 toggleSelectionHandler: function() {}, 462 463 render: function() { 464 Details.prototype.render.apply( this, arguments ); 465 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 } ); 471 } 472 }); 473 474 module.exports = TwoColumn; 475 476 477 /***/ }), 478 /* 14 */ 479 /***/ (function(module, exports) { 480 45 481 /** 46 482 * wp.media.view.MediaFrame.Manage.Router … … 111 547 module.exports = Router; 112 548 113 },{}],4:[function(require,module,exports){ 114 var Details = wp.media.view.Attachment.Details, 115 TwoColumn; 549 550 /***/ }), 551 /* 15 */ 552 /***/ (function(module, exports) { 553 554 var View = wp.media.View, 555 EditImage = wp.media.view.EditImage, 556 Details; 116 557 117 558 /** 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 559 * wp.media.view.EditImage.Details 560 * 561 * @memberOf wp.media.view.EditImage 124 562 * 125 563 * @class 126 * @augments wp.media.view.Attachment.Details 127 * @augments wp.media.view.Attachment 564 * @augments wp.media.view.EditImage 128 565 * @augments wp.media.View 129 566 * @augments wp.Backbone.View 130 567 * @augments Backbone.View 131 568 */ 132 TwoColumn = Details.extend(/** @lends wp.media.view.Attachment.Details.TowColumn.prototype */{ 133 template: wp.template( 'attachment-details-two-column' ), 134 135 initialize: function() { 136 this.controller.on( 'content:activate:edit-details', _.bind( this.editAttachment, this ) ); 137 138 Details.prototype.initialize.apply( this, arguments ); 139 }, 140 141 editAttachment: function( event ) { 142 if ( event ) { 143 event.preventDefault(); 144 } 145 this.controller.content.mode( 'edit-image' ); 146 }, 147 148 /** 149 * Noop this from parent class, doesn't apply here. 150 */ 151 toggleSelectionHandler: function() {}, 152 153 render: function() { 154 Details.prototype.render.apply( this, arguments ); 155 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 } ); 569 Details = EditImage.extend(/** @lends wp.media.view.EditImage.Details.prototype */{ 570 initialize: function( options ) { 571 this.editor = window.imageEdit; 572 this.frame = options.frame; 573 this.controller = options.controller; 574 View.prototype.initialize.apply( this, arguments ); 575 }, 576 577 back: function() { 578 this.frame.content.mode( 'edit-metadata' ); 579 }, 580 581 save: function() { 582 this.model.fetch().done( _.bind( function() { 583 this.frame.content.mode( 'edit-metadata' ); 584 }, this ) ); 161 585 } 162 586 }); 163 587 164 module.exports = TwoColumn; 165 166 },{}],5:[function(require,module,exports){ 167 var Button = wp.media.view.Button, 168 DeleteSelected = wp.media.view.DeleteSelectedButton, 169 DeleteSelectedPermanently; 588 module.exports = Details; 589 590 591 /***/ }), 592 /* 16 */ 593 /***/ (function(module, exports) { 594 595 var Frame = wp.media.view.Frame, 596 MediaFrame = wp.media.view.MediaFrame, 597 598 $ = jQuery, 599 EditAttachments; 170 600 171 601 /** 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 602 * wp.media.view.MediaFrame.EditAttachments 603 * 604 * A frame for editing the details of a specific media item. 605 * 606 * Opens in a modal by default. 607 * 608 * Requires an attachment model to be passed in the options hash under `model`. 609 * 610 * @memberOf wp.media.view.MediaFrame 177 611 * 178 612 * @class 179 * @augments wp.media.view.DeleteSelectedButton 180 * @augments wp.media.view.Button 613 * @augments wp.media.view.Frame 181 614 * @augments wp.media.View 182 615 * @augments wp.Backbone.View 183 616 * @augments Backbone.View 617 * @mixes wp.media.controller.StateMachine 184 618 */ 185 DeleteSelectedPermanently = DeleteSelected.extend(/** @lends wp.media.view.DeleteSelectedPermanentlyButton.prototype */{ 619 EditAttachments = MediaFrame.extend(/** @lends wp.media.view.MediaFrame.EditAttachments.prototype */{ 620 621 className: 'edit-attachment-frame', 622 template: wp.template( 'edit-attachment-frame' ), 623 regions: [ 'title', 'content' ], 624 625 events: { 626 'click .left': 'previousMediaItem', 627 'click .right': 'nextMediaItem' 628 }, 629 186 630 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 }, 191 192 filterChange: function( model ) { 193 this.canShow = ( 'trash' === model.get( 'status' ) ); 194 }, 195 196 selectActivate: function() { 197 this.toggleDisabled(); 198 this.$el.toggleClass( 'hidden', ! this.canShow ); 199 }, 200 201 selectDeactivate: function() { 202 this.toggleDisabled(); 203 this.$el.addClass( 'hidden' ); 204 }, 205 206 render: function() { 207 Button.prototype.render.apply( this, arguments ); 208 this.selectActivate(); 209 return this; 631 Frame.prototype.initialize.apply( this, arguments ); 632 633 _.defaults( this.options, { 634 modal: true, 635 state: 'edit-attachment' 636 }); 637 638 this.controller = this.options.controller; 639 this.gridRouter = this.controller.gridRouter; 640 this.library = this.options.library; 641 642 if ( this.options.model ) { 643 this.model = this.options.model; 644 } 645 646 this.bindHandlers(); 647 this.createStates(); 648 this.createModal(); 649 650 this.title.mode( 'default' ); 651 this.toggleNav(); 652 }, 653 654 bindHandlers: function() { 655 // Bind default title creation. 656 this.on( 'title:create:default', this.createTitle, this ); 657 658 this.on( 'content:create:edit-metadata', this.editMetadataMode, this ); 659 this.on( 'content:create:edit-image', this.editImageMode, this ); 660 this.on( 'content:render:edit-image', this.editImageModeRender, this ); 661 this.on( 'refresh', this.rerender, this ); 662 this.on( 'close', this.detach ); 663 664 this.bindModelHandlers(); 665 this.listenTo( this.gridRouter, 'route:search', this.close, this ); 666 }, 667 668 bindModelHandlers: function() { 669 // Close the modal if the attachment is deleted. 670 this.listenTo( this.model, 'change:status destroy', this.close, this ); 671 }, 672 673 createModal: function() { 674 // Initialize modal container view. 675 if ( this.options.modal ) { 676 this.modal = new wp.media.view.Modal({ 677 controller: this, 678 title: this.options.title 679 }); 680 681 this.modal.on( 'open', _.bind( function () { 682 $( 'body' ).on( 'keydown.media-modal', _.bind( this.keyEvent, this ) ); 683 }, this ) ); 684 685 // Completely destroy the modal DOM element when closing it. 686 this.modal.on( 'close', _.bind( function() { 687 $( 'body' ).off( 'keydown.media-modal' ); /* remove the keydown event */ 688 // Restore the original focus item if possible 689 $( 'li.attachment[data-id="' + this.model.get( 'id' ) +'"]' ).focus(); 690 this.resetRoute(); 691 }, this ) ); 692 693 // Set this frame as the modal's content. 694 this.modal.content( this ); 695 this.modal.open(); 696 } 697 }, 698 699 /** 700 * Add the default states to the frame. 701 */ 702 createStates: function() { 703 this.states.add([ 704 new wp.media.controller.EditAttachmentMetadata({ 705 model: this.model, 706 library: this.library 707 }) 708 ]); 709 }, 710 711 /** 712 * Content region rendering callback for the `edit-metadata` mode. 713 * 714 * @param {Object} contentRegion Basic object with a `view` property, which 715 * should be set with the proper region view. 716 */ 717 editMetadataMode: function( contentRegion ) { 718 contentRegion.view = new wp.media.view.Attachment.Details.TwoColumn({ 719 controller: this, 720 model: this.model 721 }); 722 723 /** 724 * Attach a subview to display fields added via the 725 * `attachment_fields_to_edit` filter. 726 */ 727 contentRegion.view.views.set( '.attachment-compat', new wp.media.view.AttachmentCompat({ 728 controller: this, 729 model: this.model 730 }) ); 731 732 // Update browser url when navigating media details, except on load. 733 if ( this.model && ! this.model.get( 'skipHistory' ) ) { 734 this.gridRouter.navigate( this.gridRouter.baseUrl( '?item=' + this.model.id ) ); 735 } 736 }, 737 738 /** 739 * Render the EditImage view into the frame's content region. 740 * 741 * @param {Object} contentRegion Basic object with a `view` property, which 742 * should be set with the proper region view. 743 */ 744 editImageMode: function( contentRegion ) { 745 var editImageController = new wp.media.controller.EditImage( { 746 model: this.model, 747 frame: this 748 } ); 749 // Noop some methods. 750 editImageController._toolbar = function() {}; 751 editImageController._router = function() {}; 752 editImageController._menu = function() {}; 753 754 contentRegion.view = new wp.media.view.EditImage.Details( { 755 model: this.model, 756 frame: this, 757 controller: editImageController 758 } ); 759 760 this.gridRouter.navigate( this.gridRouter.baseUrl( '?item=' + this.model.id + '&mode=edit' ) ); 761 762 }, 763 764 editImageModeRender: function( view ) { 765 view.on( 'ready', view.loadEditor ); 766 }, 767 768 toggleNav: function() { 769 this.$('.left').toggleClass( 'disabled', ! this.hasPrevious() ); 770 this.$('.right').toggleClass( 'disabled', ! this.hasNext() ); 771 }, 772 773 /** 774 * Rerender the view. 775 */ 776 rerender: function( model ) { 777 this.stopListening( this.model ); 778 779 this.model = model; 780 781 this.bindModelHandlers(); 782 783 // Only rerender the `content` region. 784 if ( this.content.mode() !== 'edit-metadata' ) { 785 this.content.mode( 'edit-metadata' ); 786 } else { 787 this.content.render(); 788 } 789 790 this.toggleNav(); 791 }, 792 793 /** 794 * Click handler to switch to the previous media item. 795 */ 796 previousMediaItem: function() { 797 if ( ! this.hasPrevious() ) { 798 this.$( '.left' ).blur(); 799 return; 800 } 801 this.trigger( 'refresh', this.library.at( this.getCurrentIndex() - 1 ) ); 802 this.$( '.left' ).focus(); 803 }, 804 805 /** 806 * Click handler to switch to the next media item. 807 */ 808 nextMediaItem: function() { 809 if ( ! this.hasNext() ) { 810 this.$( '.right' ).blur(); 811 return; 812 } 813 this.trigger( 'refresh', this.library.at( this.getCurrentIndex() + 1 ) ); 814 this.$( '.right' ).focus(); 815 }, 816 817 getCurrentIndex: function() { 818 return this.library.indexOf( this.model ); 819 }, 820 821 hasNext: function() { 822 return ( this.getCurrentIndex() + 1 ) < this.library.length; 823 }, 824 825 hasPrevious: function() { 826 return ( this.getCurrentIndex() - 1 ) > -1; 827 }, 828 /** 829 * Respond to the keyboard events: right arrow, left arrow, except when 830 * focus is in a textarea or input field. 831 */ 832 keyEvent: function( event ) { 833 if ( ( 'INPUT' === event.target.nodeName || 'TEXTAREA' === event.target.nodeName ) && ! ( event.target.readOnly || event.target.disabled ) ) { 834 return; 835 } 836 837 // The right arrow key 838 if ( 39 === event.keyCode ) { 839 this.nextMediaItem(); 840 } 841 // The left arrow key 842 if ( 37 === event.keyCode ) { 843 this.previousMediaItem(); 844 } 845 }, 846 847 resetRoute: function() { 848 var searchTerm = this.controller.browserView.toolbar.get( 'search' ).$el.val(), 849 url = '' !== searchTerm ? '?search=' + searchTerm : ''; 850 this.gridRouter.navigate( this.gridRouter.baseUrl( url ), { replace: true } ); 210 851 } 211 852 }); 212 853 213 module.exports = DeleteSelectedPermanently; 214 215 },{}],6:[function(require,module,exports){ 216 var Button = wp.media.view.Button, 217 l10n = wp.media.view.l10n, 218 DeleteSelected; 219 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 ); 238 } 239 this.controller.on( 'selection:toggle', this.toggleDisabled, this ); 240 }, 241 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 ); 247 } else { 248 this.model.set( 'text', l10n.deleteSelected ); 249 } 250 }, 251 252 toggleDisabled: function() { 253 this.model.set( 'disabled', ! this.controller.state().get( 'selection' ).length ); 254 }, 255 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' ); 262 } 263 this.toggleDisabled(); 264 return this; 265 } 266 }); 267 268 module.exports = DeleteSelected; 269 270 },{}],7:[function(require,module,exports){ 854 module.exports = EditAttachments; 855 856 857 /***/ }), 858 /* 17 */ 859 /***/ (function(module, exports) { 860 271 861 272 862 var Button = wp.media.view.Button, … … 345 935 module.exports = SelectModeToggle; 346 936 347 },{}],8:[function(require,module,exports){ 348 var View = wp.media.View, 349 EditImage = wp.media.view.EditImage, 350 Details; 937 938 /***/ }), 939 /* 18 */ 940 /***/ (function(module, exports) { 941 942 var Button = wp.media.view.Button, 943 l10n = wp.media.view.l10n, 944 DeleteSelected; 351 945 352 946 /** 353 * wp.media.view.EditImage.Details 354 * 355 * @memberOf wp.media.view.EditImage 947 * wp.media.view.DeleteSelectedButton 948 * 949 * A button that handles bulk Delete/Trash logic 950 * 951 * @memberOf wp.media.view 356 952 * 357 953 * @class 358 * @augments wp.media.view. EditImage954 * @augments wp.media.view.Button 359 955 * @augments wp.media.View 360 956 * @augments wp.Backbone.View 361 957 * @augments Backbone.View 362 958 */ 363 Details = EditImage.extend(/** @lends wp.media.view.EditImage.Details.prototype */{ 364 initialize: function( options ) { 365 this.editor = window.imageEdit; 366 this.frame = options.frame; 367 this.controller = options.controller; 368 View.prototype.initialize.apply( this, arguments ); 369 }, 370 371 back: function() { 372 this.frame.content.mode( 'edit-metadata' ); 373 }, 374 375 save: function() { 376 this.model.fetch().done( _.bind( function() { 377 this.frame.content.mode( 'edit-metadata' ); 378 }, this ) ); 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 ); 964 } 965 this.controller.on( 'selection:toggle', this.toggleDisabled, this ); 966 }, 967 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 ); 973 } else { 974 this.model.set( 'text', l10n.deleteSelected ); 975 } 976 }, 977 978 toggleDisabled: function() { 979 this.model.set( 'disabled', ! this.controller.state().get( 'selection' ).length ); 980 }, 981 982 render: function() { 983 Button.prototype.render.apply( this, arguments ); 984 if ( this.controller.isModeActive( 'select' ) ) { 985 this.$el.addClass( 'delete-selected-button' ); 986 } else { 987 this.$el.addClass( 'delete-selected-button hidden' ); 988 } 989 this.toggleDisabled(); 990 return this; 379 991 } 380 992 }); 381 993 382 module.exports = Details; 383 384 },{}],9:[function(require,module,exports){ 385 var Frame = wp.media.view.Frame, 386 MediaFrame = wp.media.view.MediaFrame, 387 388 $ = jQuery, 389 EditAttachments; 994 module.exports = DeleteSelected; 995 996 997 /***/ }), 998 /* 19 */ 999 /***/ (function(module, exports) { 1000 1001 var Button = wp.media.view.Button, 1002 DeleteSelected = wp.media.view.DeleteSelectedButton, 1003 DeleteSelectedPermanently; 390 1004 391 1005 /** 392 * wp.media.view.MediaFrame.EditAttachments 393 * 394 * A frame for editing the details of a specific media item. 395 * 396 * Opens in a modal by default. 397 * 398 * Requires an attachment model to be passed in the options hash under `model`. 399 * 400 * @memberOf wp.media.view.MediaFrame 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 401 1011 * 402 1012 * @class 403 * @augments wp.media.view.Frame 1013 * @augments wp.media.view.DeleteSelectedButton 1014 * @augments wp.media.view.Button 404 1015 * @augments wp.media.View 405 1016 * @augments wp.Backbone.View 406 1017 * @augments Backbone.View 407 * @mixes wp.media.controller.StateMachine408 1018 */ 409 EditAttachments = MediaFrame.extend(/** @lends wp.media.view.MediaFrame.EditAttachments.prototype */{ 410 411 className: 'edit-attachment-frame', 412 template: wp.template( 'edit-attachment-frame' ), 413 regions: [ 'title', 'content' ], 414 415 events: { 416 'click .left': 'previousMediaItem', 417 'click .right': 'nextMediaItem' 418 }, 419 1019 DeleteSelectedPermanently = DeleteSelected.extend(/** @lends wp.media.view.DeleteSelectedPermanentlyButton.prototype */{ 420 1020 initialize: function() { 421 Frame.prototype.initialize.apply( this, arguments ); 422 423 _.defaults( this.options, { 424 modal: true, 425 state: 'edit-attachment' 426 }); 427 428 this.controller = this.options.controller; 429 this.gridRouter = this.controller.gridRouter; 430 this.library = this.options.library; 431 432 if ( this.options.model ) { 433 this.model = this.options.model; 434 } 435 436 this.bindHandlers(); 437 this.createStates(); 438 this.createModal(); 439 440 this.title.mode( 'default' ); 441 this.toggleNav(); 442 }, 443 444 bindHandlers: function() { 445 // Bind default title creation. 446 this.on( 'title:create:default', this.createTitle, this ); 447 448 this.on( 'content:create:edit-metadata', this.editMetadataMode, this ); 449 this.on( 'content:create:edit-image', this.editImageMode, this ); 450 this.on( 'content:render:edit-image', this.editImageModeRender, this ); 451 this.on( 'refresh', this.rerender, this ); 452 this.on( 'close', this.detach ); 453 454 this.bindModelHandlers(); 455 this.listenTo( this.gridRouter, 'route:search', this.close, this ); 456 }, 457 458 bindModelHandlers: function() { 459 // Close the modal if the attachment is deleted. 460 this.listenTo( this.model, 'change:status destroy', this.close, this ); 461 }, 462 463 createModal: function() { 464 // Initialize modal container view. 465 if ( this.options.modal ) { 466 this.modal = new wp.media.view.Modal({ 467 controller: this, 468 title: this.options.title 469 }); 470 471 this.modal.on( 'open', _.bind( function () { 472 $( 'body' ).on( 'keydown.media-modal', _.bind( this.keyEvent, this ) ); 473 }, this ) ); 474 475 // Completely destroy the modal DOM element when closing it. 476 this.modal.on( 'close', _.bind( function() { 477 $( 'body' ).off( 'keydown.media-modal' ); /* remove the keydown event */ 478 // Restore the original focus item if possible 479 $( 'li.attachment[data-id="' + this.model.get( 'id' ) +'"]' ).focus(); 480 this.resetRoute(); 481 }, this ) ); 482 483 // Set this frame as the modal's content. 484 this.modal.content( this ); 485 this.modal.open(); 486 } 487 }, 488 489 /** 490 * Add the default states to the frame. 491 */ 492 createStates: function() { 493 this.states.add([ 494 new wp.media.controller.EditAttachmentMetadata({ 495 model: this.model, 496 library: this.library 497 }) 498 ]); 499 }, 500 501 /** 502 * Content region rendering callback for the `edit-metadata` mode. 503 * 504 * @param {Object} contentRegion Basic object with a `view` property, which 505 * should be set with the proper region view. 506 */ 507 editMetadataMode: function( contentRegion ) { 508 contentRegion.view = new wp.media.view.Attachment.Details.TwoColumn({ 509 controller: this, 510 model: this.model 511 }); 512 513 /** 514 * Attach a subview to display fields added via the 515 * `attachment_fields_to_edit` filter. 516 */ 517 contentRegion.view.views.set( '.attachment-compat', new wp.media.view.AttachmentCompat({ 518 controller: this, 519 model: this.model 520 }) ); 521 522 // Update browser url when navigating media details, except on load. 523 if ( this.model && ! this.model.get( 'skipHistory' ) ) { 524 this.gridRouter.navigate( this.gridRouter.baseUrl( '?item=' + this.model.id ) ); 525 } 526 }, 527 528 /** 529 * Render the EditImage view into the frame's content region. 530 * 531 * @param {Object} contentRegion Basic object with a `view` property, which 532 * should be set with the proper region view. 533 */ 534 editImageMode: function( contentRegion ) { 535 var editImageController = new wp.media.controller.EditImage( { 536 model: this.model, 537 frame: this 538 } ); 539 // Noop some methods. 540 editImageController._toolbar = function() {}; 541 editImageController._router = function() {}; 542 editImageController._menu = function() {}; 543 544 contentRegion.view = new wp.media.view.EditImage.Details( { 545 model: this.model, 546 frame: this, 547 controller: editImageController 548 } ); 549 550 this.gridRouter.navigate( this.gridRouter.baseUrl( '?item=' + this.model.id + '&mode=edit' ) ); 551 552 }, 553 554 editImageModeRender: function( view ) { 555 view.on( 'ready', view.loadEditor ); 556 }, 557 558 toggleNav: function() { 559 this.$('.left').toggleClass( 'disabled', ! this.hasPrevious() ); 560 this.$('.right').toggleClass( 'disabled', ! this.hasNext() ); 561 }, 562 563 /** 564 * Rerender the view. 565 */ 566 rerender: function( model ) { 567 this.stopListening( this.model ); 568 569 this.model = model; 570 571 this.bindModelHandlers(); 572 573 // Only rerender the `content` region. 574 if ( this.content.mode() !== 'edit-metadata' ) { 575 this.content.mode( 'edit-metadata' ); 576 } else { 577 this.content.render(); 578 } 579 580 this.toggleNav(); 581 }, 582 583 /** 584 * Click handler to switch to the previous media item. 585 */ 586 previousMediaItem: function() { 587 if ( ! this.hasPrevious() ) { 588 this.$( '.left' ).blur(); 589 return; 590 } 591 this.trigger( 'refresh', this.library.at( this.getCurrentIndex() - 1 ) ); 592 this.$( '.left' ).focus(); 593 }, 594 595 /** 596 * Click handler to switch to the next media item. 597 */ 598 nextMediaItem: function() { 599 if ( ! this.hasNext() ) { 600 this.$( '.right' ).blur(); 601 return; 602 } 603 this.trigger( 'refresh', this.library.at( this.getCurrentIndex() + 1 ) ); 604 this.$( '.right' ).focus(); 605 }, 606 607 getCurrentIndex: function() { 608 return this.library.indexOf( this.model ); 609 }, 610 611 hasNext: function() { 612 return ( this.getCurrentIndex() + 1 ) < this.library.length; 613 }, 614 615 hasPrevious: function() { 616 return ( this.getCurrentIndex() - 1 ) > -1; 617 }, 618 /** 619 * Respond to the keyboard events: right arrow, left arrow, except when 620 * focus is in a textarea or input field. 621 */ 622 keyEvent: function( event ) { 623 if ( ( 'INPUT' === event.target.nodeName || 'TEXTAREA' === event.target.nodeName ) && ! ( event.target.readOnly || event.target.disabled ) ) { 624 return; 625 } 626 627 // The right arrow key 628 if ( 39 === event.keyCode ) { 629 this.nextMediaItem(); 630 } 631 // The left arrow key 632 if ( 37 === event.keyCode ) { 633 this.previousMediaItem(); 634 } 635 }, 636 637 resetRoute: function() { 638 var searchTerm = this.controller.browserView.toolbar.get( 'search' ).$el.val(), 639 url = '' !== searchTerm ? '?search=' + searchTerm : ''; 640 this.gridRouter.navigate( this.gridRouter.baseUrl( url ), { replace: true } ); 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 }, 1025 1026 filterChange: function( model ) { 1027 this.canShow = ( 'trash' === model.get( 'status' ) ); 1028 }, 1029 1030 selectActivate: function() { 1031 this.toggleDisabled(); 1032 this.$el.toggleClass( 'hidden', ! this.canShow ); 1033 }, 1034 1035 selectDeactivate: function() { 1036 this.toggleDisabled(); 1037 this.$el.addClass( 'hidden' ); 1038 }, 1039 1040 render: function() { 1041 Button.prototype.render.apply( this, arguments ); 1042 this.selectActivate(); 1043 return this; 641 1044 } 642 1045 }); 643 1046 644 module.exports = EditAttachments; 645 646 },{}],10:[function(require,module,exports){ 647 var MediaFrame = wp.media.view.MediaFrame, 648 Library = wp.media.controller.Library, 649 650 $ = Backbone.$, 651 Manage; 652 653 /** 654 * wp.media.view.MediaFrame.Manage 655 * 656 * A generic management frame workflow. 657 * 658 * Used in the media grid view. 659 * 660 * @memberOf wp.media.view.MediaFrame 661 * 662 * @class 663 * @augments wp.media.view.MediaFrame 664 * @augments wp.media.view.Frame 665 * @augments wp.media.View 666 * @augments wp.Backbone.View 667 * @augments Backbone.View 668 * @mixes wp.media.controller.StateMachine 669 */ 670 Manage = MediaFrame.extend(/** @lends wp.media.view.MediaFrame.Manage.prototype */{ 671 /** 672 * @constructs 673 */ 674 initialize: function() { 675 _.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'); 698 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 } 704 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 ); 716 717 this.options.uploader = false; 718 } 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 }, 735 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 }); 768 }, 769 770 /** 771 * Create the default states for the frame. 772 */ 773 createStates: function() { 774 var options = this.options; 775 776 if ( this.options.states ) { 777 return; 778 } 779 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 }, 794 795 /** 796 * Bind region mode activation events to proper handlers. 797 */ 798 bindRegionModeHandlers: function() { 799 this.on( 'content:create:browse', this.browseContent, this ); 800 801 // Handle a frame-level event for editing an attachment. 802 this.on( 'edit:attachment', this.openEditAttachmentModal, this ); 803 804 this.on( 'select:activate', this.bindKeydown, this ); 805 this.on( 'select:deactivate', this.unbindKeydown, this ); 806 }, 807 808 handleKeydown: function( e ) { 809 if ( 27 === e.which ) { 810 e.preventDefault(); 811 this.deactivateMode( 'select' ).activateMode( 'edit' ); 812 } 813 }, 814 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'); 836 } else { 837 $browser.removeClass( 'fixed' ); 838 $toolbar.css('width', ''); 839 } 840 }, 841 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 } 852 }, 853 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 ); 861 } 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 } ); 868 } 869 }, 870 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(); 880 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 895 suggestedWidth: state.get('suggestedWidth'), 896 suggestedHeight: state.get('suggestedHeight'), 897 898 AttachmentView: state.get('AttachmentView'), 899 900 scrollElement: document 901 }); 902 this.browserView.on( 'ready', _.bind( this.bindDeferred, this ) ); 903 904 this.errors = wp.Uploader.errors; 905 this.errors.on( 'add remove reset', this.sidebarVisibility, this ); 906 }, 907 908 sidebarVisibility: function() { 909 this.browserView.$( '.media-sidebar' ).toggle( !! this.errors.length ); 910 }, 911 912 bindDeferred: function() { 913 if ( ! this.browserView.dfd ) { 914 return; 915 } 916 this.browserView.dfd.done( _.bind( this.startHistory, this ) ); 917 }, 918 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 } 930 } 931 }); 932 933 module.exports = Manage; 934 935 },{}]},{},[2]); 1047 module.exports = DeleteSelectedPermanently; 1048 1049 1050 /***/ }) 1051 /******/ ]); -
trunk/src/wp-includes/js/media-models.js
r41386 r41752 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; … … 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');70 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');137 Attachment = media.model.Attachment = __webpack_require__( 21 ); 138 Attachments = media.model.Attachments = __webpack_require__( 22 ); 139 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 /** … … 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; … … 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 … … 955 1034 module.exports = Attachments; 956 1035 957 },{}],4:[function(require,module,exports){ 1036 1037 /***/ }), 1038 1039 /***/ 23: 1040 /***/ (function(module, exports) { 1041 1042 var Attachments = wp.media.model.Attachments, 1043 Query; 1044 1045 /** 1046 * wp.media.model.Query 1047 * 1048 * A collection of attachments that match the supplied query arguments. 1049 * 1050 * Note: Do NOT change this.args after the query has been initialized. 1051 * Things will break. 1052 * 1053 * @memberOf wp.media.model 1054 * 1055 * @class 1056 * @augments wp.media.model.Attachments 1057 * @augments Backbone.Collection 1058 * 1059 * @param {array} [models] Models to initialize with the collection. 1060 * @param {object} [options] Options hash. 1061 * @param {object} [options.args] Attachments query arguments. 1062 * @param {object} [options.args.posts_per_page] 1063 */ 1064 Query = Attachments.extend(/** @lends wp.media.model.Query.prototype */{ 1065 /** 1066 * @param {array} [models=[]] Array of initial models to populate the collection. 1067 * @param {object} [options={}] 1068 */ 1069 initialize: function( models, options ) { 1070 var allowed; 1071 1072 options = options || {}; 1073 Attachments.prototype.initialize.apply( this, arguments ); 1074 1075 this.args = options.args; 1076 this._hasMore = true; 1077 this.created = new Date(); 1078 1079 this.filters.order = function( attachment ) { 1080 var orderby = this.props.get('orderby'), 1081 order = this.props.get('order'); 1082 1083 if ( ! this.comparator ) { 1084 return true; 1085 } 1086 1087 // We want any items that can be placed before the last 1088 // item in the set. If we add any items after the last 1089 // item, then we can't guarantee the set is complete. 1090 if ( this.length ) { 1091 return 1 !== this.comparator( attachment, this.last(), { ties: true }); 1092 1093 // Handle the case where there are no items yet and 1094 // we're sorting for recent items. In that case, we want 1095 // changes that occurred after we created the query. 1096 } else if ( 'DESC' === order && ( 'date' === orderby || 'modified' === orderby ) ) { 1097 return attachment.get( orderby ) >= this.created; 1098 1099 // If we're sorting by menu order and we have no items, 1100 // accept any items that have the default menu order (0). 1101 } else if ( 'ASC' === order && 'menuOrder' === orderby ) { 1102 return attachment.get( orderby ) === 0; 1103 } 1104 1105 // Otherwise, we don't want any items yet. 1106 return false; 1107 }; 1108 1109 // Observe the central `wp.Uploader.queue` collection to watch for 1110 // new matches for the query. 1111 // 1112 // Only observe when a limited number of query args are set. There 1113 // are no filters for other properties, so observing will result in 1114 // false positives in those queries. 1115 allowed = [ 's', 'order', 'orderby', 'posts_per_page', 'post_mime_type', 'post_parent' ]; 1116 if ( wp.Uploader && _( this.args ).chain().keys().difference( allowed ).isEmpty().value() ) { 1117 this.observe( wp.Uploader.queue ); 1118 } 1119 }, 1120 /** 1121 * Whether there are more attachments that haven't been sync'd from the server 1122 * that match the collection's query. 1123 * 1124 * @returns {boolean} 1125 */ 1126 hasMore: function() { 1127 return this._hasMore; 1128 }, 1129 /** 1130 * Fetch more attachments from the server for the collection. 1131 * 1132 * @param {object} [options={}] 1133 * @returns {Promise} 1134 */ 1135 more: function( options ) { 1136 var query = this; 1137 1138 // If there is already a request pending, return early with the Deferred object. 1139 if ( this._more && 'pending' === this._more.state() ) { 1140 return this._more; 1141 } 1142 1143 if ( ! this.hasMore() ) { 1144 return jQuery.Deferred().resolveWith( this ).promise(); 1145 } 1146 1147 options = options || {}; 1148 options.remove = false; 1149 1150 return this._more = this.fetch( options ).done( function( resp ) { 1151 if ( _.isEmpty( resp ) || -1 === this.args.posts_per_page || resp.length < this.args.posts_per_page ) { 1152 query._hasMore = false; 1153 } 1154 }); 1155 }, 1156 /** 1157 * Overrides Backbone.Collection.sync 1158 * Overrides wp.media.model.Attachments.sync 1159 * 1160 * @param {String} method 1161 * @param {Backbone.Model} model 1162 * @param {Object} [options={}] 1163 * @returns {Promise} 1164 */ 1165 sync: function( method, model, options ) { 1166 var args, fallback; 1167 1168 // Overload the read method so Attachment.fetch() functions correctly. 1169 if ( 'read' === method ) { 1170 options = options || {}; 1171 options.context = this; 1172 options.data = _.extend( options.data || {}, { 1173 action: 'query-attachments', 1174 post_id: wp.media.model.settings.post.id 1175 }); 1176 1177 // Clone the args so manipulation is non-destructive. 1178 args = _.clone( this.args ); 1179 1180 // Determine which page to query. 1181 if ( -1 !== args.posts_per_page ) { 1182 args.paged = Math.round( this.length / args.posts_per_page ) + 1; 1183 } 1184 1185 options.data.query = args; 1186 return wp.media.ajax( options ); 1187 1188 // Otherwise, fall back to Backbone.sync() 1189 } else { 1190 /** 1191 * Call wp.media.model.Attachments.sync or Backbone.sync 1192 */ 1193 fallback = Attachments.prototype.sync ? Attachments.prototype : Backbone; 1194 return fallback.sync.apply( this, arguments ); 1195 } 1196 } 1197 }, /** @lends wp.media.model.Query */{ 1198 /** 1199 * @readonly 1200 */ 1201 defaultProps: { 1202 orderby: 'date', 1203 order: 'DESC' 1204 }, 1205 /** 1206 * @readonly 1207 */ 1208 defaultArgs: { 1209 posts_per_page: 40 1210 }, 1211 /** 1212 * @readonly 1213 */ 1214 orderby: { 1215 allowed: [ 'name', 'author', 'date', 'title', 'modified', 'uploadedTo', 'id', 'post__in', 'menuOrder' ], 1216 /** 1217 * A map of JavaScript orderby values to their WP_Query equivalents. 1218 * @type {Object} 1219 */ 1220 valuemap: { 1221 'id': 'ID', 1222 'uploadedTo': 'parent', 1223 'menuOrder': 'menu_order ID' 1224 } 1225 }, 1226 /** 1227 * A map of JavaScript query properties to their WP_Query equivalents. 1228 * 1229 * @readonly 1230 */ 1231 propmap: { 1232 'search': 's', 1233 'type': 'post_mime_type', 1234 'perPage': 'posts_per_page', 1235 'menuOrder': 'menu_order', 1236 'uploadedTo': 'post_parent', 1237 'status': 'post_status', 1238 'include': 'post__in', 1239 'exclude': 'post__not_in' 1240 }, 1241 /** 1242 * Creates and returns an Attachments Query collection given the properties. 1243 * 1244 * Caches query objects and reuses where possible. 1245 * 1246 * @static 1247 * @method 1248 * 1249 * @param {object} [props] 1250 * @param {Object} [props.cache=true] Whether to use the query cache or not. 1251 * @param {Object} [props.order] 1252 * @param {Object} [props.orderby] 1253 * @param {Object} [props.include] 1254 * @param {Object} [props.exclude] 1255 * @param {Object} [props.s] 1256 * @param {Object} [props.post_mime_type] 1257 * @param {Object} [props.posts_per_page] 1258 * @param {Object} [props.menu_order] 1259 * @param {Object} [props.post_parent] 1260 * @param {Object} [props.post_status] 1261 * @param {Object} [options] 1262 * 1263 * @returns {wp.media.model.Query} A new Attachments Query collection. 1264 */ 1265 get: (function(){ 1266 /** 1267 * @static 1268 * @type Array 1269 */ 1270 var queries = []; 1271 1272 /** 1273 * @returns {Query} 1274 */ 1275 return function( props, options ) { 1276 var args = {}, 1277 orderby = Query.orderby, 1278 defaults = Query.defaultProps, 1279 query, 1280 cache = !! props.cache || _.isUndefined( props.cache ); 1281 1282 // Remove the `query` property. This isn't linked to a query, 1283 // this *is* the query. 1284 delete props.query; 1285 delete props.cache; 1286 1287 // Fill default args. 1288 _.defaults( props, defaults ); 1289 1290 // Normalize the order. 1291 props.order = props.order.toUpperCase(); 1292 if ( 'DESC' !== props.order && 'ASC' !== props.order ) { 1293 props.order = defaults.order.toUpperCase(); 1294 } 1295 1296 // Ensure we have a valid orderby value. 1297 if ( ! _.contains( orderby.allowed, props.orderby ) ) { 1298 props.orderby = defaults.orderby; 1299 } 1300 1301 _.each( [ 'include', 'exclude' ], function( prop ) { 1302 if ( props[ prop ] && ! _.isArray( props[ prop ] ) ) { 1303 props[ prop ] = [ props[ prop ] ]; 1304 } 1305 } ); 1306 1307 // Generate the query `args` object. 1308 // Correct any differing property names. 1309 _.each( props, function( value, prop ) { 1310 if ( _.isNull( value ) ) { 1311 return; 1312 } 1313 1314 args[ Query.propmap[ prop ] || prop ] = value; 1315 }); 1316 1317 // Fill any other default query args. 1318 _.defaults( args, Query.defaultArgs ); 1319 1320 // `props.orderby` does not always map directly to `args.orderby`. 1321 // Substitute exceptions specified in orderby.keymap. 1322 args.orderby = orderby.valuemap[ props.orderby ] || props.orderby; 1323 1324 // Search the query cache for a matching query. 1325 if ( cache ) { 1326 query = _.find( queries, function( query ) { 1327 return _.isEqual( query.args, args ); 1328 }); 1329 } else { 1330 queries = []; 1331 } 1332 1333 // Otherwise, create a new query and add it to the cache. 1334 if ( ! query ) { 1335 query = new Query( [], _.extend( options || {}, { 1336 props: props, 1337 args: args 1338 } ) ); 1339 queries.push( query ); 1340 } 1341 1342 return query; 1343 }; 1344 }()) 1345 }); 1346 1347 module.exports = Query; 1348 1349 1350 /***/ }), 1351 1352 /***/ 24: 1353 /***/ (function(module, exports) { 1354 958 1355 /** 959 1356 * wp.media.model.PostImage … … 1111 1508 module.exports = PostImage; 1112 1509 1113 },{}],5:[function(require,module,exports){ 1114 var Attachments = wp.media.model.Attachments, 1115 Query; 1116 1117 /** 1118 * wp.media.model.Query 1119 * 1120 * A collection of attachments that match the supplied query arguments. 1121 * 1122 * Note: Do NOT change this.args after the query has been initialized. 1123 * Things will break. 1124 * 1125 * @memberOf wp.media.model 1126 * 1127 * @class 1128 * @augments wp.media.model.Attachments 1129 * @augments Backbone.Collection 1130 * 1131 * @param {array} [models] Models to initialize with the collection. 1132 * @param {object} [options] Options hash. 1133 * @param {object} [options.args] Attachments query arguments. 1134 * @param {object} [options.args.posts_per_page] 1135 */ 1136 Query = Attachments.extend(/** @lends wp.media.model.Query.prototype */{ 1137 /** 1138 * @param {array} [models=[]] Array of initial models to populate the collection. 1139 * @param {object} [options={}] 1140 */ 1141 initialize: function( models, options ) { 1142 var allowed; 1143 1144 options = options || {}; 1145 Attachments.prototype.initialize.apply( this, arguments ); 1146 1147 this.args = options.args; 1148 this._hasMore = true; 1149 this.created = new Date(); 1150 1151 this.filters.order = function( attachment ) { 1152 var orderby = this.props.get('orderby'), 1153 order = this.props.get('order'); 1154 1155 if ( ! this.comparator ) { 1156 return true; 1157 } 1158 1159 // We want any items that can be placed before the last 1160 // item in the set. If we add any items after the last 1161 // item, then we can't guarantee the set is complete. 1162 if ( this.length ) { 1163 return 1 !== this.comparator( attachment, this.last(), { ties: true }); 1164 1165 // Handle the case where there are no items yet and 1166 // we're sorting for recent items. In that case, we want 1167 // changes that occurred after we created the query. 1168 } else if ( 'DESC' === order && ( 'date' === orderby || 'modified' === orderby ) ) { 1169 return attachment.get( orderby ) >= this.created; 1170 1171 // If we're sorting by menu order and we have no items, 1172 // accept any items that have the default menu order (0). 1173 } else if ( 'ASC' === order && 'menuOrder' === orderby ) { 1174 return attachment.get( orderby ) === 0; 1175 } 1176 1177 // Otherwise, we don't want any items yet. 1178 return false; 1179 }; 1180 1181 // Observe the central `wp.Uploader.queue` collection to watch for 1182 // new matches for the query. 1183 // 1184 // Only observe when a limited number of query args are set. There 1185 // are no filters for other properties, so observing will result in 1186 // false positives in those queries. 1187 allowed = [ 's', 'order', 'orderby', 'posts_per_page', 'post_mime_type', 'post_parent' ]; 1188 if ( wp.Uploader && _( this.args ).chain().keys().difference( allowed ).isEmpty().value() ) { 1189 this.observe( wp.Uploader.queue ); 1190 } 1191 }, 1192 /** 1193 * Whether there are more attachments that haven't been sync'd from the server 1194 * that match the collection's query. 1195 * 1196 * @returns {boolean} 1197 */ 1198 hasMore: function() { 1199 return this._hasMore; 1200 }, 1201 /** 1202 * Fetch more attachments from the server for the collection. 1203 * 1204 * @param {object} [options={}] 1205 * @returns {Promise} 1206 */ 1207 more: function( options ) { 1208 var query = this; 1209 1210 // If there is already a request pending, return early with the Deferred object. 1211 if ( this._more && 'pending' === this._more.state() ) { 1212 return this._more; 1213 } 1214 1215 if ( ! this.hasMore() ) { 1216 return jQuery.Deferred().resolveWith( this ).promise(); 1217 } 1218 1219 options = options || {}; 1220 options.remove = false; 1221 1222 return this._more = this.fetch( options ).done( function( resp ) { 1223 if ( _.isEmpty( resp ) || -1 === this.args.posts_per_page || resp.length < this.args.posts_per_page ) { 1224 query._hasMore = false; 1225 } 1226 }); 1227 }, 1228 /** 1229 * Overrides Backbone.Collection.sync 1230 * Overrides wp.media.model.Attachments.sync 1231 * 1232 * @param {String} method 1233 * @param {Backbone.Model} model 1234 * @param {Object} [options={}] 1235 * @returns {Promise} 1236 */ 1237 sync: function( method, model, options ) { 1238 var args, fallback; 1239 1240 // Overload the read method so Attachment.fetch() functions correctly. 1241 if ( 'read' === method ) { 1242 options = options || {}; 1243 options.context = this; 1244 options.data = _.extend( options.data || {}, { 1245 action: 'query-attachments', 1246 post_id: wp.media.model.settings.post.id 1247 }); 1248 1249 // Clone the args so manipulation is non-destructive. 1250 args = _.clone( this.args ); 1251 1252 // Determine which page to query. 1253 if ( -1 !== args.posts_per_page ) { 1254 args.paged = Math.round( this.length / args.posts_per_page ) + 1; 1255 } 1256 1257 options.data.query = args; 1258 return wp.media.ajax( options ); 1259 1260 // Otherwise, fall back to Backbone.sync() 1261 } else { 1262 /** 1263 * Call wp.media.model.Attachments.sync or Backbone.sync 1264 */ 1265 fallback = Attachments.prototype.sync ? Attachments.prototype : Backbone; 1266 return fallback.sync.apply( this, arguments ); 1267 } 1268 } 1269 }, /** @lends wp.media.model.Query */{ 1270 /** 1271 * @readonly 1272 */ 1273 defaultProps: { 1274 orderby: 'date', 1275 order: 'DESC' 1276 }, 1277 /** 1278 * @readonly 1279 */ 1280 defaultArgs: { 1281 posts_per_page: 40 1282 }, 1283 /** 1284 * @readonly 1285 */ 1286 orderby: { 1287 allowed: [ 'name', 'author', 'date', 'title', 'modified', 'uploadedTo', 'id', 'post__in', 'menuOrder' ], 1288 /** 1289 * A map of JavaScript orderby values to their WP_Query equivalents. 1290 * @type {Object} 1291 */ 1292 valuemap: { 1293 'id': 'ID', 1294 'uploadedTo': 'parent', 1295 'menuOrder': 'menu_order ID' 1296 } 1297 }, 1298 /** 1299 * A map of JavaScript query properties to their WP_Query equivalents. 1300 * 1301 * @readonly 1302 */ 1303 propmap: { 1304 'search': 's', 1305 'type': 'post_mime_type', 1306 'perPage': 'posts_per_page', 1307 'menuOrder': 'menu_order', 1308 'uploadedTo': 'post_parent', 1309 'status': 'post_status', 1310 'include': 'post__in', 1311 'exclude': 'post__not_in' 1312 }, 1313 /** 1314 * Creates and returns an Attachments Query collection given the properties. 1315 * 1316 * Caches query objects and reuses where possible. 1317 * 1318 * @static 1319 * @method 1320 * 1321 * @param {object} [props] 1322 * @param {Object} [props.cache=true] Whether to use the query cache or not. 1323 * @param {Object} [props.order] 1324 * @param {Object} [props.orderby] 1325 * @param {Object} [props.include] 1326 * @param {Object} [props.exclude] 1327 * @param {Object} [props.s] 1328 * @param {Object} [props.post_mime_type] 1329 * @param {Object} [props.posts_per_page] 1330 * @param {Object} [props.menu_order] 1331 * @param {Object} [props.post_parent] 1332 * @param {Object} [props.post_status] 1333 * @param {Object} [options] 1334 * 1335 * @returns {wp.media.model.Query} A new Attachments Query collection. 1336 */ 1337 get: (function(){ 1338 /** 1339 * @static 1340 * @type Array 1341 */ 1342 var queries = []; 1343 1344 /** 1345 * @returns {Query} 1346 */ 1347 return function( props, options ) { 1348 var args = {}, 1349 orderby = Query.orderby, 1350 defaults = Query.defaultProps, 1351 query, 1352 cache = !! props.cache || _.isUndefined( props.cache ); 1353 1354 // Remove the `query` property. This isn't linked to a query, 1355 // this *is* the query. 1356 delete props.query; 1357 delete props.cache; 1358 1359 // Fill default args. 1360 _.defaults( props, defaults ); 1361 1362 // Normalize the order. 1363 props.order = props.order.toUpperCase(); 1364 if ( 'DESC' !== props.order && 'ASC' !== props.order ) { 1365 props.order = defaults.order.toUpperCase(); 1366 } 1367 1368 // Ensure we have a valid orderby value. 1369 if ( ! _.contains( orderby.allowed, props.orderby ) ) { 1370 props.orderby = defaults.orderby; 1371 } 1372 1373 _.each( [ 'include', 'exclude' ], function( prop ) { 1374 if ( props[ prop ] && ! _.isArray( props[ prop ] ) ) { 1375 props[ prop ] = [ props[ prop ] ]; 1376 } 1377 } ); 1378 1379 // Generate the query `args` object. 1380 // Correct any differing property names. 1381 _.each( props, function( value, prop ) { 1382 if ( _.isNull( value ) ) { 1383 return; 1384 } 1385 1386 args[ Query.propmap[ prop ] || prop ] = value; 1387 }); 1388 1389 // Fill any other default query args. 1390 _.defaults( args, Query.defaultArgs ); 1391 1392 // `props.orderby` does not always map directly to `args.orderby`. 1393 // Substitute exceptions specified in orderby.keymap. 1394 args.orderby = orderby.valuemap[ props.orderby ] || props.orderby; 1395 1396 // Search the query cache for a matching query. 1397 if ( cache ) { 1398 query = _.find( queries, function( query ) { 1399 return _.isEqual( query.args, args ); 1400 }); 1401 } else { 1402 queries = []; 1403 } 1404 1405 // Otherwise, create a new query and add it to the cache. 1406 if ( ! query ) { 1407 query = new Query( [], _.extend( options || {}, { 1408 props: props, 1409 args: args 1410 } ) ); 1411 queries.push( query ); 1412 } 1413 1414 return query; 1415 }; 1416 }()) 1417 }); 1418 1419 module.exports = Query; 1420 1421 },{}],6:[function(require,module,exports){ 1510 1511 /***/ }), 1512 1513 /***/ 25: 1514 /***/ (function(module, exports) { 1515 1422 1516 var Attachments = wp.media.model.Attachments, 1423 1517 Selection; … … 1518 1612 module.exports = Selection; 1519 1613 1520 },{}]},{},[1]); 1614 1615 /***/ }) 1616 1617 /******/ }); -
trunk/src/wp-includes/js/media-views.js
r41557 r41752 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__) { 69 70 var media = wp.media, 71 $ = jQuery, 72 l10n; 73 74 media.isTouchDevice = ( 'ontouchend' in document ); 75 76 // Link any localized strings. 77 l10n = media.view.l10n = window._wpMediaViewsL10n || {}; 78 79 // Link any settings. 80 media.view.settings = l10n.settings || {}; 81 delete l10n.settings; 82 83 // Copy the `post` setting over to the model settings. 84 media.model.settings.post = media.view.settings.post; 85 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; 95 96 transition = _.find( _.keys( transitions ), function( transition ) { 97 return ! _.isUndefined( style[ transition ] ); 98 }); 99 100 return transition && { 101 end: transitions[ transition ] 102 }; 103 }()); 5 104 6 105 /** 7 * wp.media.controller.CollectionAdd 8 * 9 * A state for adding attachments to a collection (e.g. video playlist). 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 ); 127 } 128 129 // Resolve the deferred when the first element finishes animating. 130 selector.first().one( $.support.transition.end, deferred.resolve ); 131 132 // Just in case the event doesn't trigger, fire a callback. 133 _.delay( deferred.resolve, sensitivity ); 134 135 // Otherwise, execute on the spot. 136 } else { 137 deferred.resolve(); 138 } 139 140 return deferred.promise(); 141 }; 142 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) { 224 225 /** 226 * wp.media.controller.Region 227 * 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. 10 237 * 11 238 * @memberOf wp.media.controller 12 239 * 13 240 * @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'). 241 * 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. 40 246 */ 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', 47 48 priority: 100, 49 syncSelection: false 50 }, Library.prototype.defaults ), 51 52 /** 53 * @since 3.9.0 54 */ 55 initialize: function() { 56 var collectionType = this.get('collectionType'); 57 58 if ( 'video' === this.get( 'type' ) ) { 59 collectionType = 'video-' + collectionType; 60 } 61 62 this.set( 'id', collectionType + '-library' ); 63 this.set( 'toolbar', collectionType + '-add' ); 64 this.set( 'menu', collectionType ); 65 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 }, 72 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'); 80 81 if ( editLibrary && editLibrary !== edit ) { 82 library.unobserve( editLibrary ); 83 } 84 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 }; 90 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); 97 98 Library.prototype.activate.apply( this, arguments ); 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; 253 254 _.extend( Region.prototype,/** @lends wp.media.controller.Region.prototype */{ 255 /** 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. 266 */ 267 mode: function( mode ) { 268 if ( ! mode ) { 269 return this._mode; 270 } 271 // Bail if we're trying to change to the current mode. 272 if ( mode === this._mode ) { 273 return this; 274 } 275 276 /** 277 * Region mode deactivation event. 278 * 279 * @event wp.media.controller.Region#deactivate 280 */ 281 this.trigger('deactivate'); 282 283 this._mode = mode; 284 this.render( mode ); 285 286 /** 287 * Region mode activation event. 288 * 289 * @event wp.media.controller.Region#activate 290 */ 291 this.trigger('activate'); 292 return this; 293 }, 294 /** 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 305 */ 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 } 311 312 var set = { view: null }, 313 view; 314 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; 340 }, 341 342 /** 343 * Get the region's view. 344 * 345 * @since 3.5.0 346 * 347 * @returns {wp.media.View} 348 */ 349 get: function() { 350 return this.view.views.first( this.selector ); 351 }, 352 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; 365 } 366 return this.view.views.set( this.selector, views, options ); 367 }, 368 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; 379 380 if ( ! this._mode ) { 381 return; 382 } 383 384 args = _.toArray( arguments ); 385 base = this.id + ':' + event; 386 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; 99 395 } 100 396 }); 101 397 102 module.exports = CollectionAdd; 103 104 },{}],2:[function(require,module,exports){ 105 var Library = wp.media.controller.Library, 106 l10n = wp.media.view.l10n, 107 $ = jQuery, 108 CollectionEdit; 398 module.exports = Region; 399 400 401 /***/ }), 402 /* 28 */ 403 /***/ (function(module, exports) { 109 404 110 405 /** 111 * wp.media.controller.CollectionEdit 112 * 113 * A state for editing a collection, which is used by audio and video playlists, 114 * and can be used for other collections. 406 * wp.media.controller.StateMachine 407 * 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. 115 412 * 116 413 * @memberOf wp.media.controller 117 414 * 415 * @since 3.5.0 416 * 118 417 * @class 119 * @augments wp.media.controller.Library120 * @augments wp.media.controller.State121 418 * @augments Backbone.Model 122 * 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'). 419 * @mixin 420 * @mixes Backbone.Events 421 * 422 * @param {Array} states 146 423 */ 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 }, 162 163 /** 164 * @since 3.9.0 165 */ 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() ); 179 } 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 ); 183 } 184 Library.prototype.initialize.apply( this, arguments ); 185 }, 186 187 /** 188 * @since 3.9.0 189 */ 190 activate: function() { 191 var library = this.get('library'); 192 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 ); 198 199 this.frame.on( 'content:render:browse', this.renderSettings, this ); 200 201 Library.prototype.activate.apply( this, arguments ); 202 }, 203 204 /** 205 * @since 3.9.0 206 */ 207 deactivate: function() { 208 // Stop watching for uploaded attachments. 209 this.get('library').unobserve( wp.Uploader.queue ); 210 211 this.frame.off( 'content:render:browse', this.renderSettings, this ); 212 213 Library.prototype.deactivate.apply( this, arguments ); 214 }, 215 216 /** 217 * Render the collection embed settings view in the browser sidebar. 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 }; 428 429 // Use Backbone's self-propagating `extend` inheritance method. 430 StateMachine.extend = Backbone.Model.extend; 431 432 _.extend( StateMachine.prototype, Backbone.Events,/** @lends wp.media.controller.StateMachine.prototype */{ 433 /** 434 * Fetch a state. 218 435 * 219 * @todo This is against the pattern elsewhere in media. Typically the frame 220 * is responsible for adding region mode callbacks. Explain. 436 * If no `id` is provided, returns the active state. 221 437 * 222 * @since 3.9.0438 * Implicitly creates states. 223 439 * 224 * @param {wp.media.view.attachmentsBrowser} The attachments browser view. 225 */ 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 = {}; 232 233 if ( ! library || ! attachmentsBrowserView ) { 234 return; 235 } 236 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 ); 246 247 if ( dragInfoText ) { 248 attachmentsBrowserView.toolbar.set( 'dragInfo', new wp.media.View({ 249 el: $( '<div class="instructions">' + dragInfoText + '</div>' )[0], 250 priority: -40 251 }) ); 252 } 253 254 // Add the 'Reverse order' button to the toolbar. 255 attachmentsBrowserView.toolbar.set( 'reverse', { 256 text: l10n.reverseOrder, 257 priority: 80, 258 259 click: function() { 260 library.reset( library.toArray().reverse() ); 261 } 262 }); 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(); 451 452 // Default to the active state. 453 id = id || this._state; 454 455 if ( id && ! this.states.get( id ) ) { 456 this.states.add({ id: id }); 457 } 458 return this.states.get( id ); 459 }, 460 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(); 479 480 if ( ( previous && id === previous.id ) || ! this.states || ! this.states.get( id ) ) { 481 return this; 482 } 483 484 if ( previous ) { 485 previous.trigger('deactivate'); 486 this._lastState = previous.id; 487 } 488 489 this._state = id; 490 this.state().trigger('activate'); 491 492 return this; 493 }, 494 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 } 263 510 } 264 511 }); 265 512 266 module.exports = CollectionEdit; 267 268 },{}],3:[function(require,module,exports){ 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 }; 541 }); 542 543 module.exports = StateMachine; 544 545 546 /***/ }), 547 /* 29 */ 548 /***/ (function(module, exports) { 549 550 /** 551 * wp.media.controller.State 552 * 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. 563 * 564 * @memberOf wp.media.controller 565 * 566 * @class 567 * @augments Backbone.Model 568 */ 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 ); 589 }, 590 /** 591 * Ready event callback. 592 * 593 * @abstract 594 * @since 3.5.0 595 */ 596 ready: function() {}, 597 598 /** 599 * Activate event callback. 600 * 601 * @abstract 602 * @since 3.5.0 603 */ 604 activate: function() {}, 605 606 /** 607 * Deactivate event callback. 608 * 609 * @abstract 610 * @since 3.5.0 611 */ 612 deactivate: function() {}, 613 614 /** 615 * Reset event callback. 616 * 617 * @abstract 618 * @since 3.5.0 619 */ 620 reset: function() {}, 621 622 /** 623 * @access private 624 * @since 3.5.0 625 */ 626 _ready: function() { 627 this._updateMenu(); 628 }, 629 630 /** 631 * @access private 632 * @since 3.5.0 633 */ 634 _preActivate: function() { 635 this.active = true; 636 }, 637 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 ); 647 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(); 655 }, 656 657 /** 658 * @access private 659 * @since 3.5.0 660 */ 661 _deactivate: function() { 662 this.active = false; 663 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 ); 670 }, 671 672 /** 673 * @access private 674 * @since 3.5.0 675 */ 676 _title: function() { 677 this.frame.title.render( this.get('titleMode') || 'default' ); 678 }, 679 680 /** 681 * @access private 682 * @since 3.5.0 683 */ 684 _renderTitle: function( view ) { 685 view.$el.text( this.get('title') || '' ); 686 }, 687 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; 700 } 701 702 this.frame.router.render( mode ); 703 704 view = router.get(); 705 if ( view && view.select ) { 706 view.select( this.frame.content.mode() ); 707 } 708 }, 709 710 /** 711 * @access private 712 * @since 3.5.0 713 */ 714 _menu: function() { 715 var menu = this.frame.menu, 716 mode = this.get('menu'), 717 view; 718 719 this.frame.$el.toggleClass( 'hide-menu', ! mode ); 720 if ( ! mode ) { 721 return; 722 } 723 724 menu.mode( mode ); 725 726 view = menu.get(); 727 if ( view && view.select ) { 728 view.select( this.id ); 729 } 730 }, 731 732 /** 733 * @access private 734 * @since 3.5.0 735 */ 736 _updateMenu: function() { 737 var previous = this.previous('menu'), 738 menu = this.get('menu'); 739 740 if ( previous ) { 741 this.frame.off( 'menu:render:' + previous, this._renderMenu, this ); 742 } 743 744 if ( menu ) { 745 this.frame.on( 'menu:render:' + menu, this._renderMenu, this ); 746 } 747 }, 748 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'); 761 762 if ( ! menuItem && title ) { 763 menuItem = { text: title }; 764 765 if ( priority ) { 766 menuItem.priority = priority; 767 } 768 } 769 770 if ( ! menuItem ) { 771 return; 772 } 773 774 view.set( this.id, menuItem ); 775 } 776 }); 777 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; 791 792 793 /***/ }), 794 /* 30 */ 795 /***/ (function(module, exports) { 796 797 /** 798 * wp.media.selectionSync 799 * 800 * Sync an attachments selection in a state with another state. 801 * 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. 804 * 805 * @memberOf wp.media 806 * 807 * @mixin 808 */ 809 var selectionSync = { 810 /** 811 * @since 3.5.0 812 */ 813 syncSelection: function() { 814 var selection = this.get('selection'), 815 manager = this.frame._selection; 816 817 if ( ! this.get('syncSelection') || ! manager || ! selection ) { 818 return; 819 } 820 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 } 830 831 // Sync the selection's single item with the master. 832 selection.single( manager.single ); 833 }, 834 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 * 841 * @since 3.5.0 842 */ 843 recordSelection: function() { 844 var selection = this.get('selection'), 845 manager = this.frame._selection; 846 847 if ( ! this.get('syncSelection') || ! manager || ! selection ) { 848 return; 849 } 850 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; 859 } 860 }; 861 862 module.exports = selectionSync; 863 864 865 /***/ }), 866 /* 31 */ 867 /***/ (function(module, exports) { 868 269 869 var l10n = wp.media.view.l10n, 270 Cropper; 870 getUserSetting = window.getUserSetting, 871 setUserSetting = window.setUserSetting, 872 Library; 271 873 272 874 /** 273 * wp.media.controller. Cropper274 * 275 * A state for c ropping an image.875 * wp.media.controller.Library 876 * 877 * A state for choosing an attachment or group of attachments from the media library. 276 878 * 277 879 * @memberOf wp.media.controller … … 280 882 * @augments wp.media.controller.State 281 883 * @augments Backbone.Model 884 * @mixes media.selectionSync 885 * 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. 282 909 */ 283 Cropper = wp.media.controller.State.extend(/** @lends wp.media.controller.Cropper.prototype */{910 Library = wp.media.controller.State.extend(/** @lends wp.media.controller.Library.prototype */{ 284 911 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 }, 296 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 }, 927 928 /** 929 * If a library isn't provided, query all media items. 930 * If a selection instance isn't provided, create one. 931 * 932 * @since 3.5.0 933 */ 934 initialize: function() { 935 var selection = this.get('selection'), 936 props; 937 938 if ( ! this.get('library') ) { 939 this.set( 'library', wp.media.query() ); 940 } 941 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(); 957 }, 958 959 /** 960 * @since 3.5.0 961 */ 297 962 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 }, 302 963 this.syncSelection(); 964 965 wp.Uploader.queue.on( 'add', this.uploading, this ); 966 967 this.get('selection').on( 'add remove reset', this.refreshContent, this ); 968 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 }, 974 975 /** 976 * @since 3.5.0 977 */ 303 978 deactivate: function() { 304 this.frame.toolbar.mode('browse'); 305 }, 306 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); 314 315 }, 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 325 canSkipCrop = this.get('canSkipCrop') || false; 326 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 } 979 this.recordSelection(); 980 981 this.frame.off( 'content:activate', this.saveContentMode, this ); 982 983 // Unbind all event handlers that use this state as the context 984 // from the selection. 985 this.get('selection').off( null, null, this ); 986 987 wp.Uploader.queue.off( null, null, this ); 988 }, 989 990 /** 991 * Reset the library to its initial state. 992 * 993 * @since 3.5.0 994 */ 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 ) ); 1031 } 1032 return displays[ attachment.cid ]; 1033 }, 1034 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'; 1050 } 1051 1052 return settings; 1053 }, 1054 1055 /** 1056 * Whether an attachment is image. 1057 * 1058 * @since 4.4.1 1059 * 1060 * @param {wp.media.model.Attachment} attachment 1061 * @returns {Boolean} 1062 */ 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 } 1068 1069 return attachment.get('type') === 'image'; 1070 }, 1071 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; 354 1086 } 355 }; 356 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 }); 372 } 373 374 this.frame.toolbar.set( new wp.media.view.Toolbar(toolbarOptions) ); 375 }, 376 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 ) ); 1087 } 1088 1089 return _.contains( wp.media.view.settings.embedExts, attachment.get('filename').split('.').pop() ); 1090 }, 1091 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 } 1109 }, 1110 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 * 1122 * @since 3.5.0 1123 * 1124 * @param {wp.media.model.Attachment} attachment 1125 */ 1126 uploading: function( attachment ) { 1127 var content = this.frame.content; 1128 1129 if ( 'upload' === content.mode() ) { 1130 this.frame.content.mode('browse'); 1131 } 1132 1133 if ( this.get( 'autoSelect' ) ) { 1134 this.get('selection').add( attachment ); 1135 this.frame.trigger( 'library:selection:add' ); 1136 } 1137 }, 1138 1139 /** 1140 * Persist the mode of the content region as a user setting. 1141 * 1142 * @since 3.5.0 1143 */ 1144 saveContentMode: function() { 1145 if ( 'browse' !== this.get('router') ) { 1146 return; 1147 } 1148 1149 var mode = this.frame.content.mode(), 1150 view = this.frame.router.get(); 1151 1152 if ( view && view.get( mode ) ) { 1153 setUserSetting( 'libraryContent', mode ); 1154 } 387 1155 } 388 1156 }); 389 1157 390 module.exports = Cropper; 391 392 },{}],4:[function(require,module,exports){ 393 var Controller = wp.media.controller, 394 CustomizeImageCropper; 1158 // Make selectionSync available on any Media Library state. 1159 _.extend( Library.prototype, wp.media.selectionSync ); 1160 1161 module.exports = Library; 1162 1163 1164 /***/ }), 1165 /* 32 */ 1166 /***/ (function(module, exports) { 1167 1168 var State = wp.media.controller.State, 1169 Library = wp.media.controller.Library, 1170 l10n = wp.media.view.l10n, 1171 ImageDetails; 395 1172 396 1173 /** 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 } 433 }); 434 435 module.exports = CustomizeImageCropper; 436 437 },{}],5:[function(require,module,exports){ 438 var l10n = wp.media.view.l10n, 439 EditImage; 440 441 /** 442 * wp.media.controller.EditImage 443 * 444 * A state for editing (cropping, etc.) an image. 1174 * wp.media.controller.ImageDetails 1175 * 1176 * A state for editing the attachment display settings of an image that's been 1177 * inserted into the editor. 445 1178 * 446 1179 * @memberOf wp.media.controller … … 450 1183 * @augments Backbone.Model 451 1184 * 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. 1185 * @param {object} [attributes] The attributes hash passed to the state. 1186 * @param {string} [attributes.id=image-details] Unique identifier. 1187 * @param {string} [attributes.title=Image Details] Title for the state. Displays in the frame's title region. 1188 * @param {wp.media.model.Attachment} attributes.image The image's model. 1189 * @param {string|false} [attributes.content=image-details] Initial mode for the content region. 1190 * @param {string|false} [attributes.menu=false] Initial mode for the menu region. 1191 * @param {string|false} [attributes.router=false] Initial mode for the router region. 1192 * @param {string|false} [attributes.toolbar=image-details] Initial mode for the toolbar region. 1193 * @param {boolean} [attributes.editing=false] Unused. 1194 * @param {int} [attributes.priority=60] Unused. 1195 * 1196 * @todo This state inherits some defaults from media.controller.Library.prototype.defaults, 1197 * however this may not do anything. 460 1198 */ 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: '' 469 }, 1199 ImageDetails = State.extend(/** @lends wp.media.controller.ImageDetails.prototype */{ 1200 defaults: _.defaults({ 1201 id: 'image-details', 1202 title: l10n.imageDetailsTitle, 1203 content: 'image-details', 1204 menu: false, 1205 router: false, 1206 toolbar: 'image-details', 1207 editing: false, 1208 priority: 60 1209 }, Library.prototype.defaults ), 470 1210 471 1211 /** 472 1212 * @since 3.9.0 1213 * 1214 * @param options Attributes 1215 */ 1216 initialize: function( options ) { 1217 this.image = options.image; 1218 State.prototype.initialize.apply( this, arguments ); 1219 }, 1220 1221 /** 1222 * @since 3.9.0 473 1223 */ 474 1224 activate: function() { 475 this.frame.on( 'toolbar:render:edit-image', _.bind( this.toolbar, this ) ); 476 }, 477 478 /** 479 * @since 3.9.0 480 */ 481 deactivate: function() { 482 this.frame.off( 'toolbar:render:edit-image' ); 483 }, 484 485 /** 486 * @since 3.9.0 487 */ 488 toolbar: function() { 489 var frame = this.frame, 490 lastState = frame.lastState(), 491 previous = lastState && lastState.id; 492 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 }) ); 1225 this.frame.modal.$el.addClass('image-details'); 510 1226 } 511 1227 }); 512 1228 513 module.exports = EditImage; 514 515 },{}],6:[function(require,module,exports){ 516 var l10n = wp.media.view.l10n, 517 $ = Backbone.$, 518 Embed; 519 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: {} 554 }, 555 556 // The amount of time used when debouncing the scan. 557 sensitivity: 400, 558 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 ); 566 }, 567 568 /** 569 * Trigger a scan of the embedded URL's content for metadata required to embed. 570 * 571 * @fires wp.media.controller.Embed#scan 572 */ 573 scan: function() { 574 var scanners, 575 embed = this, 576 attributes = { 577 type: 'link', 578 scanners: [] 579 }; 580 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 } 587 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; 597 } 598 599 attributes.loading = !! attributes.scanners; 600 this.set( attributes ); 601 }, 602 /** 603 * Try scanning the embed as an image to discover its dimensions. 604 * 605 * @param {Object} attributes 606 */ 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(); 613 614 attributes.scanners.push( deferred.promise() ); 615 616 // Try to load the image and find its width/height. 617 image.onload = function() { 618 deferred.resolve(); 619 620 if ( state !== frame.state() || url !== state.props.get('url') ) { 621 return; 622 } 623 624 state.set({ 625 type: 'image' 626 }); 627 628 state.props.set({ 629 width: image.width, 630 height: image.height 631 }); 632 }; 633 634 image.onerror = deferred.reject; 635 image.src = url; 636 }, 637 638 refresh: function() { 639 this.frame.toolbar.get().refresh(); 640 }, 641 642 reset: function() { 643 this.props.clear().set({ url: '' }); 644 645 if ( this.active ) { 646 this.refresh(); 647 } 648 } 649 }); 650 651 module.exports = Embed; 652 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; 658 659 /** 660 * wp.media.controller.FeaturedImage 661 * 662 * A state for selecting a featured image for a post. 663 * 664 * @memberOf wp.media.controller 665 * 666 * @class 667 * @augments wp.media.controller.Library 668 * @augments wp.media.controller.State 669 * @augments Backbone.Model 670 * 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. 691 */ 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 703 /** 704 * @since 3.5.0 705 */ 706 initialize: function() { 707 var library, comparator; 708 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' }) ); 712 } 713 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 ); 754 755 Library.prototype.deactivate.apply( this, arguments ); 756 }, 757 758 /** 759 * @since 3.5.0 760 */ 761 updateSelection: function() { 762 var selection = this.get('selection'), 763 id = wp.media.view.settings.post.featuredImageId, 764 attachment; 765 766 if ( '' !== id && -1 !== id ) { 767 attachment = Attachment.get( id ); 768 attachment.fetch(); 769 } 770 771 selection.reset( attachment ? [ attachment ] : [] ); 772 } 773 }); 774 775 module.exports = FeaturedImage; 776 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; 782 783 /** 784 * wp.media.controller.GalleryAdd 785 * 786 * A state for selecting more images to add to a gallery. 787 * 788 * @memberOf wp.media.controller 789 * 790 * @class 791 * @augments wp.media.controller.Library 792 * @augments wp.media.controller.State 793 * @augments Backbone.Model 794 * 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. 815 */ 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 ), 827 828 /** 829 * @since 3.5.0 830 */ 831 initialize: function() { 832 // If a library wasn't supplied, create a library of images. 833 if ( ! this.get('library') ) { 834 this.set( 'library', wp.media.query({ type: 'image' }) ); 835 } 836 837 Library.prototype.initialize.apply( this, arguments ); 838 }, 839 840 /** 841 * @since 3.5.0 842 */ 843 activate: function() { 844 var library = this.get('library'), 845 edit = this.frame.state('gallery-edit').get('library'); 846 847 if ( this.editLibrary && this.editLibrary !== edit ) { 848 library.unobserve( this.editLibrary ); 849 } 850 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 }; 856 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; 863 864 Library.prototype.activate.apply( this, arguments ); 865 } 866 }); 867 868 module.exports = GalleryAdd; 869 870 },{}],9:[function(require,module,exports){ 1229 module.exports = ImageDetails; 1230 1231 1232 /***/ }), 1233 /* 33 */ 1234 /***/ (function(module, exports) { 1235 871 1236 var Library = wp.media.controller.Library, 872 1237 l10n = wp.media.view.l10n, … … 1012 1377 module.exports = GalleryEdit; 1013 1378 1014 },{}],10:[function(require,module,exports){ 1015 var State = wp.media.controller.State, 1379 1380 /***/ }), 1381 /* 34 */ 1382 /***/ (function(module, exports) { 1383 1384 var Selection = wp.media.model.Selection, 1016 1385 Library = wp.media.controller.Library, 1017 1386 l10n = wp.media.view.l10n, 1018 ImageDetails;1387 GalleryAdd; 1019 1388 1020 1389 /** 1021 * wp.media.controller.ImageDetails 1022 * 1023 * A state for editing the attachment display settings of an image that's been 1024 * inserted into the editor. 1025 * 1026 * @memberOf wp.media.controller 1027 * 1028 * @class 1029 * @augments wp.media.controller.State 1030 * @augments Backbone.Model 1031 * 1032 * @param {object} [attributes] The attributes hash passed to the state. 1033 * @param {string} [attributes.id=image-details] Unique identifier. 1034 * @param {string} [attributes.title=Image Details] Title for the state. Displays in the frame's title region. 1035 * @param {wp.media.model.Attachment} attributes.image The image's model. 1036 * @param {string|false} [attributes.content=image-details] Initial mode for the content region. 1037 * @param {string|false} [attributes.menu=false] Initial mode for the menu region. 1038 * @param {string|false} [attributes.router=false] Initial mode for the router region. 1039 * @param {string|false} [attributes.toolbar=image-details] Initial mode for the toolbar region. 1040 * @param {boolean} [attributes.editing=false] Unused. 1041 * @param {int} [attributes.priority=60] Unused. 1042 * 1043 * @todo This state inherits some defaults from media.controller.Library.prototype.defaults, 1044 * however this may not do anything. 1045 */ 1046 ImageDetails = State.extend(/** @lends wp.media.controller.ImageDetails.prototype */{ 1047 defaults: _.defaults({ 1048 id: 'image-details', 1049 title: l10n.imageDetailsTitle, 1050 content: 'image-details', 1051 menu: false, 1052 router: false, 1053 toolbar: 'image-details', 1054 editing: false, 1055 priority: 60 1056 }, Library.prototype.defaults ), 1057 1058 /** 1059 * @since 3.9.0 1060 * 1061 * @param options Attributes 1062 */ 1063 initialize: function( options ) { 1064 this.image = options.image; 1065 State.prototype.initialize.apply( this, arguments ); 1066 }, 1067 1068 /** 1069 * @since 3.9.0 1070 */ 1071 activate: function() { 1072 this.frame.modal.$el.addClass('image-details'); 1073 } 1074 }); 1075 1076 module.exports = ImageDetails; 1077 1078 },{}],11:[function(require,module,exports){ 1079 var l10n = wp.media.view.l10n, 1080 getUserSetting = window.getUserSetting, 1081 setUserSetting = window.setUserSetting, 1082 Library; 1083 1084 /** 1085 * wp.media.controller.Library 1086 * 1087 * A state for choosing an attachment or group of attachments from the media library. 1088 * 1089 * @memberOf wp.media.controller 1090 * 1091 * @class 1092 * @augments wp.media.controller.State 1093 * @augments Backbone.Model 1094 * @mixes media.selectionSync 1095 * 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. 1119 */ 1120 Library = wp.media.controller.State.extend(/** @lends wp.media.controller.Library.prototype */{ 1121 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 1136 }, 1137 1138 /** 1139 * If a library isn't provided, query all media items. 1140 * If a selection instance isn't provided, create one. 1141 * 1142 * @since 3.5.0 1143 */ 1144 initialize: function() { 1145 var selection = this.get('selection'), 1146 props; 1147 1148 if ( ! this.get('library') ) { 1149 this.set( 'library', wp.media.query() ); 1150 } 1151 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 }) ); 1164 } 1165 1166 this.resetDisplays(); 1167 }, 1168 1169 /** 1170 * @since 3.5.0 1171 */ 1172 activate: function() { 1173 this.syncSelection(); 1174 1175 wp.Uploader.queue.on( 'add', this.uploading, this ); 1176 1177 this.get('selection').on( 'add remove reset', this.refreshContent, this ); 1178 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 } 1183 }, 1184 1185 /** 1186 * @since 3.5.0 1187 */ 1188 deactivate: function() { 1189 this.recordSelection(); 1190 1191 this.frame.off( 'content:activate', this.saveContentMode, this ); 1192 1193 // Unbind all event handlers that use this state as the context 1194 // from the selection. 1195 this.get('selection').off( null, null, this ); 1196 1197 wp.Uploader.queue.off( null, null, this ); 1198 }, 1199 1200 /** 1201 * Reset the library to its initial state. 1202 * 1203 * @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 * 1216 * @since 3.5.0 1217 */ 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 }, 1227 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; 1238 1239 if ( ! displays[ attachment.cid ] ) { 1240 displays[ attachment.cid ] = new Backbone.Model( this.defaultDisplaySettings( attachment ) ); 1241 } 1242 return displays[ attachment.cid ]; 1243 }, 1244 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 ); 1255 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 } 1261 1262 return settings; 1263 }, 1264 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 } 1278 1279 return attachment.get('type') === 'image'; 1280 }, 1281 1282 /** 1283 * Whether an attachment can be embedded (audio or video). 1284 * 1285 * @since 3.6.0 1286 * 1287 * @param {wp.media.model.Attachment} attachment 1288 * @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 1302 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(); 1315 1316 if ( this.active && ! selection.length && router && ! router.get( mode ) ) { 1317 this.frame.content.render( this.get('content') ); 1318 } 1319 }, 1320 1321 /** 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 multiple 1329 * attachments are uploaded, the last attachment in the upload queue will 1330 * be selected. 1331 * 1332 * @since 3.5.0 1333 * 1334 * @param {wp.media.model.Attachment} attachment 1335 */ 1336 uploading: function( attachment ) { 1337 var content = this.frame.content; 1338 1339 if ( 'upload' === content.mode() ) { 1340 this.frame.content.mode('browse'); 1341 } 1342 1343 if ( this.get( 'autoSelect' ) ) { 1344 this.get('selection').add( attachment ); 1345 this.frame.trigger( 'library:selection:add' ); 1346 } 1347 }, 1348 1349 /** 1350 * Persist the mode of the content region as a user setting. 1351 * 1352 * @since 3.5.0 1353 */ 1354 saveContentMode: function() { 1355 if ( 'browse' !== this.get('router') ) { 1356 return; 1357 } 1358 1359 var mode = this.frame.content.mode(), 1360 view = this.frame.router.get(); 1361 1362 if ( view && view.get( mode ) ) { 1363 setUserSetting( 'libraryContent', mode ); 1364 } 1365 } 1366 }); 1367 1368 // Make selectionSync available on any Media Library state. 1369 _.extend( Library.prototype, wp.media.selectionSync ); 1370 1371 module.exports = Library; 1372 1373 },{}],12:[function(require,module,exports){ 1374 /** 1375 * wp.media.controller.MediaLibrary 1390 * wp.media.controller.GalleryAdd 1391 * 1392 * A state for selecting more images to add to a gallery. 1376 1393 * 1377 1394 * @memberOf wp.media.controller … … 1381 1398 * @augments wp.media.controller.State 1382 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. 1383 1421 */ 1384 var Library = wp.media.controller.Library, 1385 MediaLibrary; 1386 1387 MediaLibrary = Library.extend(/** @lends wp.media.controller.MediaLibrary.prototype */{ 1422 GalleryAdd = Library.extend(/** @lends wp.media.controller.GalleryAdd.prototype */{ 1388 1423 defaults: _.defaults({ 1389 // Attachments browser defaults. @see media.view.AttachmentsBrowser 1390 filterable: 'uploaded', 1391 1392 displaySettings: false, 1393 priority: 80, 1394 syncSelection: false 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 1395 1432 }, Library.prototype.defaults ), 1396 1433 1397 1434 /** 1398 * @since 3.9.0 1399 * 1400 * @param options 1401 */ 1402 initialize: function( options ) { 1403 this.media = options.media; 1404 this.type = options.type; 1405 this.set( 'library', wp.media.query({ type: this.type }) ); 1435 * @since 3.5.0 1436 */ 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' }) ); 1441 } 1406 1442 1407 1443 Library.prototype.initialize.apply( this, arguments ); … … 1409 1445 1410 1446 /** 1411 * @since 3. 9.01447 * @since 3.5.0 1412 1448 */ 1413 1449 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 } 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 ); 1455 } 1456 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 }; 1462 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 1419 1470 Library.prototype.activate.apply( this, arguments ); 1420 1471 } 1421 1472 }); 1422 1473 1423 module.exports = MediaLibrary; 1424 1425 },{}],13:[function(require,module,exports){ 1474 module.exports = GalleryAdd; 1475 1476 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 1426 1486 /** 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. 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. 1438 1491 * 1439 1492 * @memberOf wp.media.controller 1440 1493 * 1441 1494 * @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. 1495 * @augments wp.media.controller.Library 1496 * @augments wp.media.controller.State 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'). 1447 1522 */ 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. 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 }, 1538 1539 /** 1540 * @since 3.9.0 1541 */ 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' ); 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 } 1560 Library.prototype.initialize.apply( this, arguments ); 1561 }, 1562 1563 /** 1564 * @since 3.9.0 1565 */ 1566 activate: function() { 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 1577 Library.prototype.activate.apply( this, arguments ); 1578 }, 1579 1580 /** 1581 * @since 3.9.0 1582 */ 1583 deactivate: function() { 1584 // Stop watching for uploaded attachments. 1585 this.get('library').unobserve( wp.Uploader.queue ); 1586 1587 this.frame.off( 'content:render:browse', this.renderSettings, this ); 1588 1589 Library.prototype.deactivate.apply( this, arguments ); 1590 }, 1591 1592 /** 1593 * Render the collection embed settings view in the browser sidebar. 1458 1594 * 1459 * @since 3.5.0 1595 * @todo This is against the pattern elsewhere in media. Typically the frame 1596 * is responsible for adding region mode callbacks. Explain. 1460 1597 * 1461 * @ param {string} mode1598 * @since 3.9.0 1462 1599 * 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 ); 1486 1487 /** 1488 * Region mode activation event. 1489 * 1490 * @event wp.media.controller.Region#activate 1491 */ 1492 this.trigger('activate'); 1493 return this; 1494 }, 1495 /** 1496 * Render a mode. 1497 * 1498 * @since 3.5.0 1499 * 1500 * @param {string} mode 1501 * 1502 * @fires Region#create 1503 * @fires Region#render 1504 * 1505 * @returns {wp.media.controller.Region} Returns itself to allow chaining 1506 */ 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; 1527 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 ); 1539 } 1540 return this; 1541 }, 1542 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 }, 1553 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 }, 1569 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; 1580 1581 if ( ! this._mode ) { 1600 * @param {wp.media.view.attachmentsBrowser} The attachments browser view. 1601 */ 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 = {}; 1608 1609 if ( ! library || ! attachmentsBrowserView ) { 1582 1610 return; 1583 1611 } 1584 1612 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 ); 1591 1592 // Trigger `{this.id}:{event}` event on the frame. 1593 args[0] = base; 1594 this.view.trigger.apply( this.view, args ); 1595 return this; 1613 library[ collectionType ] = library[ collectionType ] || new Backbone.Model(); 1614 1615 obj[ collectionType ] = new SettingsView({ 1616 control