fl-lightbox.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736
  1. (function($){
  2. /**
  3. * Custom lightbox for builder popups.
  4. *
  5. * @class FLLightbox
  6. * @since 1.0
  7. */
  8. FLLightbox = function( settings )
  9. {
  10. this._init( settings );
  11. };
  12. /**
  13. * Closes the lightbox of a child element that
  14. * is passed to this method.
  15. *
  16. * @since 1.0
  17. * @static
  18. * @method closeParent
  19. * @param {Object} child An HTML element or jQuery reference to an element.
  20. */
  21. FLLightbox.closeParent = function( child )
  22. {
  23. var instanceId = $( child ).closest( '.fl-lightbox-wrap' ).attr( 'data-instance-id' );
  24. if ( ! _.isUndefined( instanceId ) ) {
  25. FLLightbox._instances[ instanceId ].close();
  26. }
  27. };
  28. /**
  29. * Returns the classname for the resize control in lightbox headers.
  30. *
  31. * @since 2.0
  32. * @static
  33. * @method getResizableControlClass
  34. * @return {String}
  35. */
  36. FLLightbox.getResizableControlClass = function()
  37. {
  38. var resizable = $( '.fl-lightbox-resizable' ).eq( 0 ),
  39. className = 'far fa-window-maximize';
  40. if ( resizable.length && resizable.hasClass( 'fl-lightbox-width-full' ) ) {
  41. className = 'far fa-window-minimize';
  42. }
  43. return className;
  44. };
  45. /**
  46. * Unbinds events for all lightbox instances.
  47. *
  48. * @since 2.0
  49. * @static
  50. * @method unbindAll
  51. */
  52. FLLightbox.unbindAll = function()
  53. {
  54. var id;
  55. for ( id in FLLightbox._instances ) {
  56. FLLightbox._instances[ id ]._unbind();
  57. }
  58. };
  59. /**
  60. * Binds events for all lightbox instances.
  61. *
  62. * @since 2.0
  63. * @static
  64. * @method bindAll
  65. */
  66. FLLightbox.bindAll = function()
  67. {
  68. var id;
  69. for ( id in FLLightbox._instances ) {
  70. FLLightbox._instances[ id ]._bind();
  71. }
  72. };
  73. /**
  74. * Close all lightbox instances.
  75. *
  76. * @since 2.0
  77. * @static
  78. * @method closeAll
  79. */
  80. FLLightbox.closeAll = function()
  81. {
  82. var id;
  83. for ( id in FLLightbox._instances ) {
  84. FLLightbox._instances[ id ].close();
  85. }
  86. };
  87. /**
  88. * An object that stores a reference to each
  89. * lightbox instance that is created.
  90. *
  91. * @since 1.0
  92. * @static
  93. * @access private
  94. * @property {Object} _instances
  95. */
  96. FLLightbox._instances = {};
  97. /**
  98. * Prototype for new instances.
  99. *
  100. * @since 1.0
  101. * @property {Object} prototype
  102. */
  103. FLLightbox.prototype = {
  104. /**
  105. * A unique ID for this instance that's used to store
  106. * it in the static _instances object.
  107. *
  108. * @since 1.0
  109. * @access private
  110. * @property {String} _id
  111. */
  112. _id: null,
  113. /**
  114. * A jQuery reference to the main wrapper div.
  115. *
  116. * @since 1.0
  117. * @access private
  118. * @property {Object} _node
  119. */
  120. _node: null,
  121. /**
  122. * Flag for whether the lightbox is visible or not.
  123. *
  124. * @since 1.0
  125. * @access private
  126. * @property {Boolean} _visible
  127. */
  128. _visible: false,
  129. /**
  130. * Whether closing the lightbox is allowed or not.
  131. *
  132. * @since 2.0
  133. * @access private
  134. * @property {Boolean} _allowClosing
  135. */
  136. _allowClosing: true,
  137. /**
  138. * A timeout used to throttle the resize event.
  139. *
  140. * @since 1.0
  141. * @access private
  142. * @property {Object} _resizeTimer
  143. */
  144. _resizeTimer: null,
  145. /**
  146. * Default config object.
  147. *
  148. * @since 1.0
  149. * @access private
  150. * @property {Object} _defaults
  151. * @property {String} _defaults.className - A custom classname to add to the wrapper div.
  152. * @property {Boolean} _defaults.destroyOnClose - Flag for whether the instance should be destroyed when closed.
  153. * @property {Boolean} _defaults.resizable - Flag for Whether this instance should be resizable or not.
  154. */
  155. _defaults: {
  156. className: '',
  157. destroyOnClose: false,
  158. resizable: false
  159. },
  160. /**
  161. * Opens the lightbox. You can pass new content to this method.
  162. * If no content is passed, the previous content will be shown.
  163. *
  164. * @since 1.0
  165. * @method open
  166. * @param {String} content HTML content to add to the lightbox.
  167. */
  168. open: function(content)
  169. {
  170. var lightbox = this._node.find( '.fl-lightbox' ),
  171. isPinned = ( lightbox.closest( '.fl-builder-ui-pinned' ).length ),
  172. settings = this._getPositionSettings();
  173. if ( ! isPinned && settings && this._defaults.resizable ) {
  174. lightbox.css( settings );
  175. }
  176. this._bind();
  177. this._node.show();
  178. this._visible = true;
  179. if(typeof content !== 'undefined') {
  180. this.setContent(content);
  181. }
  182. else {
  183. this._resize();
  184. }
  185. this.trigger('open');
  186. FLBuilder.triggerHook('didShowLightbox', this);
  187. },
  188. /**
  189. * Closes the lightbox.
  190. *
  191. * @since 1.0
  192. * @method close
  193. */
  194. close: function()
  195. {
  196. var parent = this._node.data('parent');
  197. if ( ! this._allowClosing ) {
  198. return;
  199. }
  200. this.trigger('beforeCloseLightbox');
  201. this._unbind();
  202. this._node.hide();
  203. this._visible = false;
  204. this.trigger('close');
  205. FLBuilder.triggerHook('didHideLightbox');
  206. if ( this._defaults.resizable && _.isUndefined( parent ) ) {
  207. FLBuilder.triggerHook('didHideAllLightboxes');
  208. }
  209. if(this._defaults.destroyOnClose) {
  210. this.destroy();
  211. }
  212. },
  213. /**
  214. * Disables closing the lightbox.
  215. *
  216. * @since 2.0
  217. * @method disableClose
  218. */
  219. disableClose: function()
  220. {
  221. this._allowClosing = false;
  222. },
  223. /**
  224. * Enables closing the lightbox.
  225. *
  226. * @since 2.0
  227. * @method enableClose
  228. */
  229. enableClose: function()
  230. {
  231. this._allowClosing = true;
  232. },
  233. /**
  234. * Adds HTML content to the lightbox replacing any
  235. * previously added content.
  236. *
  237. * @since 1.0
  238. * @method setContent
  239. * @param {String} content HTML content to add to the lightbox.
  240. */
  241. setContent: function(content)
  242. {
  243. this._node.find('.fl-lightbox-content').html(content);
  244. this._resize();
  245. },
  246. /**
  247. * Uses the jQuery empty function to remove lightbox
  248. * content and any related events.
  249. *
  250. * @since 1.0
  251. * @method empty
  252. */
  253. empty: function()
  254. {
  255. this._node.find('.fl-lightbox-content').empty();
  256. },
  257. /**
  258. * Bind an event to the lightbox.
  259. *
  260. * @since 1.0
  261. * @method on
  262. * @param {String} event The type of event to bind.
  263. * @param {Function} callback A callback to fire when the event is triggered.
  264. */
  265. on: function(event, callback)
  266. {
  267. this._node.on(event, callback);
  268. },
  269. /**
  270. * Unbind an event from the lightbox.
  271. *
  272. * @since 1.0
  273. * @method off
  274. * @param {String} event The type of event to unbind.
  275. * @param {Function} callback
  276. */
  277. off: function(event, callback)
  278. {
  279. this._node.off(event, callback);
  280. },
  281. /**
  282. * Trigger an event on the lightbox.
  283. *
  284. * @since 1.0
  285. * @method trigger
  286. * @param {String} event The type of event to trigger.
  287. * @param {Object} params Additional parameters to pass to the event.
  288. */
  289. trigger: function(event, params)
  290. {
  291. this._node.trigger(event, params);
  292. },
  293. /**
  294. * Destroy the lightbox by removing all elements, events
  295. * and object references.
  296. *
  297. * @since 1.0
  298. * @method destroy
  299. */
  300. destroy: function()
  301. {
  302. this._node.empty();
  303. this._node.remove();
  304. FLLightbox._instances[this._id] = 'undefined';
  305. try{ delete FLLightbox._instances[this._id]; } catch(e){}
  306. },
  307. /**
  308. * Initialize this lightbox instance.
  309. *
  310. * @since 1.0
  311. * @access private
  312. * @method _init
  313. * @param {Object} settings A setting object for this instance.
  314. */
  315. _init: function(settings)
  316. {
  317. var i = 0,
  318. prop = null;
  319. for(prop in FLLightbox._instances) {
  320. i++;
  321. }
  322. this._defaults = $.extend({}, this._defaults, settings);
  323. this._id = new Date().getTime() + i;
  324. FLLightbox._instances[this._id] = this;
  325. this._render();
  326. this._resizable();
  327. },
  328. /**
  329. * Renders the main wrapper.
  330. *
  331. * @since 1.0
  332. * @access private
  333. * @method _render
  334. */
  335. _render: function()
  336. {
  337. this._node = $( '<div class="fl-lightbox-wrap" data-instance-id="'+ this._id +'"><div class="fl-lightbox-mask"></div><div class="fl-lightbox"><div class="fl-lightbox-content-wrap"><div class="fl-lightbox-content"></div></div></div></div>' );
  338. this._node.addClass( this._defaults.className );
  339. $( 'body' ).append( this._node );
  340. },
  341. /**
  342. * Binds events for this instance.
  343. *
  344. * @since 1.0
  345. * @access private
  346. * @method _bind
  347. */
  348. _bind: function()
  349. {
  350. $( window ).on( 'resize.fl-lightbox-' + this._id, this._delayedResize.bind( this ) );
  351. },
  352. /**
  353. * Unbinds events for this instance.
  354. *
  355. * @since 1.0
  356. * @access private
  357. * @method _unbind
  358. */
  359. _unbind: function()
  360. {
  361. $( window ).off( 'resize.fl-lightbox-' + this._id );
  362. },
  363. /**
  364. * Enable resizing for the lightbox.
  365. *
  366. * @since 2.0
  367. * @method _resizable
  368. */
  369. _resizable: function()
  370. {
  371. var body = $( 'body' ),
  372. mask = this._node.find( '.fl-lightbox-mask' ),
  373. lightbox = this._node.find( '.fl-lightbox' ),
  374. resizable = $( '.fl-lightbox-resizable' ).eq( 0 );
  375. if ( this._defaults.resizable ) {
  376. mask.hide();
  377. lightbox.addClass( 'fl-lightbox-resizable' );
  378. lightbox.delegate( '.fl-lightbox-resize-toggle', 'click', this._resizeClicked.bind( this ) );
  379. lightbox.draggable( {
  380. cursor : 'move',
  381. handle : '.fl-lightbox-header',
  382. } ).resizable( {
  383. handles : 'all',
  384. minHeight : 500,
  385. minWidth : 380,
  386. start : this._resizeStart.bind( this ),
  387. stop : this._resizeStop.bind( this )
  388. } );
  389. if ( resizable.length && resizable.hasClass( 'fl-lightbox-width-full' ) ) { // Setup nested
  390. lightbox.addClass( 'fl-lightbox-width-full' );
  391. lightbox.draggable( 'disable' );
  392. } else { // Setup the main parent lightbox
  393. this._restorePosition();
  394. }
  395. }
  396. else {
  397. mask.show();
  398. }
  399. this._resize();
  400. },
  401. /**
  402. * Resizes the lightbox after a delay.
  403. *
  404. * @since 1.0
  405. * @access private
  406. * @method _delayedResize
  407. */
  408. _delayedResize: function()
  409. {
  410. clearTimeout( this._resizeTimer );
  411. this._resizeTimer = setTimeout( this._resize.bind( this ), 250 );
  412. },
  413. /**
  414. * Resizes the lightbox.
  415. *
  416. * @since 1.0
  417. * @access private
  418. * @method _resize
  419. */
  420. _resize: function()
  421. {
  422. var lightbox = this._node.find( '.fl-lightbox' ),
  423. boxTop = parseInt( this._node.css( 'padding-top' ) ),
  424. boxBottom = parseInt( this._node.css( 'padding-bottom' ) ),
  425. boxLeft = parseInt( this._node.css( 'padding-left' ) ),
  426. boxRight = parseInt( this._node.css( 'padding-right' ) ),
  427. boxHeight = lightbox.height(),
  428. boxWidth = lightbox.width(),
  429. win = $( window ),
  430. winHeight = win.height() - boxTop - boxBottom,
  431. winWidth = win.width() - boxLeft - boxRight,
  432. top = '0px';
  433. if ( ! this._defaults.resizable ) {
  434. if ( winHeight > boxHeight ) {
  435. top = ( ( winHeight - boxHeight - 46 ) / 2 ) + 'px';
  436. }
  437. lightbox.attr( 'style', '' ).css( 'margin', top + ' auto 0' );
  438. }
  439. else {
  440. if ( boxWidth < 600 ) {
  441. lightbox.addClass( 'fl-lightbox-width-slim' );
  442. } else {
  443. lightbox.removeClass( 'fl-lightbox-width-slim' );
  444. }
  445. if ( boxWidth < 450 ) {
  446. lightbox.addClass( 'fl-lightbox-width-micro' );
  447. } else {
  448. lightbox.removeClass( 'fl-lightbox-width-micro' );
  449. }
  450. this._resizeEditors();
  451. }
  452. this.trigger( 'resized' );
  453. },
  454. /**
  455. * Callback for when a user lightbox resize starts.
  456. *
  457. * @since 2.0
  458. * @access private
  459. * @method _resizeStart
  460. */
  461. _resizeStart: function()
  462. {
  463. $( 'body' ).addClass( 'fl-builder-resizable-is-resizing' );
  464. $( '.fl-builder-lightbox:visible' ).append( '<div class="fl-builder-resizable-iframe-fix"></div>' );
  465. FLBuilder._destroyOverlayEvents();
  466. FLBuilder._removeAllOverlays();
  467. },
  468. /**
  469. * Callback for when a user lightbox resize stops.
  470. *
  471. * @since 2.0
  472. * @access private
  473. * @method _resizeStop
  474. */
  475. _resizeStop: function( e, ui )
  476. {
  477. var lightbox = $( '.fl-lightbox-resizable:visible' );
  478. if ( parseInt( lightbox.css( 'top' ) ) < 0 ) {
  479. lightbox.css( 'top', '0' );
  480. }
  481. this._savePosition();
  482. $( 'body' ).removeClass( 'fl-builder-resizable-is-resizing' );
  483. $( '.fl-builder-resizable-iframe-fix' ).remove();
  484. FLBuilder._bindOverlayEvents();
  485. },
  486. /**
  487. * Resize to full or back to standard when the resize icon is clicked.
  488. *
  489. * @since 2.0
  490. * @access private
  491. * @method _expandLightbox
  492. */
  493. _resizeClicked: function()
  494. {
  495. var lightboxes = $( '.fl-lightbox-resizable' ),
  496. controls = lightboxes.find( '.fl-lightbox-resize-toggle' ),
  497. lightbox = this._node.find( '.fl-lightbox' );
  498. if ( lightbox.hasClass( 'fl-lightbox-width-full' ) ) {
  499. this._resizeExitFull();
  500. } else {
  501. this._resizeEnterFull();
  502. }
  503. this._resize();
  504. },
  505. /**
  506. * Resize to the full size lightbox.
  507. *
  508. * @since 2.0
  509. * @access private
  510. * @method _resizeEnterFull
  511. */
  512. _resizeEnterFull: function()
  513. {
  514. var lightboxes = $( '.fl-lightbox-resizable' ),
  515. controls = lightboxes.find( '.fl-lightbox-resize-toggle' ),
  516. lightbox = this._node.find( '.fl-lightbox' );
  517. controls.removeClass( 'fa-window-maximize' ).addClass( 'fa-window-minimize' );
  518. lightboxes.addClass( 'fl-lightbox-width-full' );
  519. lightboxes.draggable( 'disable' );
  520. lightboxes.resizable( 'disable' );
  521. },
  522. /**
  523. * Resize to the standard size lightbox.
  524. *
  525. * @since 2.0
  526. * @access private
  527. * @method _resizeEnterFull
  528. */
  529. _resizeExitFull: function()
  530. {
  531. var lightboxes = $( '.fl-lightbox-resizable' ),
  532. controls = lightboxes.find( '.fl-lightbox-resize-toggle' ),
  533. lightbox = this._node.find( '.fl-lightbox' );
  534. controls.removeClass( 'fa-window-minimize' ).addClass( 'fa-window-maximize' );
  535. lightboxes.removeClass( 'fl-lightbox-width-full' );
  536. lightboxes.draggable( 'enable' );
  537. lightboxes.resizable( 'enable' );
  538. },
  539. /**
  540. * Resizes text and code editor fields.
  541. *
  542. * @since 2.0
  543. * @method _resizeEditors
  544. */
  545. _resizeEditors: function()
  546. {
  547. $( '.fl-lightbox-resizable' ).each( function() {
  548. var lightbox = $( this ),
  549. fieldsHeight = lightbox.find( '.fl-builder-settings-fields' ).height(),
  550. editors = lightbox.find( '.mce-edit-area > iframe, textarea.wp-editor-area, .ace_editor' ),
  551. editor = null;
  552. if ( fieldsHeight < 350 ) {
  553. fieldsHeight = 350;
  554. }
  555. editors.each( function() {
  556. editor = $( this );
  557. if ( editor.hasClass( 'ace_editor' ) ) {
  558. editor.height( fieldsHeight - 60 );
  559. editor.closest( '.fl-field' ).data( 'editor' ).resize();
  560. } else if ( editor.closest( '.mce-container-body' ).find( '.mce-toolbar-grp .mce-toolbar.mce-last' ).is( ':visible' ) ) {
  561. editor.height( fieldsHeight - 175 );
  562. } else {
  563. editor.height( fieldsHeight - 150 );
  564. }
  565. } );
  566. } );
  567. },
  568. /**
  569. * Save the lightbox position for the current user.
  570. *
  571. * @since 2.0
  572. * @access private
  573. * @method _savePosition
  574. */
  575. _savePosition: function()
  576. {
  577. var lightbox = this._node.find( '.fl-lightbox' ),
  578. data = {
  579. width : lightbox.width(),
  580. height : lightbox.height(),
  581. top : parseInt( lightbox.css( 'top' ) ) < 0 ? '0px' : lightbox.css( 'top' ),
  582. left : lightbox.css( 'left' )
  583. };
  584. if ( lightbox.closest( '.fl-builder-ui-pinned' ).length ) {
  585. return;
  586. }
  587. FLBuilderConfig.userSettings.lightbox = data;
  588. FLBuilder.ajax( {
  589. action : 'save_lightbox_position',
  590. data : data
  591. } );
  592. },
  593. /**
  594. * Restores the lightbox position for the current user.
  595. *
  596. * @since 2.0
  597. * @access private
  598. * @method _restorePosition
  599. */
  600. _restorePosition: function()
  601. {
  602. var lightbox = this._node.find( '.fl-lightbox' ),
  603. settings = this._getPositionSettings();
  604. if ( settings ) {
  605. lightbox.css( settings );
  606. } else {
  607. lightbox.css( {
  608. top : 25,
  609. left : FLBuilderConfig.isRtl ? '-' + 25 : 25
  610. } );
  611. }
  612. },
  613. /**
  614. * Get the user settings for the lightbox position.
  615. *
  616. * Resize the height to 500px if the lightbox height is
  617. * taller than the window and the window is taller than
  618. * 546px (500px for lightbox min-height and 46px for the
  619. * builder bar height).
  620. *
  621. * @since 2.0
  622. * @access private
  623. * @method _getPositionSettings
  624. * @return {Object|Boolean}
  625. */
  626. _getPositionSettings: function()
  627. {
  628. var settings = FLBuilderConfig.userSettings.lightbox;
  629. if ( ! settings ) {
  630. return false;
  631. }
  632. var winHeight = $( window ).height(),
  633. height = parseInt( settings.height );
  634. if ( height > winHeight && winHeight > 546 ) {
  635. settings.height = winHeight - 50;
  636. settings.top = 0;
  637. }
  638. return settings;
  639. },
  640. };
  641. })(jQuery);