wc-shipping-zone-methods.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395
  1. /* global shippingZoneMethodsLocalizeScript, ajaxurl */
  2. ( function( $, data, wp, ajaxurl ) {
  3. $( function() {
  4. var $table = $( '.wc-shipping-zone-methods' ),
  5. $tbody = $( '.wc-shipping-zone-method-rows' ),
  6. $save_button = $( '.wc-shipping-zone-method-save' ),
  7. $row_template = wp.template( 'wc-shipping-zone-method-row' ),
  8. $blank_template = wp.template( 'wc-shipping-zone-method-row-blank' ),
  9. // Backbone model
  10. ShippingMethod = Backbone.Model.extend({
  11. changes: {},
  12. logChanges: function( changedRows ) {
  13. var changes = this.changes || {};
  14. _.each( changedRows.methods, function( row, id ) {
  15. changes.methods = changes.methods || { methods : {} };
  16. changes.methods[ id ] = _.extend( changes.methods[ id ] || { instance_id : id }, row );
  17. } );
  18. if ( typeof changedRows.zone_name !== 'undefined' ) {
  19. changes.zone_name = changedRows.zone_name;
  20. }
  21. if ( typeof changedRows.zone_locations !== 'undefined' ) {
  22. changes.zone_locations = changedRows.zone_locations;
  23. }
  24. if ( typeof changedRows.zone_postcodes !== 'undefined' ) {
  25. changes.zone_postcodes = changedRows.zone_postcodes;
  26. }
  27. this.changes = changes;
  28. this.trigger( 'change:methods' );
  29. },
  30. save: function() {
  31. $.post( ajaxurl + ( ajaxurl.indexOf( '?' ) > 0 ? '&' : '?' ) + 'action=woocommerce_shipping_zone_methods_save_changes', {
  32. wc_shipping_zones_nonce : data.wc_shipping_zones_nonce,
  33. changes : this.changes,
  34. zone_id : data.zone_id
  35. }, this.onSaveResponse, 'json' );
  36. },
  37. onSaveResponse: function( response, textStatus ) {
  38. if ( 'success' === textStatus ) {
  39. if ( response.success ) {
  40. if ( response.data.zone_id !== data.zone_id ) {
  41. data.zone_id = response.data.zone_id;
  42. if ( window.history.pushState ) {
  43. window.history.pushState({}, '', 'admin.php?page=wc-settings&tab=shipping&zone_id=' + response.data.zone_id );
  44. }
  45. }
  46. shippingMethod.set( 'methods', response.data.methods );
  47. shippingMethod.trigger( 'change:methods' );
  48. shippingMethod.changes = {};
  49. shippingMethod.trigger( 'saved:methods' );
  50. } else {
  51. window.alert( data.strings.save_failed );
  52. }
  53. }
  54. }
  55. } ),
  56. // Backbone view
  57. ShippingMethodView = Backbone.View.extend({
  58. rowTemplate: $row_template,
  59. initialize: function() {
  60. this.listenTo( this.model, 'change:methods', this.setUnloadConfirmation );
  61. this.listenTo( this.model, 'saved:methods', this.clearUnloadConfirmation );
  62. this.listenTo( this.model, 'saved:methods', this.render );
  63. $tbody.on( 'change', { view: this }, this.updateModelOnChange );
  64. $tbody.on( 'sortupdate', { view: this }, this.updateModelOnSort );
  65. $( window ).on( 'beforeunload', { view: this }, this.unloadConfirmation );
  66. $save_button.on( 'click', { view: this }, this.onSubmit );
  67. $( document.body ).on( 'input change', '#zone_name, #zone_locations, #zone_postcodes', { view: this }, this.onUpdateZone );
  68. $( document.body ).on( 'click', '.wc-shipping-zone-method-settings', { view: this }, this.onConfigureShippingMethod );
  69. $( document.body ).on( 'click', '.wc-shipping-zone-add-method', { view: this }, this.onAddShippingMethod );
  70. $( document.body ).on( 'wc_backbone_modal_response', this.onConfigureShippingMethodSubmitted );
  71. $( document.body ).on( 'wc_backbone_modal_response', this.onAddShippingMethodSubmitted );
  72. $( document.body ).on( 'change', '.wc-shipping-zone-method-selector select', this.onChangeShippingMethodSelector );
  73. $( document.body ).on( 'click', '.wc-shipping-zone-postcodes-toggle', this.onTogglePostcodes );
  74. },
  75. onUpdateZone: function( event ) {
  76. var view = event.data.view,
  77. model = view.model,
  78. value = $( this ).val(),
  79. $target = $( event.target ),
  80. attribute = $target.data( 'attribute' ),
  81. changes = {};
  82. event.preventDefault();
  83. changes[ attribute ] = value;
  84. model.set( attribute, value );
  85. model.logChanges( changes );
  86. view.render();
  87. },
  88. block: function() {
  89. $( this.el ).block({
  90. message: null,
  91. overlayCSS: {
  92. background: '#fff',
  93. opacity: 0.6
  94. }
  95. });
  96. },
  97. unblock: function() {
  98. $( this.el ).unblock();
  99. },
  100. render: function() {
  101. var methods = _.indexBy( this.model.get( 'methods' ), 'instance_id' ),
  102. zone_name = this.model.get( 'zone_name' ),
  103. view = this;
  104. // Set name.
  105. $('.wc-shipping-zone-name').text( zone_name ? zone_name : data.strings.default_zone_name );
  106. // Blank out the contents.
  107. this.$el.empty();
  108. this.unblock();
  109. if ( _.size( methods ) ) {
  110. // Sort methods
  111. methods = _.sortBy( methods, function( method ) {
  112. return parseInt( method.method_order, 10 );
  113. } );
  114. // Populate $tbody with the current methods
  115. $.each( methods, function( id, rowData ) {
  116. if ( 'yes' === rowData.enabled ) {
  117. rowData.enabled_icon = '<span class="woocommerce-input-toggle woocommerce-input-toggle--enabled">' + data.strings.yes + '</span>';
  118. } else {
  119. rowData.enabled_icon = '<span class="woocommerce-input-toggle woocommerce-input-toggle--disabled">' + data.strings.no + '</span>';
  120. }
  121. view.$el.append( view.rowTemplate( rowData ) );
  122. var $tr = view.$el.find( 'tr[data-id="' + rowData.instance_id + '"]');
  123. if ( ! rowData.has_settings ) {
  124. $tr.find( '.wc-shipping-zone-method-title > a' ).replaceWith('<span>' + $tr.find( '.wc-shipping-zone-method-title > a' ).text() + '</span>' );
  125. var $del = $tr.find( '.wc-shipping-zone-method-delete' );
  126. $tr.find( '.wc-shipping-zone-method-title .row-actions' ).empty().html($del);
  127. }
  128. } );
  129. // Make the rows function
  130. this.$el.find( '.wc-shipping-zone-method-delete' ).on( 'click', { view: this }, this.onDeleteRow );
  131. this.$el.find( '.wc-shipping-zone-method-enabled a').on( 'click', { view: this }, this.onToggleEnabled );
  132. } else {
  133. view.$el.append( $blank_template );
  134. }
  135. this.initTooltips();
  136. },
  137. initTooltips: function() {
  138. $( '#tiptip_holder' ).removeAttr( 'style' );
  139. $( '#tiptip_arrow' ).removeAttr( 'style' );
  140. $( '.tips' ).tipTip({ 'attribute': 'data-tip', 'fadeIn': 50, 'fadeOut': 50, 'delay': 50 });
  141. },
  142. onSubmit: function( event ) {
  143. event.data.view.block();
  144. event.data.view.model.save();
  145. event.preventDefault();
  146. },
  147. onDeleteRow: function( event ) {
  148. var view = event.data.view,
  149. model = view.model,
  150. methods = _.indexBy( model.get( 'methods' ), 'instance_id' ),
  151. changes = {},
  152. instance_id = $( this ).closest('tr').data('id');
  153. event.preventDefault();
  154. delete methods[ instance_id ];
  155. changes.methods = changes.methods || { methods : {} };
  156. changes.methods[ instance_id ] = _.extend( changes.methods[ instance_id ] || {}, { deleted : 'deleted' } );
  157. model.set( 'methods', methods );
  158. model.logChanges( changes );
  159. view.render();
  160. },
  161. onToggleEnabled: function( event ) {
  162. var view = event.data.view,
  163. $target = $( event.target ),
  164. model = view.model,
  165. methods = _.indexBy( model.get( 'methods' ), 'instance_id' ),
  166. instance_id = $target.closest( 'tr' ).data( 'id' ),
  167. enabled = $target.closest( 'tr' ).data( 'enabled' ) === 'yes' ? 'no' : 'yes',
  168. changes = {};
  169. event.preventDefault();
  170. methods[ instance_id ].enabled = enabled;
  171. changes.methods = changes.methods || { methods : {} };
  172. changes.methods[ instance_id ] = _.extend( changes.methods[ instance_id ] || {}, { enabled : enabled } );
  173. model.set( 'methods', methods );
  174. model.logChanges( changes );
  175. view.render();
  176. },
  177. setUnloadConfirmation: function() {
  178. this.needsUnloadConfirm = true;
  179. $save_button.removeAttr( 'disabled' );
  180. },
  181. clearUnloadConfirmation: function() {
  182. this.needsUnloadConfirm = false;
  183. $save_button.attr( 'disabled', 'disabled' );
  184. },
  185. unloadConfirmation: function( event ) {
  186. if ( event.data.view.needsUnloadConfirm ) {
  187. event.returnValue = data.strings.unload_confirmation_msg;
  188. window.event.returnValue = data.strings.unload_confirmation_msg;
  189. return data.strings.unload_confirmation_msg;
  190. }
  191. },
  192. updateModelOnChange: function( event ) {
  193. var model = event.data.view.model,
  194. $target = $( event.target ),
  195. instance_id = $target.closest( 'tr' ).data( 'id' ),
  196. attribute = $target.data( 'attribute' ),
  197. value = $target.val(),
  198. methods = _.indexBy( model.get( 'methods' ), 'instance_id' ),
  199. changes = {};
  200. if ( methods[ instance_id ][ attribute ] !== value ) {
  201. changes.methods[ instance_id ] = {};
  202. changes.methods[ instance_id ][ attribute ] = value;
  203. methods[ instance_id ][ attribute ] = value;
  204. }
  205. model.logChanges( changes );
  206. },
  207. updateModelOnSort: function( event ) {
  208. var view = event.data.view,
  209. model = view.model,
  210. methods = _.indexBy( model.get( 'methods' ), 'instance_id' ),
  211. changes = {};
  212. _.each( methods, function( method ) {
  213. var old_position = parseInt( method.method_order, 10 );
  214. var new_position = parseInt( $table.find( 'tr[data-id="' + method.instance_id + '"]').index() + 1, 10 );
  215. if ( old_position !== new_position ) {
  216. methods[ method.instance_id ].method_order = new_position;
  217. changes.methods = changes.methods || { methods : {} };
  218. changes.methods[ method.instance_id ] = _.extend( changes.methods[ method.instance_id ] || {}, { method_order : new_position } );
  219. }
  220. } );
  221. if ( _.size( changes ) ) {
  222. model.logChanges( changes );
  223. }
  224. },
  225. onConfigureShippingMethod: function( event ) {
  226. var instance_id = $( this ).closest( 'tr' ).data( 'id' ),
  227. model = event.data.view.model,
  228. methods = _.indexBy( model.get( 'methods' ), 'instance_id' ),
  229. method = methods[ instance_id ];
  230. // Only load modal if supported
  231. if ( ! method.settings_html ) {
  232. return true;
  233. }
  234. event.preventDefault();
  235. $( this ).WCBackboneModal({
  236. template : 'wc-modal-shipping-method-settings',
  237. variable : {
  238. instance_id : instance_id,
  239. method : method
  240. },
  241. data : {
  242. instance_id : instance_id,
  243. method : method
  244. }
  245. });
  246. $( document.body ).trigger( 'init_tooltips' );
  247. },
  248. onConfigureShippingMethodSubmitted: function( event, target, posted_data ) {
  249. if ( 'wc-modal-shipping-method-settings' === target ) {
  250. shippingMethodView.block();
  251. // Save method settings via ajax call
  252. $.post( ajaxurl + ( ajaxurl.indexOf( '?' ) > 0 ? '&' : '?' ) + 'action=woocommerce_shipping_zone_methods_save_settings', {
  253. wc_shipping_zones_nonce : data.wc_shipping_zones_nonce,
  254. instance_id : posted_data.instance_id,
  255. data : posted_data
  256. }, function( response, textStatus ) {
  257. if ( 'success' === textStatus && response.success ) {
  258. $( 'table.wc-shipping-zone-methods' ).parent().find( '#woocommerce_errors' ).remove();
  259. // If there were errors, prepend the form.
  260. if ( response.data.errors.length > 0 ) {
  261. shippingMethodView.showErrors( response.data.errors );
  262. }
  263. // Method was saved. Re-render.
  264. if ( _.size( shippingMethodView.model.changes ) ) {
  265. shippingMethodView.model.save();
  266. } else {
  267. shippingMethodView.model.onSaveResponse( response, textStatus );
  268. }
  269. } else {
  270. window.alert( data.strings.save_failed );
  271. shippingMethodView.unblock();
  272. }
  273. }, 'json' );
  274. }
  275. },
  276. showErrors: function( errors ) {
  277. var error_html = '<div id="woocommerce_errors" class="error notice is-dismissible">';
  278. $( errors ).each( function( index, value ) {
  279. error_html = error_html + '<p>' + value + '</p>';
  280. } );
  281. error_html = error_html + '</div>';
  282. $( 'table.wc-shipping-zone-methods' ).before( error_html );
  283. },
  284. onAddShippingMethod: function( event ) {
  285. event.preventDefault();
  286. $( this ).WCBackboneModal({
  287. template : 'wc-modal-add-shipping-method',
  288. variable : {
  289. zone_id : data.zone_id
  290. }
  291. });
  292. $( '.wc-shipping-zone-method-selector select' ).change();
  293. },
  294. onAddShippingMethodSubmitted: function( event, target, posted_data ) {
  295. if ( 'wc-modal-add-shipping-method' === target ) {
  296. shippingMethodView.block();
  297. // Add method to zone via ajax call
  298. $.post( ajaxurl + ( ajaxurl.indexOf( '?' ) > 0 ? '&' : '?' ) + 'action=woocommerce_shipping_zone_add_method', {
  299. wc_shipping_zones_nonce : data.wc_shipping_zones_nonce,
  300. method_id : posted_data.add_method_id,
  301. zone_id : data.zone_id
  302. }, function( response, textStatus ) {
  303. if ( 'success' === textStatus && response.success ) {
  304. if ( response.data.zone_id !== data.zone_id ) {
  305. data.zone_id = response.data.zone_id;
  306. if ( window.history.pushState ) {
  307. window.history.pushState({}, '', 'admin.php?page=wc-settings&tab=shipping&zone_id=' + response.data.zone_id );
  308. }
  309. }
  310. // Trigger save if there are changes, or just re-render
  311. if ( _.size( shippingMethodView.model.changes ) ) {
  312. shippingMethodView.model.save();
  313. } else {
  314. shippingMethodView.model.set( 'methods', response.data.methods );
  315. shippingMethodView.model.trigger( 'change:methods' );
  316. shippingMethodView.model.changes = {};
  317. shippingMethodView.model.trigger( 'saved:methods' );
  318. }
  319. }
  320. shippingMethodView.unblock();
  321. }, 'json' );
  322. }
  323. },
  324. onChangeShippingMethodSelector: function() {
  325. var description = $( this ).find( 'option:selected' ).data( 'description' );
  326. $( this ).parent().find( '.wc-shipping-zone-method-description' ).remove();
  327. $( this ).after( '<div class="wc-shipping-zone-method-description">' + description + '</div>' );
  328. $( this ).closest( 'article' ).height( $( this ).parent().height() );
  329. },
  330. onTogglePostcodes: function( event ) {
  331. event.preventDefault();
  332. var $tr = $( this ).closest( 'tr');
  333. $tr.find( '.wc-shipping-zone-postcodes' ).show();
  334. $tr.find( '.wc-shipping-zone-postcodes-toggle' ).hide();
  335. }
  336. } ),
  337. shippingMethod = new ShippingMethod({
  338. methods: data.methods,
  339. zone_name: data.zone_name
  340. } ),
  341. shippingMethodView = new ShippingMethodView({
  342. model: shippingMethod,
  343. el: $tbody
  344. } );
  345. shippingMethodView.render();
  346. $tbody.sortable({
  347. items: 'tr',
  348. cursor: 'move',
  349. axis: 'y',
  350. handle: 'td.wc-shipping-zone-method-sort',
  351. scrollSensitivity: 40
  352. });
  353. });
  354. })( jQuery, shippingZoneMethodsLocalizeScript, wp, ajaxurl );