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