PageRenderTime 64ms CodeModel.GetById 32ms RepoModel.GetById 0ms app.codeStats 0ms

/wp-content/plugins/woocommerce/assets/js/admin/settings-views-html-settings-tax.js

https://gitlab.com/hunt9310/ras
JavaScript | 413 lines | 341 code | 57 blank | 15 comment | 37 complexity | 1f8983b1d12b1485f554d00396fa6563 MD5 | raw file
  1. /* global htmlSettingsTaxLocalizeScript, ajaxurl */
  2. /**
  3. * Used by woocommerce/includes/admin/settings/views/html-settings-tax.php
  4. */
  5. ( function( $, data, wp, ajaxurl ) {
  6. $( function() {
  7. if ( ! String.prototype.trim ) {
  8. String.prototype.trim = function () {
  9. return this.replace( /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '' );
  10. };
  11. }
  12. var rowTemplate = wp.template( 'wc-tax-table-row' ),
  13. rowTemplateEmpty = wp.template( 'wc-tax-table-row-empty' ),
  14. paginationTemplate = wp.template( 'wc-tax-table-pagination' ),
  15. $table = $( '.wc_tax_rates' ),
  16. $tbody = $( '#rates' ),
  17. $save_button = $( 'input[name="save"]' ),
  18. $pagination = $( '#rates-pagination' ),
  19. $search_field = $( '#rates-search .wc-tax-rates-search-field' ),
  20. $submit = $( '.submit .button-primary[type=submit]' ),
  21. WCTaxTableModelConstructor = Backbone.Model.extend({
  22. changes: {},
  23. setRateAttribute: function( rateID, attribute, value ) {
  24. var rates = _.indexBy( this.get( 'rates' ), 'tax_rate_id' ),
  25. changes = {};
  26. if ( rates[ rateID ][ attribute ] !== value ) {
  27. changes[ rateID ] = {};
  28. changes[ rateID ][ attribute ] = value;
  29. rates[ rateID ][ attribute ] = value;
  30. }
  31. this.logChanges( changes );
  32. },
  33. logChanges: function( changedRows ) {
  34. var changes = this.changes || {};
  35. _.each( changedRows, function( row, id ) {
  36. changes[ id ] = _.extend( changes[ id ] || {
  37. tax_rate_id : id
  38. }, row );
  39. } );
  40. this.changes = changes;
  41. this.trigger( 'change:rates' );
  42. },
  43. getFilteredRates: function() {
  44. var rates = this.get( 'rates' ),
  45. search = $search_field.val().toLowerCase();
  46. if ( search.length ) {
  47. rates = _.filter( rates, function( rate ) {
  48. var search_text = _.toArray( rate ).join( ' ' ).toLowerCase();
  49. return ( -1 !== search_text.indexOf( search ) );
  50. } );
  51. }
  52. rates = _.sortBy( rates, function( rate ) {
  53. return parseInt( rate.tax_rate_order, 10 );
  54. } );
  55. return rates;
  56. },
  57. block: function() {
  58. $( '.wc_tax_rates' ).block({
  59. message: null,
  60. overlayCSS: {
  61. background: '#fff',
  62. opacity: 0.6
  63. }
  64. });
  65. },
  66. unblock: function() {
  67. $( '.wc_tax_rates' ).unblock();
  68. },
  69. save: function() {
  70. var self = this;
  71. self.block();
  72. Backbone.ajax({
  73. method: 'POST',
  74. dataType: 'json',
  75. url: ajaxurl + ( ajaxurl.indexOf( '?' ) > 0 ? '&' : '?' ) + 'action=woocommerce_tax_rates_save_changes',
  76. data: {
  77. current_class: data.current_class,
  78. wc_tax_nonce: data.wc_tax_nonce,
  79. changes: self.changes
  80. },
  81. success: function( response, textStatus ) {
  82. if ( 'success' === textStatus ) {
  83. WCTaxTableModelInstance.set( 'rates', response.data.rates );
  84. WCTaxTableModelInstance.trigger( 'change:rates' );
  85. WCTaxTableModelInstance.changes = {};
  86. WCTaxTableModelInstance.trigger( 'saved:rates' );
  87. // Reload view.
  88. WCTaxTableInstance.render();
  89. }
  90. self.unblock();
  91. }
  92. });
  93. }
  94. } ),
  95. WCTaxTableViewConstructor = Backbone.View.extend({
  96. rowTemplate: rowTemplate,
  97. per_page: data.limit,
  98. page: data.page,
  99. initialize: function() {
  100. var qty_pages = Math.ceil( _.toArray( this.model.get( 'rates' ) ).length / this.per_page );
  101. this.qty_pages = 0 === qty_pages ? 1 : qty_pages;
  102. this.page = this.sanitizePage( data.page );
  103. this.listenTo( this.model, 'change:rates', this.setUnloadConfirmation );
  104. this.listenTo( this.model, 'saved:rates', this.clearUnloadConfirmation );
  105. $tbody.on( 'change autocompletechange', ':input', { view: this }, this.updateModelOnChange );
  106. $tbody.on( 'sortupdate', { view: this }, this.updateModelOnSort );
  107. $search_field.on( 'keyup search', { view: this }, this.onSearchField );
  108. $pagination.on( 'click', 'a', { view: this }, this.onPageChange );
  109. $pagination.on( 'change', 'input', { view: this }, this.onPageChange );
  110. $( window ).on( 'beforeunload', { view: this }, this.unloadConfirmation );
  111. $submit.on( 'click', { view: this }, this.onSubmit );
  112. $save_button.attr( 'disabled','disabled' );
  113. // Can bind these directly to the buttons, as they won't get overwritten.
  114. $table.find( '.insert' ).on( 'click', { view: this }, this.onAddNewRow );
  115. $table.find( '.remove_tax_rates' ).on( 'click', { view: this }, this.onDeleteRow );
  116. $table.find( '.export' ).on( 'click', { view: this }, this.onExport );
  117. },
  118. render: function() {
  119. var rates = this.model.getFilteredRates(),
  120. qty_rates = _.size( rates ),
  121. qty_pages = Math.ceil( qty_rates / this.per_page ),
  122. first_index = 0 === qty_rates ? 0 : this.per_page * ( this.page - 1 ),
  123. last_index = this.per_page * this.page,
  124. paged_rates = _.toArray( rates ).slice( first_index, last_index ),
  125. view = this;
  126. // Blank out the contents.
  127. this.$el.empty();
  128. if ( paged_rates.length ) {
  129. // Populate $tbody with the current page of results.
  130. $.each( paged_rates, function( id, rowData ) {
  131. view.$el.append( view.rowTemplate( rowData ) );
  132. } );
  133. } else {
  134. view.$el.append( rowTemplateEmpty() );
  135. }
  136. // Initialize autocomplete for countries.
  137. this.$el.find( 'td.country input' ).autocomplete({
  138. source: data.countries,
  139. minLength: 2
  140. });
  141. // Initialize autocomplete for states.
  142. this.$el.find( 'td.state input' ).autocomplete({
  143. source: data.states,
  144. minLength: 3
  145. });
  146. // Postcode and city don't have `name` values by default. They're only created if the contents changes, to save on database queries (I think)
  147. this.$el.find( 'td.postcode input, td.city input' ).change( function() {
  148. $( this ).attr( 'name', $( this ).data( 'name' ) );
  149. });
  150. if ( qty_pages > 1 ) {
  151. // We've now displayed our initial page, time to render the pagination box.
  152. $pagination.html( paginationTemplate( {
  153. qty_rates: qty_rates,
  154. current_page: this.page,
  155. qty_pages: qty_pages
  156. } ) );
  157. } else {
  158. $pagination.empty();
  159. view.page = 1;
  160. }
  161. // Disable sorting if there is a search term filtering the items.
  162. if ( $search_field.val() ) {
  163. $tbody.sortable( 'disable' );
  164. } else {
  165. $tbody.sortable( 'enable' );
  166. }
  167. },
  168. updateUrl: function() {
  169. if ( ! window.history.replaceState ) {
  170. return;
  171. }
  172. var url = data.base_url,
  173. search = $search_field.val();
  174. if ( 1 < this.page ) {
  175. url += '&p=' + encodeURIComponent( this.page );
  176. }
  177. if ( search.length ) {
  178. url += '&s=' + encodeURIComponent( search );
  179. }
  180. window.history.replaceState( {}, '', url );
  181. },
  182. onSubmit: function( event ) {
  183. event.data.view.model.save();
  184. event.preventDefault();
  185. },
  186. onAddNewRow: function( event ) {
  187. var view = event.data.view,
  188. model = view.model,
  189. rates = _.indexBy( model.get( 'rates' ), 'tax_rate_id' ),
  190. changes = {},
  191. size = _.size( rates ),
  192. newRow = _.extend( {}, data.default_rate, {
  193. tax_rate_id: 'new-' + size + '-' + Date.now(),
  194. newRow: true
  195. } ),
  196. $current, current_id, current_order, rates_to_reorder, reordered_rates;
  197. $current = $tbody.children( '.current' );
  198. if ( $current.length ) {
  199. current_id = $current.last().data( 'id' );
  200. current_order = parseInt( rates[ current_id ].tax_rate_order, 10 );
  201. newRow.tax_rate_order = 1 + current_order;
  202. rates_to_reorder = _.filter( rates, function( rate ) {
  203. if ( parseInt( rate.tax_rate_order, 10 ) > current_order ) {
  204. return true;
  205. }
  206. return false;
  207. } );
  208. reordered_rates = _.map( rates_to_reorder, function( rate ) {
  209. rate.tax_rate_order++;
  210. changes[ rate.tax_rate_id ] = _.extend( changes[ rate.tax_rate_id ] || {}, { tax_rate_order : rate.tax_rate_order } );
  211. return rate;
  212. } );
  213. } else {
  214. newRow.tax_rate_order = 1 + _.max(
  215. _.pluck( rates, 'tax_rate_order' ),
  216. function ( val ) {
  217. // Cast them all to integers, because strings compare funky. Sighhh.
  218. return parseInt( val, 10 );
  219. }
  220. );
  221. // Move the last page
  222. view.page = view.qty_pages;
  223. }
  224. rates[ newRow.tax_rate_id ] = newRow;
  225. changes[ newRow.tax_rate_id ] = newRow;
  226. model.set( 'rates', rates );
  227. model.logChanges( changes );
  228. view.render();
  229. },
  230. onDeleteRow: function( event ) {
  231. var view = event.data.view,
  232. model = view.model,
  233. rates = _.indexBy( model.get( 'rates' ), 'tax_rate_id' ),
  234. changes = {},
  235. $current, current_id;
  236. event.preventDefault();
  237. if ( $current = $tbody.children( '.current' ) ) {
  238. $current.each(function(){
  239. current_id = $( this ).data('id');
  240. delete rates[ current_id ];
  241. changes[ current_id ] = _.extend( changes[ current_id ] || {}, { deleted : 'deleted' } );
  242. });
  243. model.set( 'rates', rates );
  244. model.logChanges( changes );
  245. view.render();
  246. } else {
  247. window.alert( data.strings.no_rows_selected );
  248. }
  249. },
  250. onSearchField: function( event ){
  251. event.data.view.updateUrl();
  252. event.data.view.render();
  253. },
  254. onPageChange: function( event ) {
  255. var $target = $( event.currentTarget );
  256. event.preventDefault();
  257. event.data.view.page = $target.data( 'goto' ) ? $target.data( 'goto' ) : $target.val();
  258. event.data.view.render();
  259. event.data.view.updateUrl();
  260. },
  261. onExport: function( event ) {
  262. var csv_data = 'data:application/csv;charset=utf-8,' + data.strings.csv_data_cols.join(',') + '\n';
  263. $.each( event.data.view.model.getFilteredRates(), function( id, rowData ) {
  264. var row = '';
  265. row += rowData.tax_rate_country + ',';
  266. row += rowData.tax_rate_state + ',';
  267. row += ( rowData.postcode ? rowData.postcode.join( '; ' ) : '' ) + ',';
  268. row += ( rowData.city ? rowData.city.join( '; ' ) : '' ) + ',';
  269. row += rowData.tax_rate + ',';
  270. row += rowData.tax_rate_name + ',';
  271. row += rowData.tax_rate_priority + ',';
  272. row += rowData.tax_rate_compound + ',';
  273. row += rowData.tax_rate_shipping + ',';
  274. row += data.current_class;
  275. csv_data += row + '\n';
  276. });
  277. $( this ).attr( 'href', encodeURI( csv_data ) );
  278. return true;
  279. },
  280. setUnloadConfirmation: function() {
  281. this.needsUnloadConfirm = true;
  282. $save_button.removeAttr( 'disabled' );
  283. },
  284. clearUnloadConfirmation: function() {
  285. this.needsUnloadConfirm = false;
  286. $save_button.attr( 'disabled', 'disabled' );
  287. },
  288. unloadConfirmation: function( event ) {
  289. if ( event.data.view.needsUnloadConfirm ) {
  290. event.returnValue = data.strings.unload_confirmation_msg;
  291. window.event.returnValue = data.strings.unload_confirmation_msg;
  292. return data.strings.unload_confirmation_msg;
  293. }
  294. },
  295. updateModelOnChange: function( event ) {
  296. var model = event.data.view.model,
  297. $target = $( event.target ),
  298. id = $target.closest( 'tr' ).data( 'id' ),
  299. attribute = $target.data( 'attribute' ),
  300. val = $target.val();
  301. if ( 'city' === attribute || 'postcode' === attribute ) {
  302. val = val.split( ';' );
  303. val = $.map( val, function( thing ) {
  304. return thing.trim();
  305. });
  306. }
  307. if ( 'tax_rate_compound' === attribute || 'tax_rate_shipping' === attribute ) {
  308. if ( $target.is( ':checked' ) ) {
  309. val = 1;
  310. } else {
  311. val = 0;
  312. }
  313. }
  314. model.setRateAttribute( id, attribute, val );
  315. },
  316. updateModelOnSort: function( event ) {
  317. var view = event.data.view,
  318. model = view.model,
  319. rates = _.indexBy( model.get( 'rates' ), 'tax_rate_id' ),
  320. changes = {};
  321. _.each( rates, function( rate ) {
  322. var new_position = 0;
  323. var old_position = parseInt( rate.tax_rate_order, 10 );
  324. if ( $table.find( 'tr[data-id="' + rate.tax_rate_id + '"]').length ) {
  325. new_position = parseInt( $table.find( 'tr[data-id="' + rate.tax_rate_id + '"]').index(), 10 ) + parseInt( ( view.page - 1 ) * view.per_page, 10 );
  326. } else {
  327. new_position = old_position;
  328. }
  329. if ( old_position !== new_position ) {
  330. changes[ rate.tax_rate_id ] = _.extend( changes[ rate.tax_rate_id ] || {}, { tax_rate_order : new_position } );
  331. }
  332. } );
  333. if ( _.size( changes ) ) {
  334. model.logChanges( changes );
  335. }
  336. },
  337. sanitizePage: function( page_num ) {
  338. page_num = parseInt( page_num, 10 );
  339. if ( page_num < 1 ) {
  340. page_num = 1;
  341. } else if ( page_num > this.qty_pages ) {
  342. page_num = this.qty_pages;
  343. }
  344. return page_num;
  345. }
  346. } ),
  347. WCTaxTableModelInstance = new WCTaxTableModelConstructor({
  348. rates: data.rates
  349. } ),
  350. WCTaxTableInstance = new WCTaxTableViewConstructor({
  351. model: WCTaxTableModelInstance,
  352. el: '#rates'
  353. } );
  354. WCTaxTableInstance.render();
  355. });
  356. })( jQuery, htmlSettingsTaxLocalizeScript, wp, ajaxurl );