fl-builder-history-manager.js 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. ( function( $ ) {
  2. /**
  3. * Manages undo/redo history for the builder.
  4. */
  5. FLBuilderHistoryManager = {
  6. /**
  7. * Array of change state labels.
  8. */
  9. states: [],
  10. /**
  11. * Array index for the current state.
  12. */
  13. position: 0,
  14. /**
  15. * Whether a state is currently rendering or not.
  16. */
  17. rendering: false,
  18. /**
  19. * Initializes hooks for saving state when changes
  20. * in the builder are made.
  21. */
  22. init: function() {
  23. var config = FLBuilderConfig.history
  24. var self = this
  25. this.states = config.states
  26. this.position = parseInt( config.position )
  27. this.setupMainMenuData()
  28. $.each( config.hooks, function( hook, label ) {
  29. FLBuilder.addHook( hook, function( e, data ) {
  30. self.saveCurrentState( label, data )
  31. } )
  32. } )
  33. FLBuilder.addHook( 'didPublishLayout', this.clearStatesOnPublish.bind( this ) )
  34. FLBuilder.addHook( 'restartEditingSession', this.saveCurrentStateOnRestartSession.bind( this ) )
  35. FLBuilder.addHook( 'historyItemClicked', this.itemClicked.bind( this ) )
  36. FLBuilder.addHook( 'undo', this.onUndo.bind( this ) )
  37. FLBuilder.addHook( 'redo', this.onRedo.bind( this ) )
  38. },
  39. /**
  40. * Makes a request to save the current layout state.
  41. */
  42. saveCurrentState: function( label, data ) {
  43. var self = this
  44. var data = 'undefined' === typeof data ? {} : data
  45. var moduleType = null
  46. if ( 'undefined' !== typeof data.moduleType && data.moduleType ) {
  47. moduleType = data.moduleType
  48. }
  49. const actions = FL.Builder.data.getLayoutActions()
  50. actions.saveHistoryState( label, moduleType )
  51. },
  52. /**
  53. * Makes a request to save the current state when restarting
  54. * the builder editing session if no states exist.
  55. */
  56. saveCurrentStateOnRestartSession: function( e ) {
  57. if ( this.states.length ) {
  58. return
  59. }
  60. this.saveCurrentState( 'draft_created' )
  61. },
  62. /**
  63. * Makes a request to clear all states for the current layout
  64. * when publishing and exiting the builder.
  65. */
  66. clearStatesOnPublish: function( e, data ) {
  67. var self = this
  68. this.states = []
  69. this.position = 0
  70. this.setupMainMenuData()
  71. const actions = FL.Builder.data.getLayoutActions()
  72. actions.clearHistoryStates( FLBuilderConfig.postId, data.shouldExit )
  73. },
  74. /**
  75. * Makes a request to render a layout state with
  76. * the specified position.
  77. */
  78. renderState: function( position ) {
  79. var self = this
  80. if ( this.rendering || ! this.states.length ) {
  81. return
  82. }
  83. if ( $( '.fl-builder-settings:visible', window.parent.document ).length ) {
  84. return
  85. }
  86. var timeout = setTimeout( FLBuilder.showAjaxLoader, 2000 )
  87. this.rendering = true
  88. const actions = FL.Builder.data.getLayoutActions()
  89. const callback = function( response ) {
  90. var data = JSON.parse( response )
  91. if ( ! data.error ) {
  92. self.position = parseInt( data.position )
  93. FLBuilder.triggerHook( 'didRestoreHistoryComplete', data )
  94. FLBuilder._renderLayout( data.layout )
  95. self.setupMainMenuData()
  96. }
  97. clearTimeout( timeout )
  98. self.rendering = false
  99. }
  100. actions.renderHistoryState( position, callback )
  101. },
  102. /**
  103. * Renders the previous state.
  104. */
  105. onUndo: function() {
  106. const actions = FL.Builder.data.getLayoutActions()
  107. actions.undo()
  108. },
  109. /**
  110. * Renders the next state.
  111. */
  112. onRedo: function() {
  113. const actions = FL.Builder.data.getLayoutActions()
  114. actions.redo()
  115. },
  116. /**
  117. * Adds history states to the main menu data.
  118. */
  119. setupMainMenuData: function() {
  120. var labels = FLBuilderConfig.history.labels
  121. var label = ''
  122. FLBuilderConfig.mainMenu.history.items = []
  123. for ( var i = this.states.length - 1; 0 <= i; i-- ) {
  124. if ( 'string' === typeof this.states[ i ] ) {
  125. label = labels[ this.states[ i ] ] ? labels[ this.states[ i ] ] : this.states[ i ]
  126. } else {
  127. label = labels[ this.states[ i ].label ] ? labels[ this.states[ i ].label ] : this.states[ i ].label
  128. if ( this.states[ i ].moduleType || -1 < this.states[ i ].label.indexOf( 'module' ) ) {
  129. label = label.replace( '%s', this.getModuleName( this.states[ i ].moduleType ) )
  130. }
  131. }
  132. FLBuilderConfig.mainMenu.history.items.push( {
  133. eventName: 'historyItemClicked',
  134. type: 'event',
  135. label: wp.template( 'fl-history-list-item' )( {
  136. label: label,
  137. current: i === this.position ? 1 : 0,
  138. position: i,
  139. } )
  140. } )
  141. }
  142. if ( ! FLBuilderConfig.history.enabled ) {
  143. FLBuilderConfig.mainMenu.history.items.push( {
  144. eventName: 'historyItemClicked',
  145. type: 'event',
  146. label: wp.template( 'fl-history-list-item' )( {
  147. label: FLBuilderConfig.history.labels.history_disabled,
  148. current: 0,
  149. position: 0,
  150. } )
  151. } )
  152. } else if ( this.states.length < 1 ) {
  153. FLBuilderConfig.mainMenu.history.items.push( {
  154. eventName: 'historyItemClicked',
  155. type: 'event',
  156. label: wp.template( 'fl-history-list-item' )( {
  157. label: 'No history found',
  158. current: 0,
  159. position: 0,
  160. } )
  161. } )
  162. }
  163. if ( undefined !== FLBuilder.MainMenu ) {
  164. FLBuilder.MainMenu.renderPanel( 'history' )
  165. }
  166. },
  167. /**
  168. * Returns a module's name by passing the type.
  169. */
  170. getModuleName: function( type ) {
  171. var modules = FLBuilderConfig.contentItems.module
  172. var i = 0
  173. if ( 'widget' === type ) {
  174. return FLBuilderStrings.widget
  175. }
  176. for ( ; i < modules.length; i++ ) {
  177. if ( 'undefined' === typeof modules[ i ].slug ) {
  178. continue
  179. }
  180. if ( type === modules[ i ].slug ) {
  181. return modules[ i ].name
  182. }
  183. }
  184. return FLBuilderStrings.module
  185. },
  186. /**
  187. * Callback for when a history item in the tools
  188. * menu is clicked to render that state.
  189. */
  190. itemClicked: function( e, item ) {
  191. var button = $( item ).find( '.fl-history-list-item' )
  192. var position = button.attr( 'data-position' )
  193. var current = $( '.fl-history-list-item[data-current=1]', window.parent.document )
  194. if ( $( '.fl-builder-settings:visible', window.parent.document ).length ) {
  195. FLBuilder._closeNestedSettings()
  196. FLBuilder._lightbox.close()
  197. }
  198. current.attr( 'data-current', 0 )
  199. button.attr( 'data-current', 1 )
  200. this.renderState( position )
  201. },
  202. }
  203. $( function() {
  204. FLBuilderHistoryManager.init()
  205. } )
  206. } ( jQuery ) );