media-grid.js 27 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049
  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. var media = wp.media;
  80. media.controller.EditAttachmentMetadata = __webpack_require__( 11 );
  81. media.view.MediaFrame.Manage = __webpack_require__( 12 );
  82. media.view.Attachment.Details.TwoColumn = __webpack_require__( 13 );
  83. media.view.MediaFrame.Manage.Router = __webpack_require__( 14 );
  84. media.view.EditImage.Details = __webpack_require__( 15 );
  85. media.view.MediaFrame.EditAttachments = __webpack_require__( 16 );
  86. media.view.SelectModeToggleButton = __webpack_require__( 17 );
  87. media.view.DeleteSelectedButton = __webpack_require__( 18 );
  88. media.view.DeleteSelectedPermanentlyButton = __webpack_require__( 19 );
  89. /***/ }),
  90. /* 11 */
  91. /***/ (function(module, exports) {
  92. var l10n = wp.media.view.l10n,
  93. EditAttachmentMetadata;
  94. /**
  95. * wp.media.controller.EditAttachmentMetadata
  96. *
  97. * A state for editing an attachment's metadata.
  98. *
  99. * @memberOf wp.media.controller
  100. *
  101. * @class
  102. * @augments wp.media.controller.State
  103. * @augments Backbone.Model
  104. */
  105. EditAttachmentMetadata = wp.media.controller.State.extend(/** @lends wp.media.controller.EditAttachmentMetadata.prototype */{
  106. defaults: {
  107. id: 'edit-attachment',
  108. // Title string passed to the frame's title region view.
  109. title: l10n.attachmentDetails,
  110. // Region mode defaults.
  111. content: 'edit-metadata',
  112. menu: false,
  113. toolbar: false,
  114. router: false
  115. }
  116. });
  117. module.exports = EditAttachmentMetadata;
  118. /***/ }),
  119. /* 12 */
  120. /***/ (function(module, exports) {
  121. var MediaFrame = wp.media.view.MediaFrame,
  122. Library = wp.media.controller.Library,
  123. $ = Backbone.$,
  124. Manage;
  125. /**
  126. * wp.media.view.MediaFrame.Manage
  127. *
  128. * A generic management frame workflow.
  129. *
  130. * Used in the media grid view.
  131. *
  132. * @memberOf wp.media.view.MediaFrame
  133. *
  134. * @class
  135. * @augments wp.media.view.MediaFrame
  136. * @augments wp.media.view.Frame
  137. * @augments wp.media.View
  138. * @augments wp.Backbone.View
  139. * @augments Backbone.View
  140. * @mixes wp.media.controller.StateMachine
  141. */
  142. Manage = MediaFrame.extend(/** @lends wp.media.view.MediaFrame.Manage.prototype */{
  143. /**
  144. * @constructs
  145. */
  146. initialize: function() {
  147. _.defaults( this.options, {
  148. title: '',
  149. modal: false,
  150. selection: [],
  151. library: {}, // Options hash for the query to the media library.
  152. multiple: 'add',
  153. state: 'library',
  154. uploader: true,
  155. mode: [ 'grid', 'edit' ]
  156. });
  157. this.$body = $( document.body );
  158. this.$window = $( window );
  159. this.$adminBar = $( '#wpadminbar' );
  160. // Store the Add New button for later reuse in wp.media.view.UploaderInline.
  161. this.$uploaderToggler = $( '.page-title-action' )
  162. .attr( 'aria-expanded', 'false' )
  163. .on( 'click', _.bind( this.addNewClickHandler, this ) );
  164. this.$window.on( 'scroll resize', _.debounce( _.bind( this.fixPosition, this ), 15 ) );
  165. // Ensure core and media grid view UI is enabled.
  166. this.$el.addClass('wp-core-ui');
  167. // Force the uploader off if the upload limit has been exceeded or
  168. // if the browser isn't supported.
  169. if ( wp.Uploader.limitExceeded || ! wp.Uploader.browser.supported ) {
  170. this.options.uploader = false;
  171. }
  172. // Initialize a window-wide uploader.
  173. if ( this.options.uploader ) {
  174. this.uploader = new wp.media.view.UploaderWindow({
  175. controller: this,
  176. uploader: {
  177. dropzone: document.body,
  178. container: document.body
  179. }
  180. }).render();
  181. this.uploader.ready();
  182. $('body').append( this.uploader.el );
  183. this.options.uploader = false;
  184. }
  185. this.gridRouter = new wp.media.view.MediaFrame.Manage.Router();
  186. // Call 'initialize' directly on the parent class.
  187. MediaFrame.prototype.initialize.apply( this, arguments );
  188. // Append the frame view directly the supplied container.
  189. this.$el.appendTo( this.options.container );
  190. this.createStates();
  191. this.bindRegionModeHandlers();
  192. this.render();
  193. this.bindSearchHandler();
  194. wp.media.frames.browse = this;
  195. },
  196. bindSearchHandler: function() {
  197. var search = this.$( '#media-search-input' ),
  198. searchView = this.browserView.toolbar.get( 'search' ).$el,
  199. listMode = this.$( '.view-list' ),
  200. input = _.throttle( function (e) {
  201. var val = $( e.currentTarget ).val(),
  202. url = '';
  203. if ( val ) {
  204. url += '?search=' + val;
  205. this.gridRouter.navigate( this.gridRouter.baseUrl( url ), { replace: true } );
  206. }
  207. }, 1000 );
  208. // Update the URL when entering search string (at most once per second)
  209. search.on( 'input', _.bind( input, this ) );
  210. this.gridRouter
  211. .on( 'route:search', function () {
  212. var href = window.location.href;
  213. if ( href.indexOf( 'mode=' ) > -1 ) {
  214. href = href.replace( /mode=[^&]+/g, 'mode=list' );
  215. } else {
  216. href += href.indexOf( '?' ) > -1 ? '&mode=list' : '?mode=list';
  217. }
  218. href = href.replace( 'search=', 's=' );
  219. listMode.prop( 'href', href );
  220. })
  221. .on( 'route:reset', function() {
  222. searchView.val( '' ).trigger( 'input' );
  223. });
  224. },
  225. /**
  226. * Create the default states for the frame.
  227. */
  228. createStates: function() {
  229. var options = this.options;
  230. if ( this.options.states ) {
  231. return;
  232. }
  233. // Add the default states.
  234. this.states.add([
  235. new Library({
  236. library: wp.media.query( options.library ),
  237. multiple: options.multiple,
  238. title: options.title,
  239. content: 'browse',
  240. toolbar: 'select',
  241. contentUserSetting: false,
  242. filterable: 'all',
  243. autoSelect: false
  244. })
  245. ]);
  246. },
  247. /**
  248. * Bind region mode activation events to proper handlers.
  249. */
  250. bindRegionModeHandlers: function() {
  251. this.on( 'content:create:browse', this.browseContent, this );
  252. // Handle a frame-level event for editing an attachment.
  253. this.on( 'edit:attachment', this.openEditAttachmentModal, this );
  254. this.on( 'select:activate', this.bindKeydown, this );
  255. this.on( 'select:deactivate', this.unbindKeydown, this );
  256. },
  257. handleKeydown: function( e ) {
  258. if ( 27 === e.which ) {
  259. e.preventDefault();
  260. this.deactivateMode( 'select' ).activateMode( 'edit' );
  261. }
  262. },
  263. bindKeydown: function() {
  264. this.$body.on( 'keydown.select', _.bind( this.handleKeydown, this ) );
  265. },
  266. unbindKeydown: function() {
  267. this.$body.off( 'keydown.select' );
  268. },
  269. fixPosition: function() {
  270. var $browser, $toolbar;
  271. if ( ! this.isModeActive( 'select' ) ) {
  272. return;
  273. }
  274. $browser = this.$('.attachments-browser');
  275. $toolbar = $browser.find('.media-toolbar');
  276. // Offset doesn't appear to take top margin into account, hence +16
  277. if ( ( $browser.offset().top + 16 ) < this.$window.scrollTop() + this.$adminBar.height() ) {
  278. $browser.addClass( 'fixed' );
  279. $toolbar.css('width', $browser.width() + 'px');
  280. } else {
  281. $browser.removeClass( 'fixed' );
  282. $toolbar.css('width', '');
  283. }
  284. },
  285. /**
  286. * Click handler for the `Add New` button.
  287. */
  288. addNewClickHandler: function( event ) {
  289. event.preventDefault();
  290. this.trigger( 'toggle:upload:attachment' );
  291. if ( this.uploader ) {
  292. this.uploader.refresh();
  293. }
  294. },
  295. /**
  296. * Open the Edit Attachment modal.
  297. */
  298. openEditAttachmentModal: function( model ) {
  299. // Create a new EditAttachment frame, passing along the library and the attachment model.
  300. if ( wp.media.frames.edit ) {
  301. wp.media.frames.edit.open().trigger( 'refresh', model );
  302. } else {
  303. wp.media.frames.edit = wp.media( {
  304. frame: 'edit-attachments',
  305. controller: this,
  306. library: this.state().get('library'),
  307. model: model
  308. } );
  309. }
  310. },
  311. /**
  312. * Create an attachments browser view within the content region.
  313. *
  314. * @param {Object} contentRegion Basic object with a `view` property, which
  315. * should be set with the proper region view.
  316. * @this wp.media.controller.Region
  317. */
  318. browseContent: function( contentRegion ) {
  319. var state = this.state();
  320. // Browse our library of attachments.
  321. this.browserView = contentRegion.view = new wp.media.view.AttachmentsBrowser({
  322. controller: this,
  323. collection: state.get('library'),
  324. selection: state.get('selection'),
  325. model: state,
  326. sortable: state.get('sortable'),
  327. search: state.get('searchable'),
  328. filters: state.get('filterable'),
  329. date: state.get('date'),
  330. display: state.get('displaySettings'),
  331. dragInfo: state.get('dragInfo'),
  332. sidebar: 'errors',
  333. suggestedWidth: state.get('suggestedWidth'),
  334. suggestedHeight: state.get('suggestedHeight'),
  335. AttachmentView: state.get('AttachmentView'),
  336. scrollElement: document
  337. });
  338. this.browserView.on( 'ready', _.bind( this.bindDeferred, this ) );
  339. this.errors = wp.Uploader.errors;
  340. this.errors.on( 'add remove reset', this.sidebarVisibility, this );
  341. },
  342. sidebarVisibility: function() {
  343. this.browserView.$( '.media-sidebar' ).toggle( !! this.errors.length );
  344. },
  345. bindDeferred: function() {
  346. if ( ! this.browserView.dfd ) {
  347. return;
  348. }
  349. this.browserView.dfd.done( _.bind( this.startHistory, this ) );
  350. },
  351. startHistory: function() {
  352. // Verify pushState support and activate
  353. if ( window.history && window.history.pushState ) {
  354. if ( Backbone.History.started ) {
  355. Backbone.history.stop();
  356. }
  357. Backbone.history.start( {
  358. root: window._wpMediaGridSettings.adminUrl,
  359. pushState: true
  360. } );
  361. }
  362. }
  363. });
  364. module.exports = Manage;
  365. /***/ }),
  366. /* 13 */
  367. /***/ (function(module, exports) {
  368. var Details = wp.media.view.Attachment.Details,
  369. TwoColumn;
  370. /**
  371. * wp.media.view.Attachment.Details.TwoColumn
  372. *
  373. * A similar view to media.view.Attachment.Details
  374. * for use in the Edit Attachment modal.
  375. *
  376. * @memberOf wp.media.view.Attachment.Details
  377. *
  378. * @class
  379. * @augments wp.media.view.Attachment.Details
  380. * @augments wp.media.view.Attachment
  381. * @augments wp.media.View
  382. * @augments wp.Backbone.View
  383. * @augments Backbone.View
  384. */
  385. TwoColumn = Details.extend(/** @lends wp.media.view.Attachment.Details.TowColumn.prototype */{
  386. template: wp.template( 'attachment-details-two-column' ),
  387. initialize: function() {
  388. this.controller.on( 'content:activate:edit-details', _.bind( this.editAttachment, this ) );
  389. Details.prototype.initialize.apply( this, arguments );
  390. },
  391. editAttachment: function( event ) {
  392. if ( event ) {
  393. event.preventDefault();
  394. }
  395. this.controller.content.mode( 'edit-image' );
  396. },
  397. /**
  398. * Noop this from parent class, doesn't apply here.
  399. */
  400. toggleSelectionHandler: function() {},
  401. render: function() {
  402. Details.prototype.render.apply( this, arguments );
  403. wp.media.mixin.removeAllPlayers();
  404. this.$( 'audio, video' ).each( function (i, elem) {
  405. var el = wp.media.view.MediaDetails.prepareSrc( elem );
  406. new window.MediaElementPlayer( el, wp.media.mixin.mejsSettings );
  407. } );
  408. }
  409. });
  410. module.exports = TwoColumn;
  411. /***/ }),
  412. /* 14 */
  413. /***/ (function(module, exports) {
  414. /**
  415. * wp.media.view.MediaFrame.Manage.Router
  416. *
  417. * A router for handling the browser history and application state.
  418. *
  419. * @memberOf wp.media.view.MediaFrame.Manage
  420. *
  421. * @class
  422. * @augments Backbone.Router
  423. */
  424. var Router = Backbone.Router.extend(/** @lends wp.media.view.MediaFrame.Manage.Router.prototype */{
  425. routes: {
  426. 'upload.php?item=:slug&mode=edit': 'editItem',
  427. 'upload.php?item=:slug': 'showItem',
  428. 'upload.php?search=:query': 'search',
  429. 'upload.php': 'reset'
  430. },
  431. // Map routes against the page URL
  432. baseUrl: function( url ) {
  433. return 'upload.php' + url;
  434. },
  435. reset: function() {
  436. var frame = wp.media.frames.edit;
  437. if ( frame ) {
  438. frame.close();
  439. }
  440. },
  441. // Respond to the search route by filling the search field and trigggering the input event
  442. search: function( query ) {
  443. jQuery( '#media-search-input' ).val( query ).trigger( 'input' );
  444. },
  445. // Show the modal with a specific item
  446. showItem: function( query ) {
  447. var media = wp.media,
  448. frame = media.frames.browse,
  449. library = frame.state().get('library'),
  450. item;
  451. // Trigger the media frame to open the correct item
  452. item = library.findWhere( { id: parseInt( query, 10 ) } );
  453. item.set( 'skipHistory', true );
  454. if ( item ) {
  455. frame.trigger( 'edit:attachment', item );
  456. } else {
  457. item = media.attachment( query );
  458. frame.listenTo( item, 'change', function( model ) {
  459. frame.stopListening( item );
  460. frame.trigger( 'edit:attachment', model );
  461. } );
  462. item.fetch();
  463. }
  464. },
  465. // Show the modal in edit mode with a specific item.
  466. editItem: function( query ) {
  467. this.showItem( query );
  468. wp.media.frames.edit.content.mode( 'edit-details' );
  469. }
  470. });
  471. module.exports = Router;
  472. /***/ }),
  473. /* 15 */
  474. /***/ (function(module, exports) {
  475. var View = wp.media.View,
  476. EditImage = wp.media.view.EditImage,
  477. Details;
  478. /**
  479. * wp.media.view.EditImage.Details
  480. *
  481. * @memberOf wp.media.view.EditImage
  482. *
  483. * @class
  484. * @augments wp.media.view.EditImage
  485. * @augments wp.media.View
  486. * @augments wp.Backbone.View
  487. * @augments Backbone.View
  488. */
  489. Details = EditImage.extend(/** @lends wp.media.view.EditImage.Details.prototype */{
  490. initialize: function( options ) {
  491. this.editor = window.imageEdit;
  492. this.frame = options.frame;
  493. this.controller = options.controller;
  494. View.prototype.initialize.apply( this, arguments );
  495. },
  496. back: function() {
  497. this.frame.content.mode( 'edit-metadata' );
  498. },
  499. save: function() {
  500. this.model.fetch().done( _.bind( function() {
  501. this.frame.content.mode( 'edit-metadata' );
  502. }, this ) );
  503. }
  504. });
  505. module.exports = Details;
  506. /***/ }),
  507. /* 16 */
  508. /***/ (function(module, exports) {
  509. var Frame = wp.media.view.Frame,
  510. MediaFrame = wp.media.view.MediaFrame,
  511. $ = jQuery,
  512. EditAttachments;
  513. /**
  514. * wp.media.view.MediaFrame.EditAttachments
  515. *
  516. * A frame for editing the details of a specific media item.
  517. *
  518. * Opens in a modal by default.
  519. *
  520. * Requires an attachment model to be passed in the options hash under `model`.
  521. *
  522. * @memberOf wp.media.view.MediaFrame
  523. *
  524. * @class
  525. * @augments wp.media.view.Frame
  526. * @augments wp.media.View
  527. * @augments wp.Backbone.View
  528. * @augments Backbone.View
  529. * @mixes wp.media.controller.StateMachine
  530. */
  531. EditAttachments = MediaFrame.extend(/** @lends wp.media.view.MediaFrame.EditAttachments.prototype */{
  532. className: 'edit-attachment-frame',
  533. template: wp.template( 'edit-attachment-frame' ),
  534. regions: [ 'title', 'content' ],
  535. events: {
  536. 'click .left': 'previousMediaItem',
  537. 'click .right': 'nextMediaItem'
  538. },
  539. initialize: function() {
  540. Frame.prototype.initialize.apply( this, arguments );
  541. _.defaults( this.options, {
  542. modal: true,
  543. state: 'edit-attachment'
  544. });
  545. this.controller = this.options.controller;
  546. this.gridRouter = this.controller.gridRouter;
  547. this.library = this.options.library;
  548. if ( this.options.model ) {
  549. this.model = this.options.model;
  550. }
  551. this.bindHandlers();
  552. this.createStates();
  553. this.createModal();
  554. this.title.mode( 'default' );
  555. this.toggleNav();
  556. },
  557. bindHandlers: function() {
  558. // Bind default title creation.
  559. this.on( 'title:create:default', this.createTitle, this );
  560. this.on( 'content:create:edit-metadata', this.editMetadataMode, this );
  561. this.on( 'content:create:edit-image', this.editImageMode, this );
  562. this.on( 'content:render:edit-image', this.editImageModeRender, this );
  563. this.on( 'refresh', this.rerender, this );
  564. this.on( 'close', this.detach );
  565. this.bindModelHandlers();
  566. this.listenTo( this.gridRouter, 'route:search', this.close, this );
  567. },
  568. bindModelHandlers: function() {
  569. // Close the modal if the attachment is deleted.
  570. this.listenTo( this.model, 'change:status destroy', this.close, this );
  571. },
  572. createModal: function() {
  573. // Initialize modal container view.
  574. if ( this.options.modal ) {
  575. this.modal = new wp.media.view.Modal({
  576. controller: this,
  577. title: this.options.title
  578. });
  579. this.modal.on( 'open', _.bind( function () {
  580. $( 'body' ).on( 'keydown.media-modal', _.bind( this.keyEvent, this ) );
  581. }, this ) );
  582. // Completely destroy the modal DOM element when closing it.
  583. this.modal.on( 'close', _.bind( function() {
  584. $( 'body' ).off( 'keydown.media-modal' ); /* remove the keydown event */
  585. // Restore the original focus item if possible
  586. $( 'li.attachment[data-id="' + this.model.get( 'id' ) +'"]' ).focus();
  587. this.resetRoute();
  588. }, this ) );
  589. // Set this frame as the modal's content.
  590. this.modal.content( this );
  591. this.modal.open();
  592. }
  593. },
  594. /**
  595. * Add the default states to the frame.
  596. */
  597. createStates: function() {
  598. this.states.add([
  599. new wp.media.controller.EditAttachmentMetadata({
  600. model: this.model,
  601. library: this.library
  602. })
  603. ]);
  604. },
  605. /**
  606. * Content region rendering callback for the `edit-metadata` mode.
  607. *
  608. * @param {Object} contentRegion Basic object with a `view` property, which
  609. * should be set with the proper region view.
  610. */
  611. editMetadataMode: function( contentRegion ) {
  612. contentRegion.view = new wp.media.view.Attachment.Details.TwoColumn({
  613. controller: this,
  614. model: this.model
  615. });
  616. /**
  617. * Attach a subview to display fields added via the
  618. * `attachment_fields_to_edit` filter.
  619. */
  620. contentRegion.view.views.set( '.attachment-compat', new wp.media.view.AttachmentCompat({
  621. controller: this,
  622. model: this.model
  623. }) );
  624. // Update browser url when navigating media details, except on load.
  625. if ( this.model && ! this.model.get( 'skipHistory' ) ) {
  626. this.gridRouter.navigate( this.gridRouter.baseUrl( '?item=' + this.model.id ) );
  627. }
  628. },
  629. /**
  630. * Render the EditImage view into the frame's content region.
  631. *
  632. * @param {Object} contentRegion Basic object with a `view` property, which
  633. * should be set with the proper region view.
  634. */
  635. editImageMode: function( contentRegion ) {
  636. var editImageController = new wp.media.controller.EditImage( {
  637. model: this.model,
  638. frame: this
  639. } );
  640. // Noop some methods.
  641. editImageController._toolbar = function() {};
  642. editImageController._router = function() {};
  643. editImageController._menu = function() {};
  644. contentRegion.view = new wp.media.view.EditImage.Details( {
  645. model: this.model,
  646. frame: this,
  647. controller: editImageController
  648. } );
  649. this.gridRouter.navigate( this.gridRouter.baseUrl( '?item=' + this.model.id + '&mode=edit' ) );
  650. },
  651. editImageModeRender: function( view ) {
  652. view.on( 'ready', view.loadEditor );
  653. },
  654. toggleNav: function() {
  655. this.$('.left').toggleClass( 'disabled', ! this.hasPrevious() );
  656. this.$('.right').toggleClass( 'disabled', ! this.hasNext() );
  657. },
  658. /**
  659. * Rerender the view.
  660. */
  661. rerender: function( model ) {
  662. this.stopListening( this.model );
  663. this.model = model;
  664. this.bindModelHandlers();
  665. // Only rerender the `content` region.
  666. if ( this.content.mode() !== 'edit-metadata' ) {
  667. this.content.mode( 'edit-metadata' );
  668. } else {
  669. this.content.render();
  670. }
  671. this.toggleNav();
  672. },
  673. /**
  674. * Click handler to switch to the previous media item.
  675. */
  676. previousMediaItem: function() {
  677. if ( ! this.hasPrevious() ) {
  678. return;
  679. }
  680. this.trigger( 'refresh', this.library.at( this.getCurrentIndex() - 1 ) );
  681. this.$( '.left' ).focus();
  682. },
  683. /**
  684. * Click handler to switch to the next media item.
  685. */
  686. nextMediaItem: function() {
  687. if ( ! this.hasNext() ) {
  688. return;
  689. }
  690. this.trigger( 'refresh', this.library.at( this.getCurrentIndex() + 1 ) );
  691. this.$( '.right' ).focus();
  692. },
  693. getCurrentIndex: function() {
  694. return this.library.indexOf( this.model );
  695. },
  696. hasNext: function() {
  697. return ( this.getCurrentIndex() + 1 ) < this.library.length;
  698. },
  699. hasPrevious: function() {
  700. return ( this.getCurrentIndex() - 1 ) > -1;
  701. },
  702. /**
  703. * Respond to the keyboard events: right arrow, left arrow, except when
  704. * focus is in a textarea or input field.
  705. */
  706. keyEvent: function( event ) {
  707. if ( ( 'INPUT' === event.target.nodeName || 'TEXTAREA' === event.target.nodeName ) && ! ( event.target.readOnly || event.target.disabled ) ) {
  708. return;
  709. }
  710. // The right arrow key
  711. if ( 39 === event.keyCode ) {
  712. this.nextMediaItem();
  713. }
  714. // The left arrow key
  715. if ( 37 === event.keyCode ) {
  716. this.previousMediaItem();
  717. }
  718. },
  719. resetRoute: function() {
  720. var searchTerm = this.controller.browserView.toolbar.get( 'search' ).$el.val(),
  721. url = '' !== searchTerm ? '?search=' + searchTerm : '';
  722. this.gridRouter.navigate( this.gridRouter.baseUrl( url ), { replace: true } );
  723. }
  724. });
  725. module.exports = EditAttachments;
  726. /***/ }),
  727. /* 17 */
  728. /***/ (function(module, exports) {
  729. var Button = wp.media.view.Button,
  730. l10n = wp.media.view.l10n,
  731. SelectModeToggle;
  732. /**
  733. * wp.media.view.SelectModeToggleButton
  734. *
  735. * @memberOf wp.media.view
  736. *
  737. * @class
  738. * @augments wp.media.view.Button
  739. * @augments wp.media.View
  740. * @augments wp.Backbone.View
  741. * @augments Backbone.View
  742. */
  743. SelectModeToggle = Button.extend(/** @lends wp.media.view.SelectModeToggle.prototype */{
  744. initialize: function() {
  745. _.defaults( this.options, {
  746. size : ''
  747. } );
  748. Button.prototype.initialize.apply( this, arguments );
  749. this.controller.on( 'select:activate select:deactivate', this.toggleBulkEditHandler, this );
  750. this.controller.on( 'selection:action:done', this.back, this );
  751. },
  752. back: function () {
  753. this.controller.deactivateMode( 'select' ).activateMode( 'edit' );
  754. },
  755. click: function() {
  756. Button.prototype.click.apply( this, arguments );
  757. if ( this.controller.isModeActive( 'select' ) ) {
  758. this.back();
  759. } else {
  760. this.controller.deactivateMode( 'edit' ).activateMode( 'select' );
  761. }
  762. },
  763. render: function() {
  764. Button.prototype.render.apply( this, arguments );
  765. this.$el.addClass( 'select-mode-toggle-button' );
  766. return this;
  767. },
  768. toggleBulkEditHandler: function() {
  769. var toolbar = this.controller.content.get().toolbar, children;
  770. children = toolbar.$( '.media-toolbar-secondary > *, .media-toolbar-primary > *' );
  771. // TODO: the Frame should be doing all of this.
  772. if ( this.controller.isModeActive( 'select' ) ) {
  773. this.model.set( {
  774. size: 'large',
  775. text: l10n.cancelSelection
  776. } );
  777. children.not( '.spinner, .media-button' ).hide();
  778. this.$el.show();
  779. toolbar.$( '.delete-selected-button' ).removeClass( 'hidden' );
  780. } else {
  781. this.model.set( {
  782. size: '',
  783. text: l10n.bulkSelect
  784. } );
  785. this.controller.content.get().$el.removeClass( 'fixed' );
  786. toolbar.$el.css( 'width', '' );
  787. toolbar.$( '.delete-selected-button' ).addClass( 'hidden' );
  788. children.not( '.media-button' ).show();
  789. this.controller.state().get( 'selection' ).reset();
  790. }
  791. }
  792. });
  793. module.exports = SelectModeToggle;
  794. /***/ }),
  795. /* 18 */
  796. /***/ (function(module, exports) {
  797. var Button = wp.media.view.Button,
  798. l10n = wp.media.view.l10n,
  799. DeleteSelected;
  800. /**
  801. * wp.media.view.DeleteSelectedButton
  802. *
  803. * A button that handles bulk Delete/Trash logic
  804. *
  805. * @memberOf wp.media.view
  806. *
  807. * @class
  808. * @augments wp.media.view.Button
  809. * @augments wp.media.View
  810. * @augments wp.Backbone.View
  811. * @augments Backbone.View
  812. */
  813. DeleteSelected = Button.extend(/** @lends wp.media.view.DeleteSelectedButton.prototype */{
  814. initialize: function() {
  815. Button.prototype.initialize.apply( this, arguments );
  816. if ( this.options.filters ) {
  817. this.options.filters.model.on( 'change', this.filterChange, this );
  818. }
  819. this.controller.on( 'selection:toggle', this.toggleDisabled, this );
  820. },
  821. filterChange: function( model ) {
  822. if ( 'trash' === model.get( 'status' ) ) {
  823. this.model.set( 'text', l10n.untrashSelected );
  824. } else if ( wp.media.view.settings.mediaTrash ) {
  825. this.model.set( 'text', l10n.trashSelected );
  826. } else {
  827. this.model.set( 'text', l10n.deleteSelected );
  828. }
  829. },
  830. toggleDisabled: function() {
  831. this.model.set( 'disabled', ! this.controller.state().get( 'selection' ).length );
  832. },
  833. render: function() {
  834. Button.prototype.render.apply( this, arguments );
  835. if ( this.controller.isModeActive( 'select' ) ) {
  836. this.$el.addClass( 'delete-selected-button' );
  837. } else {
  838. this.$el.addClass( 'delete-selected-button hidden' );
  839. }
  840. this.toggleDisabled();
  841. return this;
  842. }
  843. });
  844. module.exports = DeleteSelected;
  845. /***/ }),
  846. /* 19 */
  847. /***/ (function(module, exports) {
  848. var Button = wp.media.view.Button,
  849. DeleteSelected = wp.media.view.DeleteSelectedButton,
  850. DeleteSelectedPermanently;
  851. /**
  852. * wp.media.view.DeleteSelectedPermanentlyButton
  853. *
  854. * When MEDIA_TRASH is true, a button that handles bulk Delete Permanently logic
  855. *
  856. * @memberOf wp.media.view
  857. *
  858. * @class
  859. * @augments wp.media.view.DeleteSelectedButton
  860. * @augments wp.media.view.Button
  861. * @augments wp.media.View
  862. * @augments wp.Backbone.View
  863. * @augments Backbone.View
  864. */
  865. DeleteSelectedPermanently = DeleteSelected.extend(/** @lends wp.media.view.DeleteSelectedPermanentlyButton.prototype */{
  866. initialize: function() {
  867. DeleteSelected.prototype.initialize.apply( this, arguments );
  868. this.controller.on( 'select:activate', this.selectActivate, this );
  869. this.controller.on( 'select:deactivate', this.selectDeactivate, this );
  870. },
  871. filterChange: function( model ) {
  872. this.canShow = ( 'trash' === model.get( 'status' ) );
  873. },
  874. selectActivate: function() {
  875. this.toggleDisabled();
  876. this.$el.toggleClass( 'hidden', ! this.canShow );
  877. },
  878. selectDeactivate: function() {
  879. this.toggleDisabled();
  880. this.$el.addClass( 'hidden' );
  881. },
  882. render: function() {
  883. Button.prototype.render.apply( this, arguments );
  884. this.selectActivate();
  885. return this;
  886. }
  887. });
  888. module.exports = DeleteSelectedPermanently;
  889. /***/ })
  890. /******/ ]);