PluginProbe ʕ •ᴥ•ʔ
WooCommerce / 10.9.2
WooCommerce v10.9.2
11.0.0-beta.1 10.9.4 10.9.3 10.9.2 10.9.1 10.9.0 10.9.0-rc.1 10.9.0-beta.2 10.9.0-beta.1 10.8.1 10.8.0 10.8.0-rc.1 10.8.0-beta.2 10.8.0-beta.1 7.8.0-beta.1 7.8.0-beta.2 7.8.0-rc.1 7.8.0-rc.2 7.8.1 7.8.2 7.8.3 7.8.4 7.9.0 7.9.0-beta.1 7.9.0-beta.2 7.9.0-rc.2 7.9.0-rc.3 7.9.1 7.9.2 8.0.0 8.0.0-beta.1 8.0.0-beta.2 8.0.0-rc.1 8.0.0-rc.2 8.0.1 8.0.2 8.0.3 8.0.4 8.0.5 8.1.0 8.1.0-beta.1 8.1.0-rc.1 8.1.0-rc.2 8.1.1 8.1.2 8.1.3 8.1.4 8.2.0 8.2.0-beta.1 8.2.0-rc.1 8.2.0-rc.2 8.2.1 8.2.2 8.2.3 8.2.4 8.2.5 8.3.0 8.3.0-beta.1 8.3.0-rc.1 8.3.0-rc.2 8.3.1 8.3.2 8.3.3 8.3.4 8.4.0 8.4.0-beta.1 8.4.0-rc.1 8.4.1 8.4.2 8.4.3 8.5.0 8.5.0-beta.1 8.5.0-rc.1 8.5.1 8.5.2 8.5.3 8.5.4 8.5.5 8.6.0 8.6.0-beta.1 8.6.0-rc.1 8.6.1 8.6.2 8.6.3 8.6.4 8.7.0 8.7.0-beta.1 8.7.0-beta.2 8.7.0-rc.1 8.7.1 8.7.2 8.7.3 8.8.0 8.8.0-beta.1 8.8.0-rc.1 8.8.1 8.8.2 8.8.3 8.8.4 8.8.5 8.8.6 8.8.7 8.9.0 8.9.0-beta.1 8.9.0-rc.1 8.9.1 8.9.2 8.9.3 8.9.4 8.9.5 9.0.0 9.0.0-beta.1 9.0.0-beta.2 9.0.0-rc.1 9.0.1 9.0.2 9.0.3 9.0.4 9.1.0 9.1.0-beta.1 9.1.0-rc.1 9.1.1 9.1.2 9.1.3 9.1.4 9.1.5 9.1.6 9.2.0 9.2.0-beta.1 9.2.0-rc.1 9.2.1 9.2.2 9.2.3 9.2.4 9.2.5 9.3.0 9.3.0-beta.1 9.3.0-rc.1 9.3.1 9.3.2 9.3.3 9.3.4 9.3.5 9.3.6 9.4.0 9.4.0-beta.1 9.4.0-beta.2 9.4.0-rc.1 9.4.0-rc.2 9.4.0-rc.3 9.4.0-rc.4 9.4.1 9.4.2 9.4.3 9.4.4 9.4.5 9.5.0 9.5.0-beta.1 9.5.0-beta.2 9.5.0-rc.1 9.5.1 9.5.2 9.5.3 9.5.4 9.6.0 9.6.0-beta.1 9.6.0-beta.2 9.6.0-rc.1 9.6.1 9.6.2 9.6.3 9.6.4 9.7.0 9.7.0-beta.1 9.7.0-rc.1 9.7.1 9.7.2 9.7.3 9.8.0 9.8.0-beta.1 9.8.0-rc.1 9.8.1 9.8.2 9.8.3 9.8.4 9.8.5 9.8.6 9.8.7 9.9.0 9.9.0-beta.1 9.9.0-rc.1 9.9.1 9.9.2 9.9.3 9.9.4 9.9.5 9.9.6 9.9.7 3.7.3 7.1.2 3.8.0 7.2.0 3.8.0-beta.1 7.2.0-beta.1 3.8.0-rc.1 7.2.0-beta.2 3.8.0-rc.2 7.2.0-rc.1 3.8.1 7.2.0-rc.2 3.8.2 7.2.1 3.8.3 7.2.2 3.9.0 7.2.3 3.9.0-beta.1 7.2.4 3.9.0-beta.2 7.3.0 3.9.0-rc.1 7.3.0-beta.1 3.9.0-rc.2 7.3.0-beta.2 3.9.0-rc.3 7.3.0-rc.1 3.9.0-rc.4 7.3.0-rc.2 3.9.1 7.3.1 3.9.2 7.4.0 3.9.3 7.4.0-beta.1 3.9.4 7.4.0-beta.2 3.9.5 7.4.0-rc.1 4.0.0 7.4.0-rc.2 4.0.0-beta.1 7.4.1 4.0.0-rc.1 7.4.2 4.0.0-rc.2 7.5.0 4.0.1 7.5.0-beta.1 4.0.2 7.5.0-beta.2 4.0.3 7.5.0-rc.1 4.0.4 7.5.1 4.1.0 7.5.2 4.1.0-beta.1 7.6.0 4.1.0-beta.2 7.6.0-beta.1 4.1.0-rc.1 7.6.0-beta.2 4.1.0-rc.2 7.6.0-rc.1 4.1.1 7.6.0-rc.2 4.1.2 7.6.0-rc.3 4.1.3 7.6.1 4.1.4 7.6.2 4.2.0 7.7.0 4.2.0-RC.1 7.7.0-beta.1 4.2.0-RC.2 7.7.0-beta.2 4.2.0-beta.1 7.7.0-rc.1 4.2.1 7.7.1 4.2.2 7.7.2 4.2.3 7.7.3 4.2.4 7.8.0 4.2.5 4.3.0 4.3.0-beta.1 4.3.0-rc.1 4.3.0-rc.2 4.3.0-rc.3 4.3.1 4.3.2 4.3.3 4.3.4 4.3.5 4.3.6 4.4.0 4.4.0-beta.1 4.4.0-rc.1 4.4.1 4.4.2 4.4.3 4.4.4 4.5.0 4.5.0-beta.1 4.5.0-rc.1 4.5.0-rc.3 4.5.1 4.5.2 4.5.3 4.5.4 4.5.5 4.6.0 4.6.0-beta.1 4.6.0-rc.1 4.6.1 4.6.2 4.6.3 4.6.4 4.6.5 4.7.0 4.7.0-beta.1 4.7.0-beta.2 4.7.0-rc.1 4.7.1 4.7.1-beta.1 4.7.2 4.7.3 4.7.4 4.8.0 4.8.0-beta.1 4.8.0-rc.1 4.8.0-rc.2 4.8.1 4.8.2 4.8.3 4.9.0 4.9.0-beta.1 4.9.0-rc.1 4.9.0-rc.2 4.9.1 4.9.2 4.9.3 4.9.4 4.9.5 5.0.0 5.0.0-beta.1 5.0.0-beta.2 5.0.0-rc.1 5.0.0-rc.2 5.0.0-rc.3 5.0.1 5.0.2 5.0.3 5.1.0 5.1.0-beta.1 5.1.0-rc.1 trunk 5.1.1 10.0.0 5.1.2 10.0.0-rc.1 5.1.3 10.0.0-rc.2 5.2.0 10.0.1 5.2.0-beta.1 10.0.2 5.2.0-rc.1 10.0.3 5.2.0-rc.2 10.0.4 5.2.1 10.0.5 5.2.2 10.0.6 5.2.3 10.1.0 5.2.4 10.1.0-rc.1 5.2.5 10.1.0-rc.2 5.3.0 10.1.0-rc.3 5.3.0-beta.1 10.1.0-rc.4 5.3.0-rc.1 10.1.1 5.3.0-rc.2 10.1.2 5.3.1 10.1.3 5.3.2 10.1.4 5.3.3 10.2.0 5.4.0 10.2.0-beta.1 5.4.0-beta.1 10.2.0-beta.2 5.4.0-rc.1 10.2.0-rc.1 5.4.1 10.2.1 5.4.2 10.2.2 5.4.3 10.2.3 5.4.4 10.2.4 5.4.5 10.3.0 5.5.0 10.3.0-beta.1 5.5.0-beta.1 10.3.0-beta.2 5.5.0-rc.1 10.3.0-rc.1 5.5.0-rc.2 10.3.0-rc.2 5.5.1 10.3.1 5.5.2 10.3.2 5.5.3 10.3.3 5.5.4 10.3.4 5.5.5 10.3.5 5.6.0 10.3.6 5.6.0-beta.1 10.3.7 5.6.0-rc.1 10.3.8 5.6.0-rc.2 10.4.0 5.6.1 10.4.0-beta.1 5.6.2 10.4.0-beta.2 5.6.3 10.4.0-rc.1 5.7.0 10.4.1 5.7.0-beta.1 10.4.2 5.7.0-rc.1 10.4.3 5.7.1 10.4.4 5.7.2 10.5.0 5.7.3 10.5.0-beta.1 5.8.0 10.5.0-beta.2 5.8.0-beta.1 10.5.0-rc.1 5.8.0-beta.2 10.5.0-rc.2 5.8.0-rc.1 10.5.0-rc.3 5.8.1 10.5.1 5.8.2 10.5.2 5.9.0 10.5.3 5.9.0-beta.1 10.6.0 5.9.0-rc.1 10.6.0-beta.1 5.9.0-rc.2 10.6.0-beta.2 5.9.1 10.6.0-rc.1 5.9.2 10.6.1 6.0.0 10.6.2 6.0.0-beta.1 10.7.0 6.0.0-rc.1 10.7.0-beta.1 6.0.1 10.7.0-beta.2 6.0.2 10.7.0-rc.1 6.1.0 3.0.0 6.1.0-beta.1 3.0.1 6.1.0-rc.1 3.0.2 6.1.0-rc.2 3.0.3 6.1.1 3.0.4 6.1.2 3.0.5 6.1.3 3.0.6 6.2.0 3.0.7 6.2.0-beta.1 3.0.8 6.2.0-rc.1 3.0.9 6.2.0-rc.2 3.1.0 6.2.1 3.1.1 6.2.2 3.1.2 6.2.3 3.2.0 6.3.0 3.2.1 6.3.0-beta.1 3.2.2 6.3.0-rc.1 3.2.3 6.3.0-rc.2 3.2.4 6.3.1 3.2.5 6.3.2 3.2.6 6.4.0 3.3.0 6.4.0-beta.1 3.3.1 6.4.0-rc.1 3.3.2 6.4.1 3.3.2-rc.1 6.4.2 3.3.3 6.5.0 3.3.4 6.5.0-beta.1 3.3.5 6.5.0-rc.1 3.3.6 6.5.0-rc.2 3.4.0 6.5.1 3.4.0-beta.1 6.5.2 3.4.0-rc.2 6.6.0 3.4.1 6.6.0-beta.1 3.4.2 6.6.0-rc.1 3.4.3 6.6.0-rc.2 3.4.4 6.6.1 3.4.5 6.6.2 3.4.6 6.7.0 3.4.7 6.7.0-beta.1 3.4.8 6.7.0-beta.2 3.5.0 6.7.0-rc.1 3.5.0-beta.1 6.7.1 3.5.0-rc.1 6.8.0 3.5.0-rc.2 6.8.0-beta.1 3.5.1 6.8.0-beta.2 3.5.10 6.8.0-rc.1 3.5.2 6.8.1 3.5.3 6.8.2 3.5.4 6.8.3 3.5.5 6.9.0 3.5.6 6.9.0-beta.1 3.5.7 6.9.0-beta.2 3.5.8 6.9.0-rc.1 3.5.9 6.9.1 3.6.0 6.9.2 3.6.0-beta.1 6.9.3 3.6.0-rc.1 6.9.4 3.6.0-rc.2 6.9.5 3.6.0-rc.3 7.0.0 3.6.1 7.0.0-beta.1 3.6.2 7.0.0-beta.2 3.6.3 7.0.0-beta.3 3.6.4 7.0.0-rc.1 3.6.5 7.0.0-rc.2 3.6.6 7.0.1 3.6.7 7.0.2 3.7.0 7.1.0 3.7.0-beta.1 7.1.0-beta.1 3.7.0-rc.1 7.1.0-beta.2 3.7.0-rc.2 7.1.0-rc.1 3.7.1 7.1.0-rc.2 3.7.2 7.1.1
woocommerce / assets / js / admin / meta-boxes-product.js
woocommerce / assets / js / admin Last commit date
api-keys.js 5 years ago api-keys.min.js 3 years ago backbone-modal.js 2 years ago backbone-modal.min.js 2 years ago marketplace-suggestions.js 10 months ago marketplace-suggestions.min.js 10 months ago meta-boxes-coupon.js 1 year ago meta-boxes-coupon.min.js 1 year ago meta-boxes-order.js 1 month ago meta-boxes-order.min.js 1 month ago meta-boxes-product-variation.js 1 year ago meta-boxes-product-variation.min.js 1 year ago meta-boxes-product.js 1 month ago meta-boxes-product.min.js 1 month ago meta-boxes.js 4 months ago meta-boxes.min.js 4 months ago network-orders.js 8 years ago network-orders.min.js 3 years ago order-attribution-admin.js 2 years ago order-attribution-admin.min.js 2 years ago product-editor.js 11 months ago product-editor.min.js 11 months ago product-ordering.js 3 months ago product-ordering.min.js 3 months ago quick-edit.js 1 year ago quick-edit.min.js 1 year ago reports.js 1 year ago reports.min.js 1 year ago settings-views-html-settings-tax.js 1 year ago settings-views-html-settings-tax.min.js 1 year ago settings.js 1 year ago settings.min.js 1 year ago system-status.js 3 months ago system-status.min.js 3 months ago term-ordering.js 3 months ago term-ordering.min.js 3 months ago users.js 5 years ago users.min.js 3 years ago variation-gallery.js 1 month ago variation-gallery.min.js 1 month ago wc-brands-enhanced-select.js 1 year ago wc-brands-enhanced-select.min.js 1 year ago wc-clipboard.js 5 years ago wc-clipboard.min.js 5 years ago wc-customer-stock-notifications.js 10 months ago wc-customer-stock-notifications.min.js 10 months 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 3 years ago wc-product-export.js 1 year ago wc-product-export.min.js 1 year ago wc-product-import.js 3 years ago wc-product-import.min.js 3 years ago wc-recent-reviews-widget-async.js 4 months ago wc-recent-reviews-widget-async.min.js 4 months ago wc-setup.js 5 years ago wc-setup.min.js 3 years ago wc-shipping-classes.js 1 year ago wc-shipping-classes.min.js 1 year ago wc-shipping-providers.js 3 months ago wc-shipping-providers.min.js 3 months ago wc-shipping-zone-methods.js 6 months ago wc-shipping-zone-methods.min.js 6 months ago wc-shipping-zones.js 1 year ago wc-shipping-zones.min.js 1 year ago wc-status-widget-async.js 4 months ago wc-status-widget-async.min.js 4 months ago wc-status-widget.js 1 year ago wc-status-widget.min.js 1 year ago woocommerce_admin.js 1 month ago woocommerce_admin.min.js 1 month ago
meta-boxes-product.js
1484 lines
1 /*global woocommerce_admin_meta_boxes, _ */
2 jQuery( function ( $ ) {
3 let isPageUnloading = false;
4
5 $( window ).on( 'beforeunload', function () {
6 isPageUnloading = true;
7 } );
8
9 // Scroll to first checked category
10 // https://github.com/scribu/wp-category-checklist-tree/blob/d1c3c1f449e1144542efa17dde84a9f52ade1739/category-checklist-tree.php
11 $( function () {
12 $( '[id$="-all"] > ul.categorychecklist' ).each( function () {
13 var $list = $( this );
14 var $firstChecked = $list.find( ':checked' ).first();
15
16 if ( ! $firstChecked.length ) {
17 return;
18 }
19
20 var pos_first = $list.find( 'input' ).position().top;
21 var pos_checked = $firstChecked.position().top;
22
23 $list
24 .closest( '.tabs-panel' )
25 .scrollTop( pos_checked - pos_first + 5 );
26 } );
27 } );
28
29 // Prevent enter submitting post form.
30 $( '#upsell_product_data' ).on( 'keypress', function ( e ) {
31 if ( e.keyCode === 13 ) {
32 return false;
33 }
34 } );
35
36 // Type box.
37 if ( $( 'body' ).hasClass( 'wc-wp-version-gte-55' ) ) {
38 $( '.type_box' ).appendTo( '#woocommerce-product-data .hndle' );
39 } else {
40 $( '.type_box' ).appendTo( '#woocommerce-product-data .hndle span' );
41 }
42
43 $( function () {
44 var woocommerce_product_data = $( '#woocommerce-product-data' );
45
46 // Prevent inputs in meta box headings opening/closing contents.
47 woocommerce_product_data.find( '.hndle' ).off( 'click.postboxes' );
48
49 woocommerce_product_data.on( 'click', '.hndle', function ( event ) {
50 // If the user clicks on some form input inside the h3 the box should not be toggled.
51 if (
52 $( event.target ).filter( 'input, option, label, select' )
53 .length
54 ) {
55 return;
56 }
57
58 if ( woocommerce_product_data.hasClass( 'closed' ) ) {
59 woocommerce_product_data.removeClass( 'closed' );
60 } else {
61 woocommerce_product_data.addClass( 'closed' );
62 }
63 } );
64 } );
65
66 // Catalog Visibility.
67 $( '#catalog-visibility' )
68 .find( '.edit-catalog-visibility' )
69 .on( 'click', function () {
70 if ( $( '#catalog-visibility-select' ).is( ':hidden' ) ) {
71 $( '#catalog-visibility-select' ).slideDown( 'fast' );
72 $( this ).hide();
73 }
74 return false;
75 } );
76 $( '#catalog-visibility' )
77 .find( '.save-post-visibility' )
78 .on( 'click', function () {
79 $( '#catalog-visibility-select' ).slideUp( 'fast' );
80 $( '#catalog-visibility' )
81 .find( '.edit-catalog-visibility' )
82 .show();
83
84 var label = $( 'input[name=_visibility]:checked' ).attr(
85 'data-label'
86 );
87
88 if ( $( 'input[name=_featured]' ).is( ':checked' ) ) {
89 label =
90 label + ', ' + woocommerce_admin_meta_boxes.featured_label;
91 $( 'input[name=_featured]' ).attr( 'checked', 'checked' );
92 }
93
94 $( '#catalog-visibility-display' ).text( label );
95 return false;
96 } );
97 $( '#catalog-visibility' )
98 .find( '.cancel-post-visibility' )
99 .on( 'click', function () {
100 $( '#catalog-visibility-select' ).slideUp( 'fast' );
101 $( '#catalog-visibility' )
102 .find( '.edit-catalog-visibility' )
103 .show();
104
105 var current_visibility = $( '#current_visibility' ).val();
106 var current_featured = $( '#current_featured' ).val();
107
108 $( 'input[name=_visibility]' ).prop( 'checked', false );
109 $(
110 'input[name=_visibility][value=' + current_visibility + ']'
111 ).attr( 'checked', 'checked' );
112
113 var label = $( 'input[name=_visibility]:checked' ).attr(
114 'data-label'
115 );
116
117 if ( 'yes' === current_featured ) {
118 label =
119 label + ', ' + woocommerce_admin_meta_boxes.featured_label;
120 $( 'input[name=_featured]' ).attr( 'checked', 'checked' );
121 } else {
122 $( 'input[name=_featured]' ).prop( 'checked', false );
123 }
124
125 $( '#catalog-visibility-display' ).text( label );
126 return false;
127 } );
128
129 // Product type specific options.
130 $( 'select#product-type' )
131 .on( 'change', function () {
132 // Get value.
133 var select_val = $( this ).val();
134
135 if ( 'variable' === select_val ) {
136 $( 'input#_manage_stock' ).trigger( 'change' );
137 $( 'input#_downloadable' ).prop( 'checked', false );
138 $( 'input#_virtual' ).prop( 'checked', false );
139 } else if ( 'grouped' === select_val ) {
140 $( 'input#_downloadable' ).prop( 'checked', false );
141 $( 'input#_virtual' ).prop( 'checked', false );
142 } else if ( 'external' === select_val ) {
143 $( 'input#_downloadable' ).prop( 'checked', false );
144 $( 'input#_virtual' ).prop( 'checked', false );
145 }
146
147 const cogs_field_tip = $( '._cogs_value_field' ).find(
148 '.woocommerce-help-tip'
149 );
150 const cogs_field_tip_text =
151 'variable' === select_val
152 ? woocommerce_admin_meta_boxes.cogs_value_tooltip_variable_products
153 : woocommerce_admin_meta_boxes.cogs_value_tooltip_simple_products;
154 $( cogs_field_tip ).attr( 'aria-label', cogs_field_tip_text );
155 $( cogs_field_tip ).tipTip( {
156 attribute: 'aria-label',
157 fadeIn: 50,
158 fadeOut: 50,
159 delay: 200,
160 keepAlive: true,
161 } );
162
163 show_and_hide_panels();
164 change_product_type_tip( get_product_tip_content( select_val ) );
165
166 $( 'ul.wc-tabs li:visible' ).eq( 0 ).find( 'a' ).trigger( 'click' );
167
168 $( document.body ).trigger(
169 'woocommerce-product-type-change',
170 select_val,
171 $( this )
172 );
173 } )
174 .trigger( 'change' );
175
176 $( 'input#_downloadable' ).on( 'change', function () {
177 show_and_hide_panels();
178 } );
179
180 $( 'input#_virtual' ).on( 'change', function () {
181 show_and_hide_panels();
182
183 // If user enables virtual while on shipping tab, switch to general tab.
184 if (
185 $( this ).is( ':checked' ) &&
186 $( '.shipping_options.shipping_tab' ).hasClass( 'active' )
187 ) {
188 $( '.general_options.general_tab > a' ).trigger( 'click' );
189 }
190 } );
191
192 function change_product_type_tip( content ) {
193 $( '#tiptip_holder' ).removeAttr( 'style' );
194 $( '#tiptip_arrow' ).removeAttr( 'style' );
195 $( '.woocommerce-product-type-tip' )
196 .attr( 'tabindex', '0' )
197 .attr( 'aria-label', $( '<div />' ).html( content ).text() ) // Remove HTML tags.
198 .tipTip( {
199 attribute: 'data-tip',
200 content: content,
201 fadeIn: 50,
202 fadeOut: 50,
203 delay: 200,
204 keepAlive: true,
205 } );
206 }
207
208 function get_product_tip_content( product_type ) {
209 switch ( product_type ) {
210 case 'simple':
211 return woocommerce_admin_meta_boxes.i18n_product_simple_tip;
212 case 'grouped':
213 return woocommerce_admin_meta_boxes.i18n_product_grouped_tip;
214 case 'external':
215 return woocommerce_admin_meta_boxes.i18n_product_external_tip;
216 case 'variable':
217 return woocommerce_admin_meta_boxes.i18n_product_variable_tip;
218 default:
219 return woocommerce_admin_meta_boxes.i18n_product_other_tip;
220 }
221 }
222
223 function show_and_hide_controls( context ) {
224 var product_type = $( 'select#product-type' ).val();
225 var is_virtual = $( 'input#_virtual:checked' ).length;
226 var is_downloadable = $( 'input#_downloadable:checked' ).length;
227
228 // Hide/Show all with rules.
229 var hide_classes = '.hide_if_downloadable, .hide_if_virtual';
230 var show_classes = '.show_if_downloadable, .show_if_virtual';
231
232 $.each(
233 woocommerce_admin_meta_boxes.product_types,
234 function ( index, value ) {
235 hide_classes = hide_classes + ', .hide_if_' + value;
236 show_classes = show_classes + ', .show_if_' + value;
237 }
238 );
239
240 $( hide_classes, context ).show();
241 $( show_classes, context ).hide();
242
243 // Shows rules.
244 if ( is_downloadable ) {
245 $( '.show_if_downloadable', context ).show();
246 }
247 if ( is_virtual ) {
248 $( '.show_if_virtual', context ).show();
249 }
250
251 $( '.show_if_' + product_type, context ).show();
252
253 // Hide rules.
254 if ( is_downloadable ) {
255 $( '.hide_if_downloadable', context ).hide();
256 }
257 if ( is_virtual ) {
258 $( '.hide_if_virtual', context ).hide();
259 }
260
261 $( '.hide_if_' + product_type, context ).hide();
262
263 // POS visibility - requires combination of type AND downloadable status.
264 var is_pos_supported =
265 ( product_type === 'simple' || product_type === 'variable' ) &&
266 ! is_downloadable;
267 if ( is_pos_supported ) {
268 $( '#pos_visibility_supported', context ).show();
269 $( '#pos_visibility_unsupported', context ).hide();
270 } else {
271 $( '#pos_visibility_supported', context ).hide();
272 $( '#pos_visibility_unsupported', context ).show();
273 }
274 }
275
276 function show_and_hide_panels() {
277 show_and_hide_controls();
278
279 $( 'input#_manage_stock' ).trigger( 'change' );
280
281 // Hide empty panels/tabs after display.
282 $( '.woocommerce_options_panel' ).each( function () {
283 var $children = $( this ).children( '.options_group' );
284
285 if ( 0 === $children.length ) {
286 return;
287 }
288
289 var $invisible = $children.filter( function () {
290 return 'none' === $( this ).css( 'display' );
291 } );
292
293 // Hide panel.
294 if ( $invisible.length === $children.length ) {
295 var $id = $( this ).prop( 'id' );
296 $( '.product_data_tabs' )
297 .find( 'li a[href="#' + $id + '"]' )
298 .parent()
299 .hide();
300 }
301 } );
302 }
303
304 // Sale price schedule.
305 $( '.sale_price_dates_fields' ).each( function () {
306 var $these_sale_dates = $( this );
307 var sale_schedule_set = false;
308 var $wrap = $these_sale_dates.closest( 'div, table' );
309
310 $these_sale_dates.find( 'input' ).each( function () {
311 if ( '' !== $( this ).val() ) {
312 sale_schedule_set = true;
313 }
314 } );
315
316 if ( sale_schedule_set ) {
317 $wrap.find( '.sale_schedule' ).hide();
318 $wrap.find( '.sale_price_dates_fields' ).show();
319 } else {
320 $wrap.find( '.sale_schedule' ).show();
321 $wrap.find( '.sale_price_dates_fields' ).hide();
322 }
323 } );
324
325 $( '#woocommerce-product-data' ).on(
326 'click',
327 '.sale_schedule',
328 function () {
329 var $wrap = $( this ).closest( 'div, table' );
330
331 $( this ).hide();
332 $wrap.find( '.cancel_sale_schedule' ).show();
333 $wrap.find( '.sale_price_dates_fields' ).show();
334
335 return false;
336 }
337 );
338 $( '#woocommerce-product-data' ).on(
339 'click',
340 '.cancel_sale_schedule',
341 function () {
342 var $wrap = $( this ).closest( 'div, table' );
343
344 $( this ).hide();
345 $wrap.find( '.sale_schedule' ).show();
346 $wrap.find( '.sale_price_dates_fields' ).hide();
347 $wrap.find( '.sale_price_dates_fields' ).find( 'input' ).val( '' );
348
349 return false;
350 }
351 );
352
353 // File inputs.
354 $( '#woocommerce-product-data' ).on(
355 'click',
356 '.downloadable_files a.insert',
357 function () {
358 $( this )
359 .closest( '.downloadable_files' )
360 .find( 'tbody' )
361 .append( $( this ).data( 'row' ) );
362 return false;
363 }
364 );
365 $( '#woocommerce-product-data' ).on(
366 'click',
367 '.downloadable_files a.delete',
368 function () {
369 $( this ).closest( 'tr' ).remove();
370 return false;
371 }
372 );
373
374 // Stock options.
375 function show_or_hide_stock_management_fields(
376 isStockManagementEnabled,
377 productType
378 ) {
379 const $stockManagementFields = $( '.stock_fields' );
380 const $stockStatusField = $( '.stock_status_field' );
381
382 $stockManagementFields.toggle( isStockManagementEnabled );
383 $stockStatusField.toggle(
384 ! isStockManagementEnabled &&
385 // do not show stock status field if it should be hidden for the product type
386 ! $stockStatusField.is( '.hide_if_' + productType )
387 );
388 }
389
390 $( 'input#_manage_stock' )
391 .on( 'change', function () {
392 const isStockManagementEnabled = $( this ).is( ':checked' );
393 const productType = $( 'select#product-type' ).val();
394
395 show_or_hide_stock_management_fields(
396 isStockManagementEnabled,
397 productType
398 );
399
400 $( 'input.variable_manage_stock' ).trigger( 'change' );
401 } )
402 .trigger( 'change' );
403
404 // Date picker fields.
405 function date_picker_select( datepicker ) {
406 var option = $( datepicker ).next().is( '.hasDatepicker' )
407 ? 'minDate'
408 : 'maxDate',
409 otherDateField =
410 'minDate' === option
411 ? $( datepicker ).next()
412 : $( datepicker ).prev(),
413 date = $( datepicker ).datepicker( 'getDate' );
414
415 $( otherDateField ).datepicker( 'option', option, date );
416 $( datepicker ).trigger( 'change' );
417 }
418
419 $( '.sale_price_dates_fields' ).each( function () {
420 $( this )
421 .find( 'input' )
422 .datepicker( {
423 defaultDate: '',
424 dateFormat: 'yy-mm-dd',
425 numberOfMonths: 1,
426 showButtonPanel: true,
427 onSelect: function () {
428 date_picker_select( $( this ) );
429 },
430 } );
431 $( this )
432 .find( 'input' )
433 .each( function () {
434 date_picker_select( $( this ) );
435 } );
436 } );
437
438 // Set up attributes, if current page has the attributes list.
439 const $product_attributes = $( '.product_attributes' );
440 if ( $product_attributes.length === 1 ) {
441 // When the attributes tab is shown, add an empty attribute to be filled out by the user.
442 $( '#product_attributes' ).on( 'woocommerce_tab_shown', function () {
443 remove_blank_custom_attribute_if_no_other_attributes();
444
445 const woocommerce_attribute_items = $product_attributes
446 .find( '.woocommerce_attribute' )
447 .get();
448
449 // If the product has no attributes, add an empty attribute to be filled out by the user.
450 if ( woocommerce_attribute_items.length === 0 ) {
451 add_custom_attribute_to_list();
452 }
453 } );
454
455 const woocommerce_attribute_items = $product_attributes
456 .find( '.woocommerce_attribute' )
457 .get();
458
459 // Sort the attributes by their position.
460 woocommerce_attribute_items.sort( function ( a, b ) {
461 var compA = parseInt( $( a ).attr( 'rel' ), 10 );
462 var compB = parseInt( $( b ).attr( 'rel' ), 10 );
463 return compA < compB ? -1 : compA > compB ? 1 : 0;
464 } );
465
466 $( woocommerce_attribute_items ).each( function ( index, el ) {
467 $product_attributes.append( el );
468 } );
469 }
470
471 function update_attribute_row_indexes() {
472 $( '.product_attributes .woocommerce_attribute' ).each( function (
473 index,
474 el
475 ) {
476 $( '.attribute_position', el ).val(
477 parseInt(
478 $( el ).index(
479 '.product_attributes .woocommerce_attribute'
480 ),
481 10
482 )
483 );
484 } );
485 }
486
487 var selectedAttributes = [];
488 var currentAttributeTermCreationContext = null;
489 $( '.product_attributes .woocommerce_attribute' ).each( function (
490 index,
491 el
492 ) {
493 if (
494 $( el ).css( 'display' ) !== 'none' &&
495 $( el ).is( '.taxonomy' )
496 ) {
497 selectedAttributes.push( $( el ).data( 'taxonomy' ) );
498 $( 'select.attribute_taxonomy' )
499 .find( 'option[value="' + $( el ).data( 'taxonomy' ) + '"]' )
500 .attr( 'disabled', 'disabled' );
501 }
502
503 if (
504 'undefined' === $( el ).attr( 'data-taxonomy' ) ||
505 false === $( el ).attr( 'data-taxonomy' ) ||
506 '' === $( el ).attr( 'data-taxonomy' )
507 ) {
508 add_placeholder_to_attribute_values_field( $( el ) );
509
510 $(
511 '.woocommerce_attribute input.woocommerce_attribute_used_for_variations'
512 ).on( 'change', function () {
513 add_placeholder_to_attribute_values_field( $( el ) );
514 } );
515 }
516 } );
517 $( 'select.wc-attribute-search' ).data(
518 'disabled-items',
519 selectedAttributes
520 );
521
522 function get_new_attribute_list_item_html(
523 indexInList,
524 globalAttributeId
525 ) {
526 return new Promise( function ( resolve, reject ) {
527 $.post( {
528 url: woocommerce_admin_meta_boxes.ajax_url,
529 data: {
530 action: 'woocommerce_add_attribute',
531 product_type: $( '#product-type' ).val(),
532 taxonomy: globalAttributeId ? globalAttributeId : '',
533 i: indexInList,
534 security: woocommerce_admin_meta_boxes.add_attribute_nonce,
535 },
536 success: function ( newAttributeListItemHtml ) {
537 resolve( newAttributeListItemHtml );
538 },
539 error: function ( jqXHR, textStatus, errorThrown ) {
540 reject( { jqXHR, textStatus, errorThrown } );
541 },
542 } );
543 } );
544 }
545
546 function block_attributes_tab_container() {
547 const $attributesTabContainer = $( '#product_attributes' );
548
549 $attributesTabContainer.block( {
550 message: null,
551 overlayCSS: {
552 background: '#fff',
553 opacity: 0.6,
554 },
555 } );
556 }
557
558 function unblock_attributes_tab_container() {
559 const $attributesTabContainer = $( '#product_attributes' );
560 $attributesTabContainer.unblock();
561 }
562
563 function toggle_expansion_of_attribute_list_item( $attributeListItem ) {
564 $attributeListItem.find( 'h3' ).trigger( 'click' );
565 }
566
567 function add_placeholder_to_attribute_values_field( $attributeListItem ) {
568 var $used_for_variations_checkbox = $attributeListItem.find(
569 'input.woocommerce_attribute_used_for_variations'
570 );
571
572 if (
573 $used_for_variations_checkbox.length &&
574 $used_for_variations_checkbox.is( ':checked' )
575 ) {
576 $attributeListItem
577 .find( 'textarea' )
578 .attr(
579 'placeholder',
580 woocommerce_admin_meta_boxes.i18n_attributes_used_for_variations_placeholder
581 );
582 } else {
583 $attributeListItem
584 .find( 'textarea' )
585 .attr(
586 'placeholder',
587 woocommerce_admin_meta_boxes.i18n_attributes_default_placeholder
588 );
589 }
590 }
591
592 function init_select_controls() {
593 $( document.body ).trigger( 'wc-enhanced-select-init' );
594 }
595
596 async function add_attribute_to_list( globalAttributeId ) {
597 try {
598 block_attributes_tab_container();
599
600 const numberOfAttributesInList = $(
601 '.product_attributes .woocommerce_attribute'
602 ).length;
603 const newAttributeListItemHtml =
604 await get_new_attribute_list_item_html(
605 numberOfAttributesInList,
606 globalAttributeId
607 );
608
609 const $attributesListContainer = $(
610 '#product_attributes .product_attributes'
611 );
612
613 const $attributeListItem = $( newAttributeListItemHtml ).appendTo(
614 $attributesListContainer
615 );
616
617 show_and_hide_controls( $attributeListItem );
618
619 init_select_controls(); // make sure any new select controls in the new list item are initialized
620
621 update_attribute_row_indexes();
622
623 toggle_expansion_of_attribute_list_item( $attributeListItem );
624
625 // Conditionally change the placeholder of product-level Attributes depending on the value of the "Use for variations" checkbox.
626 if ( 'undefined' === typeof globalAttributeId ) {
627 add_placeholder_to_attribute_values_field( $attributeListItem );
628
629 $(
630 '.woocommerce_attribute input.woocommerce_attribute_used_for_variations'
631 ).on( 'change', function () {
632 add_placeholder_to_attribute_values_field(
633 $( this ).closest( '.woocommerce_attribute' )
634 );
635 } );
636 }
637
638 $( document.body ).trigger( 'woocommerce_added_attribute' );
639
640 jQuery.maybe_disable_save_button();
641 } catch ( error ) {
642 if ( isPageUnloading ) {
643 // If the page is unloading, the outstanding ajax fetch may fail in Firefox (and possible other browsers, too).
644 // We don't want to show an error message in this case, because it was caused by the user leaving the page.
645 return;
646 }
647
648 alert(
649 woocommerce_admin_meta_boxes.i18n_add_attribute_error_notice
650 );
651 throw error;
652 } finally {
653 unblock_attributes_tab_container();
654 }
655 }
656
657 function add_global_attribute_to_list( globalAttributeId ) {
658 add_attribute_to_list( globalAttributeId );
659 }
660
661 function add_custom_attribute_to_list() {
662 add_attribute_to_list();
663 }
664
665 function add_if_not_exists( arr, item ) {
666 return arr.includes( item ) ? attr : [ ...arr, item ];
667 }
668
669 function disable_in_attribute_search( selectedAttributes ) {
670 $( 'select.wc-attribute-search' ).data(
671 'disabled-items',
672 selectedAttributes
673 );
674 }
675
676 function remove_blank_custom_attribute_if_no_other_attributes() {
677 const $attributes = $( '.product_attributes .woocommerce_attribute' );
678
679 if ( $attributes.length === 1 ) {
680 const $attribute = $attributes.first();
681
682 const $attributeName = $attribute.find(
683 'input[name="attribute_names[0]"]'
684 );
685 const $attributeValue = $attribute.find(
686 'input[name="attribute_values[0]"]'
687 );
688
689 if ( ! $attributeName.val() && ! $attributeValue.val() ) {
690 $attribute.remove();
691 }
692 }
693 }
694
695 // Handle the Attributes onboarding dismissible notice.
696 // If users dismiss the notice, never show it again.
697 if ( localStorage.getItem( 'attributes-notice-dismissed' ) ) {
698 $( '#product_attributes .notice' ).hide();
699 }
700
701 $( '#product_attributes .notice.woocommerce-message button' ).on(
702 'click',
703 function ( e ) {
704 $( '#product_attributes .notice' ).hide();
705 localStorage.setItem( 'attributes-notice-dismissed', 'true' );
706 }
707 );
708
709 $( 'select.wc-attribute-search' ).on( 'select2:select', function ( e ) {
710 const attributeId = e && e.params && e.params.data && e.params.data.id;
711
712 if ( attributeId ) {
713 remove_blank_custom_attribute_if_no_other_attributes();
714
715 add_global_attribute_to_list( attributeId );
716
717 selectedAttributes = add_if_not_exists(
718 selectedAttributes,
719 attributeId
720 );
721 disable_in_attribute_search( selectedAttributes );
722 }
723
724 $( this ).val( null );
725 $( this ).trigger( 'change' );
726
727 return false;
728 } );
729
730 // Add rows.
731
732 $( 'button.add_custom_attribute' ).on( 'click', function () {
733 add_custom_attribute_to_list();
734
735 return false;
736 } );
737
738 $( '.product_attributes' ).on( 'blur', 'input.attribute_name', function () {
739 var $inputElement = $( this );
740 var text = $inputElement.val();
741 var $attribute = $inputElement
742 .closest( '.woocommerce_attribute' )
743 .find( 'strong.attribute_name' );
744 if ( text === '' ) {
745 $attribute
746 .addClass( 'placeholder' )
747 .text(
748 woocommerce_admin_meta_boxes.i18n_attribute_name_placeholder
749 );
750 } else {
751 $attribute.removeClass( 'placeholder' ).text( text );
752 }
753 } );
754
755 $( '.product_attributes' ).on(
756 'click',
757 'button.select_all_attributes',
758 function () {
759 $( '.product_attributes' ).block( {
760 message: null,
761 overlayCSS: {
762 background: '#fff',
763 opacity: 0.6,
764 },
765 } );
766
767 var $wrapper = $( this ).closest( '.woocommerce_attribute' );
768 var attribute = $wrapper.data( 'taxonomy' );
769
770 var data = {
771 action: 'woocommerce_json_search_taxonomy_terms',
772 taxonomy: attribute,
773 security: wc_enhanced_select_params.search_taxonomy_terms_nonce,
774 };
775
776 $.get(
777 woocommerce_admin_meta_boxes.ajax_url,
778 data,
779 function ( response ) {
780 if ( response.errors ) {
781 // Error.
782 window.alert( response.errors );
783 } else if ( response && response.length > 0 ) {
784 // Success.
785 response.forEach( function ( term ) {
786 const currentItem = $wrapper.find(
787 'select.attribute_values option[value="' +
788 term.term_id +
789 '"]'
790 );
791 if ( currentItem && currentItem.length > 0 ) {
792 currentItem.prop( 'selected', 'selected' );
793 } else {
794 $wrapper
795 .find( 'select.attribute_values' )
796 .append(
797 '<option value="' +
798 term.term_id +
799 '" selected="selected">' +
800 term.name +
801 '</option>'
802 );
803 }
804 } );
805 $wrapper
806 .find( 'select.attribute_values' )
807 .trigger( 'change' );
808 }
809
810 $( '.product_attributes' ).unblock();
811 }
812 );
813 return false;
814 }
815 );
816
817 $( '.product_attributes' ).on(
818 'click',
819 'button.select_no_attributes',
820 function () {
821 $( this )
822 .closest( 'td' )
823 .find( 'select option' )
824 .prop( 'selected', false );
825 $( this ).closest( 'td' ).find( 'select' ).trigger( 'change' );
826 return false;
827 }
828 );
829
830 $( '#product_attributes' ).on(
831 'click',
832 '.product_attributes .remove_row',
833 function () {
834 var $parent = $( this ).parent().parent();
835 var isUsedForVariations = $parent
836 .find( 'input[name^="attribute_variation"]' )
837 .is( ':visible:checked' );
838
839 if (
840 ! isUsedForVariations ||
841 window.confirm(
842 woocommerce_admin_meta_boxes.i18n_remove_used_attribute_confirmation_message
843 )
844 ) {
845 if ( $parent.is( '.taxonomy' ) ) {
846 $parent.find( 'select, input[type=text]' ).val( '' );
847 $parent.hide();
848 $( 'select.attribute_taxonomy' )
849 .find(
850 'option[value="' + $parent.data( 'taxonomy' ) + '"]'
851 )
852 .prop( 'disabled', false );
853 selectedAttributes = selectedAttributes.filter(
854 ( attr ) => attr !== $parent.data( 'taxonomy' )
855 );
856 $( 'select.wc-attribute-search' ).data(
857 'disabled-items',
858 selectedAttributes
859 );
860 } else {
861 $parent.find( 'select, input[type=text]' ).val( '' );
862 $parent.hide();
863 update_attribute_row_indexes();
864 }
865
866 $parent.remove();
867
868 window.wcTracks.recordEvent( 'product_attributes_buttons', {
869 action: 'remove_attribute',
870 } );
871
872 jQuery.maybe_disable_save_button();
873 }
874 return false;
875 }
876 );
877
878 // Attribute ordering.
879 $( '.product_attributes' ).sortable( {
880 items: '.woocommerce_attribute',
881 cursor: 'move',
882 axis: 'y',
883 handle: 'h3',
884 scrollSensitivity: 40,
885 forcePlaceholderSize: true,
886 helper: 'clone',
887 opacity: 0.65,
888 placeholder: 'wc-metabox-sortable-placeholder',
889 start: function ( event, ui ) {
890 ui.item.css( 'background-color', '#f6f6f6' );
891 },
892 stop: function ( event, ui ) {
893 ui.item.removeAttr( 'style' );
894 update_attribute_row_indexes();
895 },
896 } );
897
898 $( document.body ).on(
899 'wc_backbone_modal_loaded',
900 function ( event, target ) {
901 if ( 'wc-modal-add-attribute-term' !== target ) {
902 return;
903 }
904
905 const modal = document.querySelector(
906 '.wc-backbone-modal-add-attribute-term'
907 );
908
909 if ( ! modal ) {
910 return;
911 }
912
913 const termInput = modal.querySelector(
914 '#wc-modal-add-attribute-term-input'
915 );
916 if ( termInput ) {
917 termInput.focus();
918 }
919
920 const form = modal.querySelector( '.wc-add-attribute-term-fields' );
921 if ( form ) {
922 form.addEventListener( 'submit', ( submitEvent ) => {
923 submitEvent.preventDefault();
924
925 const submitButton = modal.querySelector( '#btn-ok' );
926 if ( submitButton && ! submitButton.disabled ) {
927 submitButton.click();
928 }
929 } );
930 }
931 }
932 );
933
934 $( document.body ).on(
935 'wc_backbone_modal_validation',
936 function ( event, target, postedData ) {
937 if ( 'wc-modal-add-attribute-term' !== target ) {
938 return;
939 }
940
941 const modal = document.querySelector(
942 '.wc-backbone-modal-add-attribute-term'
943 );
944
945 if ( ! modal ) {
946 return;
947 }
948
949 const submitButton = modal.querySelector( '#btn-ok' );
950
951 if ( ! submitButton ) {
952 return;
953 }
954
955 const termName =
956 postedData && postedData.term ? postedData.term.trim() : '';
957 const hasValue = termName.length > 0;
958
959 submitButton.disabled = ! hasValue;
960 }
961 );
962
963 $( document.body ).on(
964 'wc_backbone_modal_response',
965 function ( event, target, postedData ) {
966 if ( 'wc-modal-add-attribute-term' !== target ) {
967 return;
968 }
969
970 if (
971 ! currentAttributeTermCreationContext ||
972 ! currentAttributeTermCreationContext.wrapper ||
973 ! currentAttributeTermCreationContext.attribute
974 ) {
975 $( '.product_attributes' ).unblock();
976 return;
977 }
978
979 const termName =
980 postedData && postedData.term ? postedData.term.trim() : '';
981
982 if ( ! termName ) {
983 $( '.product_attributes' ).unblock();
984 return;
985 }
986
987 const wrapper = currentAttributeTermCreationContext.wrapper;
988 const data = {
989 action: 'woocommerce_add_new_attribute',
990 taxonomy: currentAttributeTermCreationContext.attribute,
991 term: termName,
992 security: woocommerce_admin_meta_boxes.add_attribute_nonce,
993 };
994
995 if (
996 currentAttributeTermCreationContext.isVisualAttribute &&
997 postedData
998 ) {
999 if ( postedData.wc_visual_attribute_type ) {
1000 data.wc_visual_attribute_type =
1001 postedData.wc_visual_attribute_type;
1002 }
1003
1004 if ( postedData.term_color ) {
1005 data.term_color = postedData.term_color;
1006 }
1007
1008 if ( postedData.term_image ) {
1009 data.term_image = postedData.term_image;
1010 }
1011 }
1012
1013 $.post(
1014 woocommerce_admin_meta_boxes.ajax_url,
1015 data,
1016 function ( response ) {
1017 if ( response.error ) {
1018 // Error.
1019 window.alert( response.error );
1020 } else if ( response.slug ) {
1021 // Success.
1022 const select = wrapper.querySelector(
1023 'select.attribute_values'
1024 );
1025 if ( select ) {
1026 const option = document.createElement( 'option' );
1027 option.value = String( response.term_id );
1028 option.selected = true;
1029 option.textContent = response.name;
1030 select.appendChild( option );
1031
1032 // Trigger change event natively.
1033 const changeEvent = new Event( 'change', {
1034 bubbles: true,
1035 } );
1036 select.dispatchEvent( changeEvent );
1037 }
1038 }
1039
1040 $( '.product_attributes' ).unblock();
1041 currentAttributeTermCreationContext = null;
1042 }
1043 );
1044 }
1045 );
1046
1047 // Add a new attribute (via ajax).
1048 $( '.product_attributes' ).on(
1049 'click',
1050 'button.add_new_attribute',
1051 function ( event ) {
1052 // Prevent form submission but allow event propagation.
1053 event.preventDefault();
1054
1055 $( '.product_attributes' ).block( {
1056 message: null,
1057 overlayCSS: {
1058 background: '#fff',
1059 opacity: 0.6,
1060 },
1061 } );
1062
1063 const wrapper = this.closest( '.woocommerce_attribute' );
1064 const attribute = wrapper ? wrapper.dataset.taxonomy : '';
1065 const isVisualAttribute =
1066 this.dataset.isVisualAttribute === 'yes';
1067
1068 currentAttributeTermCreationContext = {
1069 wrapper,
1070 attribute,
1071 isVisualAttribute,
1072 };
1073
1074 $( this ).WCBackboneModal( {
1075 template: 'wc-modal-add-attribute-term',
1076 variable: {
1077 isVisualAttribute,
1078 },
1079 } );
1080 }
1081 );
1082
1083 $( document.body ).on(
1084 'wc_backbone_modal_before_remove',
1085 function ( event, target, postedData, submitButtonCalled ) {
1086 if ( 'wc-modal-add-attribute-term' !== target ) {
1087 return;
1088 }
1089
1090 if ( submitButtonCalled ) {
1091 return;
1092 }
1093
1094 $( '.product_attributes' ).unblock();
1095 currentAttributeTermCreationContext = null;
1096 }
1097 );
1098
1099 // Save attributes and update variations.
1100 $( '.save_attributes' ).on( 'click', function ( event ) {
1101 if ( $( this ).hasClass( 'disabled' ) ) {
1102 event.preventDefault();
1103 return;
1104 }
1105 $( '.product_attributes' ).block( {
1106 message: null,
1107 overlayCSS: {
1108 background: '#fff',
1109 opacity: 0.6,
1110 },
1111 } );
1112
1113 var original_data = $( '.product_attributes' ).find(
1114 'input, select, textarea'
1115 );
1116 var data = {
1117 post_id: woocommerce_admin_meta_boxes.post_id,
1118 product_type: $( '#product-type' ).val(),
1119 data: original_data.serialize(),
1120 action: 'woocommerce_save_attributes',
1121 security: woocommerce_admin_meta_boxes.save_attributes_nonce,
1122 };
1123
1124 $.post(
1125 woocommerce_admin_meta_boxes.ajax_url,
1126 data,
1127 function ( response ) {
1128 if ( response.error ) {
1129 // Error.
1130 window.alert( response.error );
1131 } else if ( response.data ) {
1132 // Success.
1133 $( '.product_attributes' ).html( response.data.html );
1134 $( '.product_attributes' ).unblock();
1135
1136 // Hide the 'Used for variations' checkbox if not viewing a variable product
1137 show_and_hide_panels();
1138
1139 // Make sure the dropdown is not disabled for empty value attributes.
1140 $( 'select.attribute_taxonomy' )
1141 .find( 'option' )
1142 .prop( 'disabled', false );
1143
1144 var newSelectedAttributes = [];
1145 $( '.product_attributes .woocommerce_attribute' ).each(
1146 function ( index, el ) {
1147 if (
1148 $( el ).css( 'display' ) !== 'none' &&
1149 $( el ).is( '.taxonomy' )
1150 ) {
1151 newSelectedAttributes.push(
1152 $( el ).data( 'taxonomy' )
1153 );
1154 $( 'select.attribute_taxonomy' )
1155 .find(
1156 'option[value="' +
1157 $( el ).data( 'taxonomy' ) +
1158 '"]'
1159 )
1160 .prop( 'disabled', true );
1161 }
1162 }
1163 );
1164 selectedAttributes = newSelectedAttributes;
1165 $( 'select.wc-attribute-search' ).data(
1166 'disabled-items',
1167 newSelectedAttributes
1168 );
1169
1170 // Reload variations panel.
1171 var this_page = window.location.toString();
1172 this_page = this_page.replace(
1173 'post-new.php?',
1174 'post.php?post=' +
1175 woocommerce_admin_meta_boxes.post_id +
1176 '&action=edit&'
1177 );
1178
1179 $( '#variable_product_options' ).load(
1180 this_page + ' #variable_product_options_inner',
1181 function () {
1182 $( '#variable_product_options' ).trigger(
1183 'reload'
1184 );
1185 }
1186 );
1187
1188 $( document.body ).trigger(
1189 'woocommerce_attributes_saved'
1190 );
1191 }
1192 }
1193 );
1194 } );
1195
1196 // Go to attributes tab when clicking on link in variations message
1197 $( document.body ).on(
1198 'click',
1199 '#variable_product_options .add-attributes-message a[href="#product_attributes"]',
1200 function () {
1201 $(
1202 '#woocommerce-product-data .attribute_tab a[href="#product_attributes"]'
1203 ).trigger( 'click' );
1204 return false;
1205 }
1206 );
1207
1208 // Uploading files.
1209 var downloadable_file_frame;
1210 var file_path_field;
1211
1212 $( document.body ).on( 'click', '.upload_file_button', function ( event ) {
1213 var $el = $( this );
1214
1215 file_path_field = $el.closest( 'tr' ).find( 'td.file_url input' );
1216
1217 event.preventDefault();
1218
1219 // If the media frame already exists, reopen it.
1220 if ( downloadable_file_frame ) {
1221 downloadable_file_frame.open();
1222 return;
1223 }
1224
1225 var downloadable_file_states = [
1226 // Main states.
1227 new wp.media.controller.Library( {
1228 library: wp.media.query(),
1229 multiple: true,
1230 title: $el.data( 'choose' ),
1231 priority: 20,
1232 filterable: 'uploaded',
1233 } ),
1234 ];
1235
1236 // Create the media frame.
1237 downloadable_file_frame = wp.media.frames.downloadable_file = wp.media(
1238 {
1239 // Set the title of the modal.
1240 title: $el.data( 'choose' ),
1241 library: {
1242 type: '',
1243 },
1244 button: {
1245 text: $el.data( 'update' ),
1246 },
1247 multiple: true,
1248 states: downloadable_file_states,
1249 }
1250 );
1251
1252 // When an image is selected, run a callback.
1253 downloadable_file_frame.on( 'select', function () {
1254 var file_path = '';
1255 var selection = downloadable_file_frame.state().get( 'selection' );
1256
1257 selection.map( function ( attachment ) {
1258 attachment = attachment.toJSON();
1259 if ( attachment.url ) {
1260 file_path = attachment.url;
1261 }
1262 } );
1263
1264 file_path_field.val( file_path ).trigger( 'change' );
1265 } );
1266
1267 // Set post to 0 and set our custom type.
1268 downloadable_file_frame.on( 'ready', function () {
1269 downloadable_file_frame.uploader.options.uploader.params = {
1270 type: 'downloadable_product',
1271 };
1272 } );
1273
1274 // Finally, open the modal.
1275 downloadable_file_frame.open();
1276 } );
1277
1278 // Download ordering.
1279 $( '.downloadable_files tbody' ).sortable( {
1280 items: 'tr',
1281 cursor: 'move',
1282 axis: 'y',
1283 handle: 'td.sort',
1284 scrollSensitivity: 40,
1285 forcePlaceholderSize: true,
1286 helper: 'clone',
1287 opacity: 0.65,
1288 } );
1289
1290 // Product gallery file uploads.
1291 var product_gallery_frame;
1292 var $image_gallery_ids = $( '#product_image_gallery' );
1293 var $product_images = $( '#product_images_container' ).find(
1294 'ul.product_images'
1295 );
1296
1297 $( '.add_product_images' ).on( 'click', 'a', function ( event ) {
1298 var $el = $( this );
1299
1300 event.preventDefault();
1301
1302 // If the media frame already exists, reopen it.
1303 if ( product_gallery_frame ) {
1304 product_gallery_frame.open();
1305 return;
1306 }
1307
1308 // Create the media frame.
1309 product_gallery_frame = wp.media.frames.product_gallery = wp.media( {
1310 // Set the title of the modal.
1311 title: $el.data( 'choose' ),
1312 button: {
1313 text: $el.data( 'update' ),
1314 },
1315 states: [
1316 new wp.media.controller.Library( {
1317 title: $el.data( 'choose' ),
1318 filterable: 'all',
1319 multiple: true,
1320 } ),
1321 ],
1322 } );
1323
1324 // When an image is selected, run a callback.
1325 product_gallery_frame.on( 'select', function () {
1326 var selection = product_gallery_frame.state().get( 'selection' );
1327 var attachment_ids = $image_gallery_ids.val();
1328
1329 selection.map( function ( attachment ) {
1330 attachment = attachment.toJSON();
1331
1332 if ( attachment.id ) {
1333 attachment_ids = attachment_ids
1334 ? attachment_ids + ',' + attachment.id
1335 : attachment.id;
1336 var attachment_image =
1337 attachment.sizes && attachment.sizes.thumbnail
1338 ? attachment.sizes.thumbnail.url
1339 : attachment.url;
1340
1341 $product_images.append(
1342 '<li class="image" data-attachment_id="' +
1343 attachment.id +
1344 '"><img src="' +
1345 attachment_image +
1346 '" /><ul class="actions"><li><a href="#" class="delete" title="' +
1347 $el.data( 'delete' ) +
1348 '">' +
1349 $el.data( 'text' ) +
1350 '</a></li></ul></li>'
1351 );
1352 }
1353 } );
1354
1355 $image_gallery_ids.val( attachment_ids );
1356 } );
1357
1358 // Finally, open the modal.
1359 product_gallery_frame.open();
1360 } );
1361
1362 // Image ordering.
1363 $product_images.sortable( {
1364 items: 'li.image',
1365 cursor: 'move',
1366 scrollSensitivity: 40,
1367 forcePlaceholderSize: true,
1368 forceHelperSize: false,
1369 helper: 'clone',
1370 opacity: 0.65,
1371 placeholder: 'wc-metabox-sortable-placeholder',
1372 start: function ( event, ui ) {
1373 ui.item.css( 'background-color', '#f6f6f6' );
1374 },
1375 stop: function ( event, ui ) {
1376 ui.item.removeAttr( 'style' );
1377 },
1378 update: function () {
1379 var attachment_ids = '';
1380
1381 $( '#product_images_container' )
1382 .find( 'ul li.image' )
1383 .css( 'cursor', 'default' )
1384 .each( function () {
1385 var attachment_id = $( this ).attr( 'data-attachment_id' );
1386 attachment_ids = attachment_ids + attachment_id + ',';
1387 } );
1388
1389 $image_gallery_ids.val( attachment_ids );
1390 },
1391 } );
1392
1393 // Remove images.
1394 $( '#product_images_container' ).on( 'click', 'a.delete', function () {
1395 $( this ).closest( 'li.image' ).remove();
1396
1397 var attachment_ids = '';
1398
1399 $( '#product_images_container' )
1400 .find( 'ul li.image' )
1401 .css( 'cursor', 'default' )
1402 .each( function () {
1403 var attachment_id = $( this ).attr( 'data-attachment_id' );
1404 attachment_ids = attachment_ids + attachment_id + ',';
1405 } );
1406
1407 $image_gallery_ids.val( attachment_ids );
1408
1409 // Remove any lingering tooltips.
1410 $( '#tiptip_holder' ).removeAttr( 'style' );
1411 $( '#tiptip_arrow' ).removeAttr( 'style' );
1412
1413 return false;
1414 } );
1415
1416 // Add a descriptive tooltip to the product description editor
1417 $( '#wp-content-media-buttons' )
1418 .append( '<span class="woocommerce-help-tip" tabindex="0"></span>' )
1419 .find( '.woocommerce-help-tip' )
1420 .attr( 'tabindex', '0' )
1421 .attr( 'for', 'content' )
1422 .attr(
1423 'aria-label',
1424 woocommerce_admin_meta_boxes.i18n_product_description_tip
1425 )
1426 .tipTip( {
1427 attribute: 'data-tip',
1428 content: woocommerce_admin_meta_boxes.i18n_product_description_tip,
1429 fadeIn: 50,
1430 fadeOut: 50,
1431 delay: 200,
1432 keepAlive: true,
1433 } );
1434
1435 // Add a descriptive tooltip to the product short description meta box title
1436 $( '#postexcerpt > .postbox-header > .hndle' )
1437 .append( '<span class="woocommerce-help-tip"></span>' )
1438 .find( '.woocommerce-help-tip' )
1439 .attr( 'tabindex', '0' )
1440 .attr(
1441 'aria-label',
1442 woocommerce_admin_meta_boxes.i18n_product_short_description_tip
1443 )
1444 .tipTip( {
1445 attribute: 'data-tip',
1446 content:
1447 woocommerce_admin_meta_boxes.i18n_product_short_description_tip,
1448 fadeIn: 50,
1449 fadeOut: 50,
1450 delay: 200,
1451 keepAlive: true,
1452 } );
1453
1454 // add a tooltip to the right of the product image meta box "Set product image" and "Add product gallery images"
1455 const setProductImageLink = $( '#set-post-thumbnail' );
1456 // Escape the translated label before interpolating into the attribute so a
1457 // translation containing quotes or markup cannot break the rendered span.
1458 const tooltipMarkup = `<span class="woocommerce-help-tip" tabindex="0" aria-label="${
1459 _.escape( woocommerce_admin_meta_boxes.i18n_product_image_tip )
1460 }"></span>`;
1461 const tooltipData = {
1462 attribute: 'data-tip',
1463 content: woocommerce_admin_meta_boxes.i18n_product_image_tip,
1464 fadeIn: 50,
1465 fadeOut: 50,
1466 delay: 200,
1467 keepAlive: true,
1468 };
1469
1470 if ( setProductImageLink ) {
1471 $( tooltipMarkup )
1472 .insertAfter( setProductImageLink )
1473 .tipTip( tooltipData );
1474 }
1475
1476 const addProductImagesLink = $( '.add_product_images > a' );
1477
1478 if ( addProductImagesLink ) {
1479 $( tooltipMarkup )
1480 .insertAfter( addProductImagesLink )
1481 .tipTip( tooltipData );
1482 }
1483 } );
1484