Aucune description

media-grid.js 29KB

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