Guest User

add-to-cart-bundle.js

a guest
Oct 9th, 2020
32
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 83.49 KB | None | 0 0
  1. /* jshint -W069 */
  2. /* jshint -W041 */
  3.  
  4. /*-----------------------------------------------------------------*/
  5. /* Global script variable. */
  6. /*-----------------------------------------------------------------*/
  7.  
  8. var wc_pb_bundle_scripts = {};
  9.  
  10. /*-----------------------------------------------------------------*/
  11. /* Global utility variables + functions. */
  12. /*-----------------------------------------------------------------*/
  13.  
  14. /**
  15. * Converts numbers to formatted price strings. Respects WC price format settings.
  16. */
  17. function wc_pb_price_format( price, plain ) {
  18.  
  19. plain = typeof( plain ) === 'undefined' ? false : plain;
  20.  
  21. return wc_pb_woocommerce_number_format( wc_pb_number_format( price ), plain );
  22. }
  23.  
  24. /**
  25. * Formats price strings according to WC settings.
  26. */
  27. function wc_pb_woocommerce_number_format( price, plain ) {
  28. /*woocs FIX*/
  29. if (woocs_current_currency != undefined && woocs_current_currency['rate'] != undefined && wc_bundle_params.woocs_is_multiple==0) {
  30. price = wc_pb_number_round(price * woocs_current_currency['rate']);
  31. }
  32. /*woocs FIX*/
  33. var remove = wc_bundle_params.currency_format_decimal_sep,
  34. position = wc_bundle_params.currency_position,
  35. symbol = wc_bundle_params.currency_symbol,
  36. trim_zeros = wc_bundle_params.currency_format_trim_zeros,
  37. decimals = wc_bundle_params.currency_format_num_decimals;
  38.  
  39. plain = typeof( plain ) === 'undefined' ? false : plain;
  40.  
  41. if ( trim_zeros == 'yes' && decimals > 0 ) {
  42. for ( var i = 0; i < decimals; i++ ) { remove = remove + '0'; }
  43. price = price.replace( remove, '' );
  44. }
  45.  
  46. var formatted_price = String( price ),
  47. formatted_symbol = plain ? symbol : '<span class="woocommerce-Price-currencySymbol">' + symbol + '</span>';
  48.  
  49. if ( 'left' === position ) {
  50. formatted_price = formatted_symbol + formatted_price;
  51. } else if ( 'right' === position ) {
  52. formatted_price = formatted_price + formatted_symbol;
  53. } else if ( 'left_space' === position ) {
  54. formatted_price = formatted_symbol + ' ' + formatted_price;
  55. } else if ( 'right_space' === position ) {
  56. formatted_price = formatted_price + ' ' + formatted_symbol;
  57. }
  58.  
  59. formatted_price = plain ? formatted_price : '<span class="woocommerce-Price-amount amount">' + formatted_price + '</span>';
  60.  
  61. return formatted_price;
  62. }
  63.  
  64. /**
  65. * Formats price values according to WC settings.
  66. */
  67. function wc_pb_number_format( number ) {
  68.  
  69. var decimals = wc_bundle_params.currency_format_num_decimals,
  70. decimal_sep = wc_bundle_params.currency_format_decimal_sep,
  71. thousands_sep = wc_bundle_params.currency_format_thousand_sep;
  72.  
  73. var n = number, c = isNaN( decimals = Math.abs( decimals ) ) ? 2 : decimals;
  74. var d = decimal_sep == undefined ? ',' : decimal_sep;
  75. var t = thousands_sep == undefined ? '.' : thousands_sep, s = n < 0 ? '-' : '';
  76. var i = parseInt( n = Math.abs( +n || 0 ).toFixed( c ), 10 ) + '', j = ( j = i.length ) > 3 ? j % 3 : 0;
  77.  
  78. return s + ( j ? i.substr( 0, j ) + t : '' ) + i.substr( j ).replace( /(\d{3})(?=\d)/g, '$1' + t ) + ( c ? d + Math.abs( n - i ).toFixed( c ).slice( 2 ) : '' );
  79. }
  80.  
  81. /**
  82. * Rounds price values according to WC settings.
  83. */
  84. function wc_pb_number_round( number ) {
  85.  
  86. var precision = wc_bundle_params.currency_format_num_decimals,
  87. factor = Math.pow( 10, precision ),
  88. tempNumber = number * factor,
  89. roundedTempNumber = Math.round( tempNumber );
  90.  
  91. return roundedTempNumber / factor;
  92. }
  93.  
  94. /**
  95. * i18n-friendly string joining.
  96. */
  97. function wc_pb_format_list( arr ) {
  98.  
  99. var formatted = '',
  100. count = arr.length;
  101.  
  102. if ( count > 0 ) {
  103.  
  104. var loop = 0,
  105. item = '';
  106.  
  107. for ( var i = 0; i < count; i++ ) {
  108.  
  109. loop++;
  110. item = wc_bundle_params.i18n_string_list_item.replace( '%s', arr[ i ] );
  111.  
  112. if ( count == 1 || loop == 1 ) {
  113. formatted = item;
  114. } else if ( loop === count ) {
  115. formatted = wc_bundle_params.i18n_string_list_last_sep.replace( '%s', formatted ).replace( '%v', item );
  116. } else {
  117. formatted = wc_bundle_params.i18n_string_list_sep.replace( '%s', formatted ).replace( '%v', item );
  118. }
  119. }
  120. }
  121.  
  122. return formatted;
  123. }
  124.  
  125. /**
  126. * Bundle script object getter.
  127. */
  128. jQuery.fn.wc_get_bundle_script = function() {
  129.  
  130. var $bundle_form = jQuery( this );
  131.  
  132. if ( ! $bundle_form.hasClass( 'bundle_form' ) ) {
  133. return false;
  134. }
  135.  
  136. var script_id = $bundle_form.data( 'script_id' );
  137.  
  138. if ( typeof( wc_pb_bundle_scripts[ script_id ] ) !== 'undefined' ) {
  139. return wc_pb_bundle_scripts[ script_id ];
  140. }
  141.  
  142. return false;
  143. };
  144.  
  145. /*-----------------------------------------------------------------*/
  146. /* Encapsulation. */
  147. /*-----------------------------------------------------------------*/
  148.  
  149. ( function( $ ) {
  150.  
  151. /**
  152. * Main bundle object.
  153. */
  154. function WC_PB_Bundle( data ) {
  155.  
  156. var bundle = this;
  157.  
  158. this.bundle_id = data.bundle_id;
  159.  
  160. this.$bundle_form = data.$bundle_form;
  161. this.$bundle_data = data.$bundle_data;
  162. this.$bundle_wrap = data.$bundle_data.find( '.bundle_wrap' );
  163. this.$bundled_items = data.$bundle_form.find( '.bundled_product' );
  164.  
  165. this.$bundle_availability = data.$bundle_data.find( '.bundle_availability' );
  166. this.$bundle_price = data.$bundle_data.find( '.bundle_price' );
  167. this.$bundle_button = data.$bundle_data.find( '.bundle_button' );
  168. this.$bundle_error = data.$bundle_data.find( '.bundle_error' );
  169. this.$bundle_error_content = this.$bundle_error.find( 'ul.msg' );
  170. this.$bundle_quantity = this.$bundle_button.find( 'input.qty' );
  171.  
  172. this.$nyp = this.$bundle_data.find( '.nyp' );
  173.  
  174. this.$addons_totals = this.$bundle_data.find( '#product-addons-total' );
  175. this.show_addons_totals = false;
  176.  
  177. this.bundled_items = {};
  178.  
  179. this.price_data = data.$bundle_data.data( 'bundle_price_data' );
  180.  
  181. this.$initial_stock_status = false;
  182.  
  183. this.update_bundle_timer = false;
  184. this.update_price_timer = false;
  185.  
  186. this.validation_messages = [];
  187.  
  188. this.is_initialized = false;
  189.  
  190. this.composite_data = data.composite_data;
  191.  
  192. this.dirty_subtotals = false;
  193.  
  194. this.filters = false;
  195.  
  196. this.api = {
  197.  
  198. /**
  199. * Get the current bundle totals.
  200. *
  201. * @return object
  202. */
  203. get_bundle_totals: function() {
  204.  
  205. return bundle.price_data[ 'totals' ];
  206. },
  207.  
  208. /**
  209. * Get the current bundled item totals.
  210. *
  211. * @return object
  212. */
  213. get_bundled_item_totals: function( bundled_item_id ) {
  214.  
  215. return bundle.price_data[ 'bundled_item_' + bundled_item_id + '_totals' ];
  216. },
  217.  
  218. /**
  219. * Get the current bundled item recurring totals.
  220. *
  221. * @return object
  222. */
  223. get_bundled_item_recurring_totals: function( bundled_item_id ) {
  224.  
  225. return bundle.price_data[ 'bundled_item_' + bundled_item_id + '_recurring_totals' ];
  226. },
  227.  
  228. /**
  229. * Get the current validation status of the bundle.
  230. *
  231. * @return string ('pass' | 'fail')
  232. */
  233. get_bundle_validation_status: function() {
  234.  
  235. return bundle.passes_validation() ? 'pass' : 'fail';
  236. },
  237.  
  238. /**
  239. * Get the current validation messages for the bundle.
  240. *
  241. * @return array
  242. */
  243. get_bundle_validation_messages: function() {
  244.  
  245. return bundle.get_validation_messages();
  246. },
  247.  
  248. /**
  249. * Get the current stock status of the bundle.
  250. *
  251. * @return string ('in-stock' | 'out-of-stock')
  252. */
  253. get_bundle_stock_status: function() {
  254.  
  255. var availability = bundle.$bundle_wrap.find( 'p.out-of-stock' ).not( '.inactive' );
  256.  
  257. return availability.length > 0 ? 'out-of-stock' : 'in-stock';
  258. },
  259.  
  260. /**
  261. * Get the current availability string of the bundle.
  262. *
  263. * @return string
  264. */
  265. get_bundle_availability: function() {
  266.  
  267. var availability = bundle.$bundle_wrap.find( 'p.stock' );
  268.  
  269. if ( availability.hasClass( 'inactive' ) ) {
  270. if ( false !== bundle.$initial_stock_status ) {
  271. availability = bundle.$initial_stock_status.clone().wrap( '<div></div>' ).parent().html();
  272. } else {
  273. availability = '';
  274. }
  275. } else {
  276. availability = availability.clone().removeAttr( 'style' ).wrap( '<div></div>' ).parent().html();
  277. }
  278.  
  279. return availability;
  280. },
  281.  
  282. /**
  283. * Gets bundle configuration details.
  284. *
  285. * @return object | false
  286. */
  287. get_bundle_configuration: function() {
  288.  
  289. var bundle_config = {};
  290.  
  291. if ( bundle.bundled_items.length === 0 ) {
  292. return false;
  293. }
  294.  
  295. $.each( bundle.bundled_items, function( index, bundled_item ) {
  296.  
  297. var bundled_item_config = {
  298. title: bundled_item.get_title(),
  299. product_title: bundled_item.get_product_title(),
  300. product_id: bundled_item.get_product_id(),
  301. variation_id: bundled_item.get_variation_id(),
  302. quantity: bundle.price_data[ 'quantities' ][ bundled_item.bundled_item_id ],
  303. product_type: bundled_item.get_product_type(),
  304. };
  305.  
  306. bundle_config[ bundled_item.bundled_item_id ] = bundled_item_config;
  307. } );
  308.  
  309. return bundle_config;
  310. }
  311. };
  312.  
  313. /**
  314. * Object initialization.
  315. */
  316. this.initialize = function() {
  317.  
  318. /**
  319. * Initial states and loading.
  320. */
  321.  
  322. // Filters API.
  323. this.filters = new WC_PB_Filters_Manager();
  324.  
  325. // Addons compatibility.
  326. if ( this.has_addons() ) {
  327.  
  328. // Totals visible?
  329. if ( 1 == this.$addons_totals.data( 'show-sub-total' ) || ( this.is_composited() && this.composite_data.component.show_addons_totals ) ) {
  330. // Ensure addons ajax is not triggered at all, as we calculate tax on the client side.
  331. this.$addons_totals.data( 'show-sub-total', 0 );
  332. this.$bundle_price.after( this.$addons_totals );
  333. this.show_addons_totals = true;
  334. }
  335.  
  336. } else {
  337. this.$addons_totals = false;
  338. }
  339.  
  340. // Save initial availability.
  341. if ( this.$bundle_wrap.find( 'p.stock' ).length > 0 ) {
  342. this.$initial_stock_status = this.$bundle_wrap.find( 'p.stock' ).clone();
  343. }
  344.  
  345. // Price suffix data.
  346. this.price_data.suffix_exists = wc_bundle_params.price_display_suffix !== '';
  347. this.price_data.suffix = wc_bundle_params.price_display_suffix !== '' ? ' <small class="woocommerce-price-suffix">' + wc_bundle_params.price_display_suffix + '</small>' : '';
  348. this.price_data.suffix_contains_price_incl = wc_bundle_params.price_display_suffix.indexOf( '{price_including_tax}' ) > -1;
  349. this.price_data.suffix_contains_price_excl = wc_bundle_params.price_display_suffix.indexOf( '{price_excluding_tax}' ) > -1;
  350.  
  351. // Delete redundant form inputs.
  352. this.$bundle_button.find( 'input[name*="bundle_variation"], input[name*="bundle_attribute"]' ).remove();
  353.  
  354. /**
  355. * Bind bundle event handlers.
  356. */
  357.  
  358. this.bind_event_handlers();
  359. this.viewport_resized();
  360.  
  361. /**
  362. * Init Bundled Items.
  363. */
  364.  
  365. this.init_bundled_items();
  366.  
  367. /**
  368. * Init Composite Products integration.
  369. */
  370.  
  371. if ( this.is_composited() ) {
  372. this.init_composite();
  373. }
  374.  
  375. /**
  376. * Initialize.
  377. */
  378.  
  379. this.$bundle_data.triggerHandler( 'woocommerce-product-bundle-initializing', [ this ] );
  380.  
  381. $.each( this.bundled_items, function( index, bundled_item ) {
  382. bundled_item.init_scripts();
  383. } );
  384.  
  385. this.update_bundle_task();
  386.  
  387. this.is_initialized = true;
  388.  
  389. this.$bundle_form.addClass( 'initialized' );
  390.  
  391. this.$bundle_data.trigger( 'woocommerce-product-bundle-initialized', [ this ] );
  392. };
  393.  
  394. /**
  395. * Shuts down events, actions and filters managed by this script object.
  396. */
  397. this.shutdown = function() {
  398.  
  399. this.$bundle_form.find( '*' ).off();
  400.  
  401. if ( false !== this.composite_data ) {
  402. this.remove_composite_hooks();
  403. }
  404. };
  405.  
  406. /**
  407. * Composite Products app integration.
  408. */
  409. this.init_composite = function() {
  410.  
  411. /**
  412. * Add/remove hooks on the 'component_scripts_initialized' action.
  413. */
  414. this.composite_data.composite.actions.add_action( 'component_scripts_initialized_' + this.composite_data.component.step_id, this.component_scripts_initialized_action, 10, this );
  415. };
  416.  
  417. /**
  418. * Add hooks on the 'component_scripts_initialized' action.
  419. */
  420. this.component_scripts_initialized_action = function() {
  421.  
  422. var is_bundle_selected = false;
  423.  
  424. // Composite Products < 4.0 compatibility.
  425. if ( typeof this.composite_data.component.component_selection_model.selected_product !== 'undefined' ) {
  426. is_bundle_selected = parseInt( this.composite_data.component.component_selection_model.selected_product, 10 ) === parseInt( this.bundle_id, 10 );
  427. } else {
  428. is_bundle_selected = parseInt( this.composite_data.component.component_selection_model.get( 'selected_product' ), 10 ) === parseInt( this.bundle_id, 10 );
  429. }
  430.  
  431. if ( is_bundle_selected ) {
  432. this.add_composite_hooks();
  433. } else {
  434. this.remove_composite_hooks();
  435. }
  436. };
  437.  
  438. /**
  439. * Composite Products app integration - add actions and filters.
  440. */
  441. this.add_composite_hooks = function() {
  442.  
  443. /**
  444. * Filter validation state.
  445. */
  446. this.composite_data.composite.filters.add_filter( 'component_is_valid', this.cp_component_is_valid_filter, 10, this );
  447.  
  448. /**
  449. * Filter title in summary.
  450. */
  451. this.composite_data.composite.filters.add_filter( 'component_selection_formatted_title', this.cp_component_selection_formatted_title_filter, 10, this );
  452. this.composite_data.composite.filters.add_filter( 'component_selection_meta', this.cp_component_selection_meta_filter, 10, this );
  453.  
  454. /**
  455. * Filter totals.
  456. */
  457. this.composite_data.composite.filters.add_filter( 'component_totals', this.cp_component_totals_filter, 10, this );
  458.  
  459. /**
  460. * Filter component configuration data.
  461. */
  462. this.composite_data.composite.filters.add_filter( 'component_configuration', this.cp_component_configuration_filter, 10, this );
  463.  
  464. /**
  465. * Add validation messages.
  466. */
  467. this.composite_data.composite.actions.add_action( 'validate_step', this.cp_validation_messages_action, 10, this );
  468. };
  469.  
  470. /**
  471. * Composite Products app integration - remove actions and filters.
  472. */
  473. this.remove_composite_hooks = function() {
  474.  
  475. this.composite_data.composite.filters.remove_filter( 'component_is_valid', this.cp_component_is_valid_filter );
  476. this.composite_data.composite.filters.remove_filter( 'component_selection_formatted_title', this.cp_component_selection_formatted_title_filter );
  477. this.composite_data.composite.filters.remove_filter( 'component_selection_meta', this.cp_component_selection_meta_filter );
  478. this.composite_data.composite.filters.remove_filter( 'component_totals', this.cp_component_totals_filter );
  479. this.composite_data.composite.filters.remove_filter( 'component_configuration', this.cp_component_configuration_filter );
  480.  
  481. this.composite_data.composite.actions.remove_action( 'component_scripts_initialized_' + this.composite_data.component.step_id, this.component_scripts_initialized_action );
  482. this.composite_data.composite.actions.remove_action( 'validate_step', this.cp_validation_messages_action );
  483. };
  484.  
  485. /**
  486. * Appends bundle configuration data to component config data.
  487. */
  488. this.cp_component_configuration_filter = function( configuration_data, component ) {
  489.  
  490. if ( component.step_id === this.composite_data.component.step_id && parseInt( component.get_selected_product(), 10 ) === parseInt( bundle.bundle_id, 10 ) ) {
  491. configuration_data[ 'bundled_items' ] = bundle.api.get_bundle_configuration();
  492. }
  493.  
  494. return configuration_data;
  495. };
  496.  
  497. /**
  498. * Filters the component totals to pass on the calculated bundle totals.
  499. */
  500. this.cp_component_totals_filter = function( totals, component, qty ) {
  501.  
  502. if ( component.step_id === this.composite_data.component.step_id && parseInt( component.get_selected_product(), 10 ) === parseInt( bundle.bundle_id, 10 ) ) {
  503.  
  504. var price_data = $.extend( true, {}, bundle.price_data ),
  505. addons_raw_price = bundle.has_addons() ? bundle.get_addons_raw_price() : 0;
  506.  
  507. qty = typeof( qty ) === 'undefined' ? component.get_selected_quantity() : qty;
  508.  
  509. if ( addons_raw_price > 0 ) {
  510. // Recalculate price html with add-ons price and qty embedded.
  511. price_data.base_price = Number( price_data.base_price ) + Number( addons_raw_price );
  512. }
  513.  
  514. price_data = bundle.calculate_subtotals( false, price_data, qty );
  515. price_data = bundle.calculate_totals( price_data );
  516.  
  517. return price_data.totals;
  518. }
  519.  
  520. return totals;
  521. };
  522.  
  523. /**
  524. * Filters the summary view title to include bundled product details.
  525. */
  526. this.cp_component_selection_formatted_title_filter = function( formatted_title, raw_title, qty, formatted_meta, component ) {
  527.  
  528. if ( component.step_id === this.composite_data.component.step_id && parseInt( component.get_selected_product(), 10 ) === parseInt( this.bundle_id, 10 ) ) {
  529.  
  530. var bundled_products_count = 0;
  531.  
  532. $.each( this.bundled_items, function( index, bundled_item ) {
  533. if ( bundled_item.$bundled_item_cart.data( 'quantity' ) > 0 ) {
  534. bundled_products_count++;
  535. }
  536. } );
  537.  
  538. if ( this.group_mode_supports( 'component_multiselect' ) ) {
  539. if ( bundled_products_count === 0 ) {
  540. formatted_title = wc_composite_params.i18n_no_selection;
  541. } else {
  542.  
  543. var contents = this.cp_get_formatted_contents( component );
  544.  
  545. if ( contents ) {
  546. formatted_title = contents;
  547. }
  548. }
  549. }
  550. }
  551.  
  552. return formatted_title;
  553. };
  554.  
  555. /**
  556. * Filters the summary view title to include bundled product details.
  557. */
  558. this.cp_component_selection_meta_filter = function( meta, component ) {
  559.  
  560. if ( component.step_id === this.composite_data.component.step_id && parseInt( component.get_selected_product(), 10 ) === parseInt( this.bundle_id, 10 ) ) {
  561.  
  562. var bundled_products_count = 0;
  563.  
  564. $.each( this.bundled_items, function( index, bundled_item ) {
  565. if ( bundled_item.$bundled_item_cart.data( 'quantity' ) > 0 ) {
  566. bundled_products_count++;
  567. }
  568. } );
  569.  
  570. if ( bundled_products_count !== 0 && false === this.group_mode_supports( 'component_multiselect' ) ) {
  571.  
  572. var selected_bundled_products = this.cp_get_formatted_contents( component );
  573.  
  574. if ( selected_bundled_products !== '' ) {
  575. meta.push( { meta_key: wc_bundle_params.i18n_contents, meta_value: selected_bundled_products } );
  576. }
  577. }
  578. }
  579.  
  580. return meta;
  581. };
  582.  
  583. /**
  584. * Formatted bundle contents for display in Composite Products summary views.
  585. */
  586. this.cp_get_formatted_contents = function( component ) {
  587.  
  588. var formatted_contents = '',
  589. bundled_item_details = [],
  590. bundle_qty = component.get_selected_quantity();
  591.  
  592. $.each( this.bundled_items, function( index, bundled_item ) {
  593.  
  594. if ( bundled_item.$self.hasClass( 'bundled_item_hidden' ) ) {
  595. return true;
  596. }
  597.  
  598. if ( bundled_item.$bundled_item_cart.data( 'quantity' ) > 0 ) {
  599.  
  600. var $item_image = bundled_item.$bundled_item_image.find( 'img' ).first(),
  601. item_image = $item_image.length > 0 ? $item_image.get( 0 ).outerHTML : false,
  602. item_quantity = parseInt( bundled_item.$bundled_item_cart.data( 'quantity' ) * bundle_qty, 10 ),
  603. item_meta = wc_cp_get_variation_data( bundled_item.$bundled_item_cart.find( '.variations' ) ),
  604. formatted_item_title = bundled_item.$bundled_item_cart.data( 'title' ),
  605. formatted_item_quantity = item_quantity > 1 ? '<strong>' + wc_composite_params.i18n_qty_string.replace( '%s', item_quantity ) + '</strong>' : '',
  606. formatted_item_meta = '';
  607.  
  608. if ( item_meta.length > 0 ) {
  609.  
  610. $.each( item_meta, function( index, meta ) {
  611. formatted_item_meta = formatted_item_meta + '<span class="bundled_meta_element"><span class="bundled_meta_key">' + meta.meta_key + ':</span> <span class="bundled_meta_value">' + meta.meta_value + '</span>';
  612. if ( index !== item_meta.length - 1 ) {
  613. formatted_item_meta = formatted_item_meta + '<span class="bundled_meta_value_sep">, </span>';
  614. }
  615. formatted_item_meta = formatted_item_meta + '</span>';
  616. } );
  617.  
  618. formatted_item_title = wc_bundle_params.i18n_title_meta_string.replace( '%t', formatted_item_title ).replace( '%m', '<span class="content_bundled_product_meta">' + formatted_item_meta + '</span>' );
  619. }
  620.  
  621. formatted_item_title = wc_composite_params.i18n_title_string.replace( '%t', formatted_item_title ).replace( '%q', formatted_item_quantity ).replace( '%p', '' );
  622.  
  623. bundled_item_details.push( { title: formatted_item_title, image: item_image } );
  624. }
  625. } );
  626.  
  627. if ( bundled_item_details.length > 0 ) {
  628.  
  629. formatted_contents = formatted_contents + '<span class="content_bundled_product_details_wrapper">';
  630.  
  631. $.each( bundled_item_details, function( index, details ) {
  632. formatted_contents = formatted_contents + '<span class="content_bundled_product_details">' + ( details.image ? '<span class="content_bundled_product_image">' + details.image + '</span>' : '' ) + '<span class="content_bundled_product_title">' + details.title + '</span></span>';
  633. } );
  634.  
  635. formatted_contents = formatted_contents + '</span>';
  636. }
  637.  
  638. return formatted_contents;
  639. };
  640.  
  641. /**
  642. * Filters the validation state of the component containing this bundle.
  643. */
  644. this.cp_component_is_valid_filter = function( is_valid, check_scenarios, component ) {
  645.  
  646. if ( component.step_id === this.composite_data.component.step_id ) {
  647. if ( parseInt( component.get_selected_product( check_scenarios ), 10 ) === parseInt( this.bundle_id, 10 ) && component.get_selected_quantity() > 0 && component.is_visible() ) {
  648. is_valid = this.passes_validation();
  649. }
  650. }
  651.  
  652. return is_valid;
  653. };
  654.  
  655. /**
  656. * Adds validation messages to the component containing this bundle.
  657. */
  658. this.cp_validation_messages_action = function( step, is_valid ) {
  659.  
  660. if ( step.step_id === this.composite_data.component.step_id && false === is_valid && parseInt( step.get_selected_product(), 10 ) === parseInt( this.bundle_id, 10 ) ) {
  661.  
  662. var validation_messages = this.get_validation_messages();
  663.  
  664. $.each( validation_messages, function( index, message ) {
  665. step.add_validation_message( message );
  666. step.add_validation_message( message, 'composite' );
  667. } );
  668. }
  669. };
  670.  
  671. /**
  672. * WC front-end ajax URL.
  673. */
  674. this.get_ajax_url = function( action ) {
  675.  
  676. return woocommerce_params.wc_ajax_url.toString().replace( '%%endpoint%%', action );
  677. };
  678.  
  679. /**
  680. * Handler for viewport resizing.
  681. */
  682. this.viewport_resized = function() {
  683.  
  684. var form_width = this.$bundle_form.width();
  685.  
  686. if ( form_width <= wc_bundle_params.responsive_breakpoint ) {
  687. this.$bundle_form.addClass( 'small_width' );
  688. } else {
  689. this.$bundle_form.removeClass( 'small_width' );
  690. }
  691. }
  692.  
  693. /**
  694. * Attach bundle-level event handlers.
  695. */
  696. this.bind_event_handlers = function() {
  697.  
  698. // Add responsive class to bundle form.
  699. $( window ).resize( function() {
  700.  
  701. clearTimeout( bundle.viewport_resize_timer );
  702.  
  703. bundle.viewport_resize_timer = setTimeout( function() {
  704. bundle.viewport_resized();
  705. }, 50 );
  706. } );
  707.  
  708. // PAO compatibility.
  709. if ( bundle.has_addons() ) {
  710. bundle.$bundle_data.on( 'updated_addons', bundle.updated_addons_handler );
  711. }
  712.  
  713. // CP compatibility.
  714. if ( bundle.is_composited() ) {
  715. bundle.$bundle_quantity.on( 'input change', function() {
  716. bundle.update_bundle();
  717. } );
  718. }
  719.  
  720. this.$bundle_data
  721.  
  722. // NYP compatibility.
  723. .on( 'woocommerce-nyp-updated-item', function( event ) {
  724.  
  725. if ( bundle.$nyp.is( ':visible' ) ) {
  726.  
  727. bundle.price_data[ 'base_price' ] = bundle.$nyp.data( 'price' );
  728.  
  729. if ( bundle.is_initialized ) {
  730. bundle.dirty_subtotals = true;
  731. bundle.update_totals();
  732. }
  733. }
  734.  
  735. event.stopPropagation();
  736. } )
  737.  
  738. .on( 'woocommerce-product-bundle-validation-status-changed', function( event, bundle ) {
  739. bundle.updated_totals();
  740. } )
  741.  
  742. .on( 'click', '.bundle_add_to_cart_button', function( event ) {
  743.  
  744. if ( $( this ).hasClass( 'disabled' ) ) {
  745.  
  746. event.preventDefault();
  747. window.alert( wc_bundle_params.i18n_validation_alert );
  748.  
  749. } else {
  750.  
  751. $.each( bundle.bundled_items, function( index, bundled_item ) {
  752.  
  753. if ( bundled_item.has_required_addons() && bundled_item.is_optional() && false === bundled_item.is_selected() ) {
  754. bundled_item.$required_addons.prop( 'required', false );
  755. }
  756. } );
  757. }
  758. } )
  759.  
  760. .on( 'woocommerce-product-bundle-update-totals', function( event, force, _bundle ) {
  761.  
  762. var target_bundle = typeof( _bundle ) === 'undefined' ? bundle : _bundle;
  763.  
  764. force = typeof( force ) === 'undefined' ? false : force;
  765.  
  766. if ( force ) {
  767. target_bundle.dirty_subtotals = true;
  768. }
  769.  
  770. target_bundle.update_totals();
  771. } );
  772. };
  773.  
  774. /**
  775. * Initialize bundled item objects.
  776. */
  777. this.init_bundled_items = function() {
  778.  
  779. bundle.$bundled_items.each( function( index ) {
  780.  
  781. bundle.bundled_items[ index ] = new WC_PB_Bundled_Item( bundle, $( this ), index );
  782.  
  783. bundle.bind_bundled_item_event_handlers( bundle.bundled_items[ index ] );
  784. } );
  785. };
  786.  
  787. /**
  788. * Attach bundled-item-level event handlers.
  789. */
  790. this.bind_bundled_item_event_handlers = function( bundled_item ) {
  791.  
  792. bundled_item.$self
  793.  
  794. /**
  795. * Update totals upon changing quantities.
  796. */
  797. .on( 'input change', 'input.bundled_qty', function( e ) {
  798.  
  799. var $input = $( this ),
  800. min = parseFloat( $input.attr( 'min' ) ),
  801. max = parseFloat( $input.attr( 'max' ) );
  802.  
  803. if ( wc_bundle_params.force_min_max_qty_input === 'yes' ) {
  804.  
  805. if ( e.type === 'change' && min >= 0 && ( parseFloat( $input.val() ) < min || isNaN( parseFloat( $input.val() ) ) ) ) {
  806. $input.val( min );
  807. }
  808.  
  809. if ( e.type === 'change' && max > 0 && parseFloat( $input.val() ) > max ) {
  810. $input.val( max );
  811. }
  812. }
  813.  
  814. // A zero quantity item is considered optional by NYP.
  815. if( bundled_item.is_nyp() && ! bundled_item.is_optional() && min === 0 ) {
  816. bundled_item.$nyp.data( 'optional_status', parseFloat( $input.val() ) > 0 ? true : false );
  817. }
  818.  
  819. bundled_item.update_selection_title();
  820. bundle.update_bundle( bundled_item );
  821. } )
  822.  
  823. .on( 'change', '.bundled_product_optional_checkbox input', function( event ) {
  824.  
  825. if ( $( this ).is( ':checked' ) ) {
  826.  
  827. bundled_item.$bundled_item_content.css( {
  828. height: '',
  829. display: 'block',
  830. position: 'absolute',
  831. } );
  832.  
  833. var height = bundled_item.$bundled_item_content.get( 0 ).getBoundingClientRect().height;
  834.  
  835. if ( typeof height === 'undefined' ) {
  836. height = bundled_item.$bundled_item_content.outerHeight();
  837. }
  838.  
  839. bundled_item.$bundled_item_content.css( {
  840. height: '',
  841. position: '',
  842. display: 'none'
  843. } );
  844.  
  845. if ( height ) {
  846. bundled_item.$bundled_item_content.addClass( 'bundled_item_cart_content--populated' );
  847. bundled_item.$bundled_item_content.slideDown( 200 );
  848. }
  849.  
  850. bundled_item.set_selected( true );
  851.  
  852. // Tabular mini-extension compat.
  853. bundled_item.$self.find( '.bundled_item_qty_col .quantity' ).removeClass( 'quantity_hidden' );
  854.  
  855. if( bundled_item.is_nyp() ) {
  856. bundled_item.$nyp.trigger( 'wc-nyp-update', [ { 'force': true } ] );
  857. }
  858.  
  859. // Allow variations script to flip images in bundled_product_images div.
  860. bundled_item.$bundled_item_cart.find( '.variations select:eq(0)' ).trigger( 'change' );
  861.  
  862. } else {
  863.  
  864. bundled_item.$bundled_item_content.slideUp( 200 );
  865. bundled_item.set_selected( false );
  866.  
  867. // Tabular mini-extension compat.
  868. bundled_item.$self.find( '.bundled_item_qty_col .quantity' ).addClass( 'quantity_hidden' );
  869.  
  870. if ( ! bundled_item.has_single_variation() ) {
  871. // Reset image in bundled_product_images div.
  872. bundled_item.maybe_add_wc_core_gallery_class();
  873. bundled_item.$bundled_item_cart.trigger( 'reset_image' );
  874. bundled_item.maybe_remove_wc_core_gallery_class();
  875. }
  876. }
  877.  
  878. bundled_item.update_selection_title();
  879. bundle.update_bundle( bundled_item );
  880.  
  881. event.stopPropagation();
  882. } )
  883.  
  884. .on( 'found_variation', function( event, variation ) {
  885.  
  886. bundled_item.variation_id = variation.variation_id.toString();
  887.  
  888. // Put variation price data in price table.
  889. bundle.price_data[ 'prices' ][ bundled_item.bundled_item_id ] = Number( variation.price );
  890. bundle.price_data[ 'regular_prices' ][ bundled_item.bundled_item_id ] = Number( variation.regular_price );
  891.  
  892. bundle.price_data[ 'prices_tax' ][ bundled_item.bundled_item_id ] = variation.price_tax;
  893.  
  894. // Put variation recurring component data in price table.
  895. bundle.price_data[ 'recurring_prices' ][ bundled_item.bundled_item_id ] = Number( variation.recurring_price );
  896. bundle.price_data[ 'regular_recurring_prices' ][ bundled_item.bundled_item_id ] = Number( variation.regular_recurring_price );
  897.  
  898. bundle.price_data[ 'recurring_html' ][ bundled_item.bundled_item_id ] = variation.recurring_html;
  899. bundle.price_data[ 'recurring_keys' ][ bundled_item.bundled_item_id ] = variation.recurring_key;
  900.  
  901. // Remove .images class from bundled_product_images div in order to avoid styling issues.
  902. bundled_item.maybe_remove_wc_core_gallery_class();
  903.  
  904. // Tabular mini-extension compat.
  905. var $tabular_qty = bundled_item.$self.find( '.bundled_item_qty_col .quantity input' );
  906.  
  907. if ( $tabular_qty.length > 0 ) {
  908. $tabular_qty.attr( 'min', variation.min_qty ).attr( 'max', variation.max_qty );
  909. }
  910.  
  911. if ( variation.is_in_stock ) {
  912. // Ensure min/max values are always honored.
  913. bundled_item.$bundled_item_qty.trigger( 'change' );
  914. }
  915.  
  916. // If the bundled item is optional and not selected, reset the variable product image.
  917. if ( bundled_item.is_optional() && ! bundled_item.has_single_variation() && ! bundled_item.$self.find( '.bundled_product_optional_checkbox input' ).is( ':checked' ) ) {
  918. bundled_item.maybe_add_wc_core_gallery_class();
  919. bundled_item.$bundled_item_cart.trigger( 'reset_image' );
  920. bundled_item.maybe_remove_wc_core_gallery_class();
  921. }
  922.  
  923. bundled_item.update_selection_title();
  924. bundle.update_bundle( bundled_item );
  925.  
  926. event.stopPropagation();
  927. } )
  928.  
  929. .on( 'reset_image', function() {
  930. // Remove .images class from bundled_product_images div in order to avoid styling issues.
  931. bundled_item.maybe_remove_wc_core_gallery_class();
  932.  
  933. } )
  934.  
  935. .on( 'woocommerce-product-addons-update', function( event ) {
  936. event.stopPropagation();
  937. } )
  938.  
  939. .on( 'woocommerce_variation_select_focusin', function( event ) {
  940. event.stopPropagation();
  941. } )
  942.  
  943. .on( 'woocommerce_variation_has_changed', function( event ) {
  944.  
  945. if ( bundled_item.$reset_bundled_variations ) {
  946. if ( bundled_item.variation_id ) {
  947. bundled_item.$reset_bundled_variations.slideDown( 200 );
  948. } else {
  949. bundled_item.$reset_bundled_variations.slideUp( 200 );
  950. }
  951. }
  952.  
  953. event.stopPropagation();
  954. } )
  955.  
  956. .on( 'woocommerce_variation_select_change', function( event ) {
  957.  
  958. bundled_item.variation_id = '';
  959.  
  960. // Add .images class to bundled_product_images div ( required by the variations script to flip images ).
  961. if ( bundled_item.is_selected() ) {
  962. bundled_item.maybe_add_wc_core_gallery_class();
  963. }
  964.  
  965. if ( bundled_item.$attribute_select ) {
  966. bundled_item.$attribute_select.each( function() {
  967.  
  968. if ( $( this ).val() === '' ) {
  969.  
  970. // Prevent from appearing as out of stock.
  971. bundled_item.$bundled_item_cart.find( '.bundled_item_wrap .stock' ).addClass( 'disabled' );
  972.  
  973. bundle.update_bundle( bundled_item );
  974. return false;
  975. }
  976. } );
  977. }
  978.  
  979. event.stopPropagation();
  980.  
  981. } );
  982.  
  983.  
  984. if ( bundled_item.has_addons() ) {
  985.  
  986. bundled_item.$bundled_item_cart
  987.  
  988. /**
  989. * Calculate taxes and render addons totals on the client side.
  990. * We already prevented Add-ons from firing an ajax request in 'WC_PB_Bundled_Item'.
  991. */
  992. .on( 'updated_addons', function( event ) {
  993.  
  994. var addons_price = bundle.get_addons_raw_price( bundled_item ),
  995. regular_addons_price = bundle.has_pct_addons( bundled_item ) ? bundle.get_addons_raw_price( bundled_item, 'regular' ) : addons_price;
  996.  
  997. if ( bundle.price_data.addons_prices[ bundled_item.bundled_item_id ] !== addons_price || bundle.price_data.regular_addons_prices[ bundled_item.bundled_item_id ] !== regular_addons_price ) {
  998. bundle.price_data.addons_prices[ bundled_item.bundled_item_id ] = addons_price;
  999. bundle.price_data.regular_addons_prices[ bundled_item.bundled_item_id ] = regular_addons_price;
  1000. bundle.update_bundle( bundled_item );
  1001. }
  1002.  
  1003. if ( bundled_item.show_addons_totals ) {
  1004.  
  1005. var qty = bundled_item.get_quantity(),
  1006. tax_ratios = bundle.price_data.prices_tax[ bundled_item.bundled_item_id ],
  1007. addons_totals = bundle.get_taxed_totals( addons_price, addons_price, tax_ratios, qty );
  1008.  
  1009. if ( addons_totals.price > 0 ) {
  1010.  
  1011. var price = Number( bundle.price_data.prices[ bundled_item.bundled_item_id ] ),
  1012. total = price + Number( addons_price ),
  1013. totals = bundle.get_taxed_totals( total, total, tax_ratios, qty ),
  1014. price_html = wc_pb_price_format( totals.price ),
  1015. price_html_suffix = bundle.get_formatted_price_suffix( bundle.price_data, totals ),
  1016. addons_totals_html = '<span class="price">' + '<span class="subtotal">' + wc_bundle_params.i18n_subtotal + '</span>' + price_html + price_html_suffix + '</span>';
  1017.  
  1018. // Save for later use.
  1019. bundled_item.addons_totals_html = addons_totals_html;
  1020.  
  1021. bundled_item.$addons_totals.html( addons_totals_html ).slideDown( 200 );
  1022.  
  1023. } else {
  1024.  
  1025. // Put totals html back in as PAO just emptied the div.
  1026. bundled_item.$addons_totals.html( bundled_item.addons_totals_html ).slideUp( 200 );
  1027. }
  1028. }
  1029.  
  1030. event.stopPropagation();
  1031.  
  1032. } );
  1033. }
  1034.  
  1035. if ( bundled_item.is_nyp() ) {
  1036.  
  1037. bundled_item.$bundled_item_cart
  1038.  
  1039. .on( 'woocommerce-nyp-updated-item', function( event ) {
  1040.  
  1041. if ( bundled_item.$nyp.is( ':visible' ) ) {
  1042.  
  1043. var nyp_price = bundled_item.$nyp.data( 'price' );
  1044.  
  1045. bundle.price_data[ 'prices' ][ bundled_item.bundled_item_id ] = nyp_price;
  1046. bundle.price_data[ 'regular_prices' ][ bundled_item.bundled_item_id ] = nyp_price;
  1047.  
  1048. bundle.update_bundle( bundled_item );
  1049. }
  1050.  
  1051. event.stopPropagation();
  1052.  
  1053. } );
  1054. }
  1055. };
  1056.  
  1057. /**
  1058. * Returns the quantity of this bundle.
  1059. */
  1060. this.get_quantity = function() {
  1061. var qty = bundle.$bundle_quantity.length > 0 ? bundle.$bundle_quantity.val() : 1;
  1062. return isNaN( qty ) ? 1 : parseInt( qty, 10 );
  1063. };
  1064.  
  1065. /**
  1066. * Schedules an update of the bundle totals.
  1067. */
  1068. this.update_bundle = function( triggered_by ) {
  1069.  
  1070. clearTimeout( bundle.update_bundle_timer );
  1071.  
  1072. bundle.update_bundle_timer = setTimeout( function() {
  1073. bundle.update_bundle_task( triggered_by );
  1074. }, 5 );
  1075. };
  1076.  
  1077. /**
  1078. * Updates the bundle totals.
  1079. */
  1080. this.update_bundle_task = function( triggered_by ) {
  1081.  
  1082. var out_of_stock_found = false,
  1083. $overridden_stock_status = false,
  1084. validation_status = false === bundle.is_initialized ? '' : bundle.api.get_bundle_validation_status(),
  1085. unset_count = 0,
  1086. unset_titles = [],
  1087. total_items_qty = 0,
  1088. nyp_error_count = 0,
  1089. nyp_error_titles = [];
  1090.  
  1091. /*
  1092. * Validate bundle.
  1093. */
  1094.  
  1095. // Reset validation messages.
  1096. bundle.validation_messages = [];
  1097.  
  1098. // Validate bundled items and prepare price data for totals calculation.
  1099. $.each( bundle.bundled_items, function( index, bundled_item ) {
  1100.  
  1101. var bundled_item_qty = bundled_item.is_selected() ? bundled_item.get_quantity() : 0;
  1102.  
  1103. // Add item qty to total.
  1104. total_items_qty += bundled_item_qty;
  1105.  
  1106. // Check variable products.
  1107. if ( ( bundled_item.get_product_type() === 'variable' || bundled_item.get_product_type() === 'variable-subscription' ) && bundled_item.get_variation_id() === '' ) {
  1108. if ( bundled_item_qty > 0 ) {
  1109. unset_count++;
  1110. if ( bundled_item.is_visible() && bundled_item.get_title( true ) ) {
  1111. unset_titles.push( bundled_item.get_title( true ) );
  1112. }
  1113. }
  1114. }
  1115.  
  1116. // Check NYP validity.
  1117. if( bundled_item.is_nyp() && ! bundled_item.is_nyp_valid() ) {
  1118. nyp_error_count++;
  1119. if ( bundled_item.is_visible() && bundled_item.get_title( true ) ) {
  1120. nyp_error_titles.push( bundled_item.get_title( true ) );
  1121. }
  1122. }
  1123.  
  1124. } );
  1125.  
  1126. if ( unset_count > 0 ) {
  1127.  
  1128. var select_options_message = '';
  1129.  
  1130. if ( unset_count === unset_titles.length && unset_count < 5 ) {
  1131. select_options_message = wc_bundle_params.i18n_select_options_for.replace( '%s', wc_pb_format_list( unset_titles ) );
  1132. } else {
  1133. select_options_message = wc_bundle_params.i18n_select_options;
  1134. }
  1135.  
  1136. bundle.add_validation_message( select_options_message );
  1137. }
  1138.  
  1139. if ( nyp_error_count > 0 ) {
  1140.  
  1141. var nyp_amount_message = '';
  1142.  
  1143. if ( nyp_error_count === nyp_error_titles.length && nyp_error_count < 5 ) {
  1144. nyp_amount_message = wc_bundle_params.i18n_enter_valid_price_for.replace( '%s', wc_pb_format_list( nyp_error_titles ) );
  1145. } else {
  1146. nyp_amount_message = wc_bundle_params.i18n_enter_valid_price;
  1147. }
  1148.  
  1149. bundle.add_validation_message( nyp_amount_message );
  1150. }
  1151.  
  1152. if ( 0 === total_items_qty && 'no' === bundle.price_data[ 'zero_items_allowed' ] ) {
  1153. bundle.add_validation_message( wc_bundle_params.i18n_zero_qty_error );
  1154. }
  1155.  
  1156. // Bundle not purchasable?
  1157. if ( bundle.price_data[ 'is_purchasable' ] !== 'yes' ) {
  1158. // Show 'i18n_unavailable_text' message.
  1159. bundle.add_validation_message( wc_bundle_params.i18n_unavailable_text );
  1160. } else {
  1161. // Validate 3rd party constraints.
  1162. bundle.$bundle_data.triggerHandler( 'woocommerce-product-bundle-validate', [ bundle ] );
  1163. }
  1164.  
  1165. // Validation status changed?
  1166. if ( validation_status !== bundle.api.get_bundle_validation_status() ) {
  1167. bundle.$bundle_data.triggerHandler( 'woocommerce-product-bundle-validation-status-changed', [ bundle ] );
  1168. }
  1169.  
  1170. /*
  1171. * Calculate totals.
  1172. */
  1173.  
  1174. if ( bundle.price_data[ 'is_purchasable' ] === 'yes' ) {
  1175. bundle.update_totals( triggered_by );
  1176. }
  1177.  
  1178. /*
  1179. * Validation result handling.
  1180. */
  1181.  
  1182. if ( bundle.passes_validation() ) {
  1183.  
  1184. // Check if any item is out of stock.
  1185. $.each( bundle.bundled_items, function( index, bundled_item ) {
  1186.  
  1187. if ( ! bundled_item.is_selected() ) {
  1188. return true;
  1189. }
  1190.  
  1191. var $item_stock_p = bundled_item.$bundled_item_cart.find( 'p.stock:not(.disabled)' );
  1192.  
  1193. if ( $item_stock_p.hasClass( 'out-of-stock' ) && bundle.price_data[ 'quantities' ][ bundled_item.bundled_item_id ] > 0 ) {
  1194. out_of_stock_found = true;
  1195. }
  1196.  
  1197. } );
  1198.  
  1199. // Show add-to-cart button.
  1200. if ( out_of_stock_found ) {
  1201. bundle.$bundle_button.find( 'button' ).addClass( 'disabled' );
  1202. } else {
  1203. bundle.$bundle_button.find( 'button' ).removeClass( 'disabled' );
  1204. }
  1205.  
  1206. // Hide validation messages.
  1207. setTimeout( function() {
  1208. bundle.$bundle_error.slideUp( 200 );
  1209. }, 1 );
  1210.  
  1211. bundle.$bundle_wrap.trigger( 'woocommerce-product-bundle-show' );
  1212.  
  1213. } else {
  1214.  
  1215. bundle.hide_bundle();
  1216. }
  1217.  
  1218. /**
  1219. * Override bundle availability.
  1220. */
  1221. $.each( bundle.bundled_items, function( index, bundled_item ) {
  1222.  
  1223. if ( ! bundled_item.is_selected() ) {
  1224. return true;
  1225. }
  1226.  
  1227. var $item_stock_p = bundled_item.$bundled_item_cart.find( 'p.stock:not(.disabled)' );
  1228.  
  1229. if ( $item_stock_p.hasClass( 'out-of-stock' ) && bundle.price_data[ 'quantities' ][ bundled_item.bundled_item_id ] > 0 ) {
  1230. $overridden_stock_status = $item_stock_p.clone().html( wc_bundle_params.i18n_partially_out_of_stock );
  1231. }
  1232.  
  1233. if ( ! out_of_stock_found && $item_stock_p.hasClass( 'available-on-backorder' ) && bundle.price_data[ 'quantities' ][ bundled_item.bundled_item_id ] > 0 ) {
  1234. $overridden_stock_status = $item_stock_p.clone().html( wc_bundle_params.i18n_partially_on_backorder );
  1235. }
  1236.  
  1237. } );
  1238.  
  1239. if ( $overridden_stock_status ) {
  1240. bundle.$bundle_availability.html( $overridden_stock_status );
  1241. bundle.$bundle_availability.slideDown( 200 );
  1242. } else {
  1243. if ( bundle.$initial_stock_status ) {
  1244. bundle.$bundle_availability.html( bundle.$initial_stock_status );
  1245. } else {
  1246. if ( bundle.is_composited() ) {
  1247. bundle.$bundle_availability.find( 'p.stock' ).addClass( 'inactive' );
  1248. }
  1249. bundle.$bundle_availability.slideUp( 200 );
  1250. }
  1251. }
  1252.  
  1253. // If composited, run 'component_selection_content_changed' action to update all models/views.
  1254. if ( bundle.is_composited() ) {
  1255.  
  1256. // CP > 4.0+.
  1257. if ( typeof bundle.composite_data.component.component_selection_model.set_stock_status === 'function' ) {
  1258. bundle.composite_data.component.component_selection_model.set_stock_status( out_of_stock_found ? 'out-of-stock' : 'in-stock' );
  1259. }
  1260.  
  1261. bundle.composite_data.composite.actions.do_action( 'component_selection_content_changed', [ bundle.composite_data.component ] );
  1262. }
  1263.  
  1264. bundle.$bundle_data.trigger( 'woocommerce-product-bundle-updated', [ bundle ] );
  1265. };
  1266.  
  1267. /**
  1268. * Hide the add-to-cart button and show validation messages.
  1269. */
  1270. this.hide_bundle = function( hide_message ) {
  1271.  
  1272. var messages = $( '<ul/>' );
  1273.  
  1274. if ( typeof( hide_message ) === 'undefined' ) {
  1275.  
  1276. var hide_messages = bundle.get_validation_messages();
  1277.  
  1278. if ( hide_messages.length > 0 ) {
  1279. $.each( hide_messages, function( i, message ) {
  1280. messages.append( $( '<li/>' ).html( message ) );
  1281. } );
  1282. } else {
  1283. messages.append( $( '<li/>' ).html( wc_bundle_params.i18n_unavailable_text ) );
  1284. }
  1285.  
  1286. } else {
  1287. messages.append( $( '<li/>' ).html( hide_message.toString() ) );
  1288. }
  1289.  
  1290. bundle.$bundle_error_content.html( messages.html() );
  1291. setTimeout( function() {
  1292. bundle.$bundle_error.slideDown( 200 );
  1293. }, 1 );
  1294. bundle.$bundle_button.find( 'button' ).addClass( 'disabled' );
  1295.  
  1296. bundle.$bundle_wrap.trigger( 'woocommerce-product-bundle-hide' );
  1297. };
  1298.  
  1299. /**
  1300. * Updates the 'price_data' property with the latest values.
  1301. */
  1302. this.update_price_data = function() {
  1303.  
  1304. $.each( bundle.bundled_items, function( index, bundled_item ) {
  1305.  
  1306. var cart = bundled_item.$bundled_item_cart,
  1307. bundled_item_id = bundled_item.bundled_item_id,
  1308. item_quantity = bundled_item.get_quantity();
  1309.  
  1310. bundle.price_data[ 'quantities' ][ bundled_item_id ] = 0;
  1311.  
  1312. // Set quantity based on optional flag.
  1313. if ( bundled_item.is_selected() && item_quantity > 0 ) {
  1314. bundle.price_data[ 'quantities' ][ bundled_item_id ] = parseInt( item_quantity, 10 );
  1315. }
  1316.  
  1317. // Store quantity for easy access by 3rd parties.
  1318. cart.data( 'quantity', bundle.price_data[ 'quantities' ][ bundled_item_id ] );
  1319.  
  1320. // Check variable products.
  1321. if ( ( bundled_item.get_product_type() === 'variable' || bundled_item.get_product_type() === 'variable-subscription' ) && bundled_item.get_variation_id() === '' ) {
  1322. bundle.price_data[ 'prices' ][ bundled_item_id ] = 0.0;
  1323. bundle.price_data[ 'regular_prices' ][ bundled_item_id ] = 0.0;
  1324. bundle.price_data[ 'prices_tax' ][ bundled_item_id ] = false;
  1325. }
  1326.  
  1327. // Cast amounts.
  1328. bundle.price_data[ 'prices' ][ bundled_item_id ] = Number( bundle.price_data[ 'prices' ][ bundled_item_id ] );
  1329. bundle.price_data[ 'regular_prices' ][ bundled_item_id ] = Number( bundle.price_data[ 'regular_prices' ][ bundled_item_id ] );
  1330.  
  1331. bundle.price_data[ 'addons_prices' ][ bundled_item_id ] = Number( bundle.price_data[ 'addons_prices' ][ bundled_item_id ] );
  1332. bundle.price_data[ 'regular_addons_prices' ][ bundled_item_id ] = Number( bundle.price_data[ 'regular_addons_prices' ][ bundled_item_id ] );
  1333.  
  1334. bundle.price_data[ 'recurring_prices' ][ bundled_item_id ] = Number( bundle.price_data[ 'recurring_prices' ][ bundled_item_id ] );
  1335. bundle.price_data[ 'regular_recurring_prices' ][ bundled_item_id ] = Number( bundle.price_data[ 'regular_recurring_prices' ][ bundled_item_id ] );
  1336. } );
  1337. };
  1338.  
  1339. /**
  1340. * Calculates and updates bundle subtotals.
  1341. */
  1342. this.update_totals = function( triggered_by ) {
  1343.  
  1344. this.update_price_data();
  1345. this.calculate_subtotals( triggered_by );
  1346.  
  1347. if ( bundle.dirty_subtotals || false === bundle.is_initialized ) {
  1348. bundle.dirty_subtotals = false;
  1349. bundle.calculate_totals();
  1350. }
  1351. };
  1352.  
  1353. /**
  1354. * Calculates totals by applying tax ratios to raw prices.
  1355. */
  1356. this.get_taxed_totals = function( price, regular_price, tax_ratios, qty ) {
  1357.  
  1358. qty = typeof( qty ) === 'undefined' ? 1 : qty;
  1359.  
  1360. var tax_ratio_incl = tax_ratios && typeof( tax_ratios.incl ) !== 'undefined' ? Number( tax_ratios.incl ) : false,
  1361. tax_ratio_excl = tax_ratios && typeof( tax_ratios.excl ) !== 'undefined' ? Number( tax_ratios.excl ) : false,
  1362. totals = {
  1363. price: qty * price,
  1364. regular_price: qty * regular_price,
  1365. price_incl_tax: qty * price,
  1366. price_excl_tax: qty * price
  1367. };
  1368.  
  1369. if ( tax_ratio_incl && tax_ratio_excl ) {
  1370.  
  1371. totals.price_incl_tax = wc_pb_number_round( totals.price * tax_ratio_incl );
  1372. totals.price_excl_tax = wc_pb_number_round( totals.price * tax_ratio_excl );
  1373.  
  1374. if ( wc_bundle_params.tax_display_shop === 'incl' ) {
  1375. totals.price = totals.price_incl_tax;
  1376. totals.regular_price = wc_pb_number_round( totals.regular_price * tax_ratio_incl );
  1377. } else {
  1378. totals.price = totals.price_excl_tax;
  1379. totals.regular_price = wc_pb_number_round( totals.regular_price * tax_ratio_excl );
  1380. }
  1381. }
  1382.  
  1383. return totals;
  1384. };
  1385.  
  1386. /**
  1387. * Calculates bundled item subtotals (bundle totals) and updates the corresponding 'price_data' fields.
  1388. */
  1389. this.calculate_subtotals = function( triggered_by, price_data_array, qty ) {
  1390.  
  1391. var price_data = typeof( price_data_array ) === 'undefined' ? bundle.price_data : price_data_array;
  1392.  
  1393. qty = typeof( qty ) === 'undefined' ? 1 : parseInt( qty, 10 );
  1394. triggered_by = typeof( triggered_by ) === 'undefined' ? false : triggered_by;
  1395.  
  1396. // Base.
  1397. if ( false === triggered_by ) {
  1398.  
  1399. var base_price = Number( price_data[ 'base_price' ] ),
  1400. base_regular_price = Number( price_data[ 'base_regular_price' ] ),
  1401. base_price_tax_ratios = price_data[ 'base_price_tax' ];
  1402.  
  1403. price_data[ 'base_price_totals' ] = this.get_taxed_totals( base_price, base_regular_price, base_price_tax_ratios, qty );
  1404. }
  1405.  
  1406. // Items.
  1407. $.each( bundle.bundled_items, function( index, bundled_item ) {
  1408.  
  1409. if ( false !== triggered_by && triggered_by.bundled_item_id !== bundled_item.bundled_item_id ) {
  1410. return true;
  1411. }
  1412.  
  1413. var product_qty = bundled_item.is_sold_individually() && price_data[ 'quantities' ][ bundled_item.bundled_item_id ] > 0 ? 1 : price_data[ 'quantities' ][ bundled_item.bundled_item_id ] * qty,
  1414. product_id = bundled_item.get_product_type() === 'variable' ? bundled_item.get_variation_id() : bundled_item.get_product_id(),
  1415. tax_ratios = price_data[ 'prices_tax' ][ bundled_item.bundled_item_id ],
  1416. regular_price = price_data[ 'regular_prices' ][ bundled_item.bundled_item_id ] + price_data[ 'regular_addons_prices' ][ bundled_item.bundled_item_id ],
  1417. price = price_data[ 'prices' ][ bundled_item.bundled_item_id ] + price_data[ 'addons_prices' ][ bundled_item.bundled_item_id ],
  1418. regular_recurring_price = price_data[ 'regular_recurring_prices' ][ bundled_item.bundled_item_id ] + price_data[ 'regular_addons_prices' ][ bundled_item.bundled_item_id ],
  1419. recurring_price = price_data[ 'recurring_prices' ][ bundled_item.bundled_item_id ] + price_data[ 'addons_prices' ][ bundled_item.bundled_item_id ],
  1420. totals = {
  1421. price: 0.0,
  1422. regular_price: 0.0,
  1423. price_incl_tax: 0.0,
  1424. price_excl_tax: 0.0
  1425. },
  1426. recurring_totals = {
  1427. price: 0.0,
  1428. regular_price: 0.0,
  1429. price_incl_tax: 0.0,
  1430. price_excl_tax: 0.0
  1431. };
  1432.  
  1433. if ( wc_bundle_params.calc_taxes === 'yes' ) {
  1434.  
  1435. if ( product_id > 0 && product_qty > 0 ) {
  1436.  
  1437. if ( price > 0 || regular_price > 0 ) {
  1438. totals = bundle.get_taxed_totals( price, regular_price, tax_ratios, product_qty );
  1439. }
  1440.  
  1441. if ( recurring_price > 0 || regular_recurring_price > 0 ) {
  1442. recurring_totals = bundle.get_taxed_totals( recurring_price, regular_recurring_price, tax_ratios, product_qty );
  1443. }
  1444. }
  1445.  
  1446. } else {
  1447.  
  1448. totals.price = product_qty * price;
  1449. totals.regular_price = product_qty * regular_price;
  1450. totals.price_incl_tax = product_qty * price;
  1451. totals.price_excl_tax = product_qty * price;
  1452.  
  1453. recurring_totals.price = product_qty * recurring_price;
  1454. recurring_totals.regular_price = product_qty * regular_recurring_price;
  1455. recurring_totals.price_incl_tax = product_qty * recurring_price;
  1456. recurring_totals.price_excl_tax = product_qty * recurring_price;
  1457. }
  1458.  
  1459. // Filter bundled item totals.
  1460. totals = bundle.filters.apply_filters( 'bundled_item_totals', [ totals, bundled_item, qty ] );
  1461.  
  1462. // Filter bundled item totals.
  1463. recurring_totals = bundle.filters.apply_filters( 'bundled_item_recurring_totals', [ recurring_totals, bundled_item, qty ] );
  1464.  
  1465. if ( bundle.totals_changed( price_data[ 'bundled_item_' + bundled_item.bundled_item_id + '_totals' ], totals ) ) {
  1466. bundle.dirty_subtotals = true;
  1467. price_data[ 'bundled_item_' + bundled_item.bundled_item_id + '_totals' ] = totals;
  1468. }
  1469.  
  1470. if ( bundle.totals_changed( price_data[ 'bundled_item_' + bundled_item.bundled_item_id + '_recurring_totals' ], recurring_totals ) ) {
  1471. bundle.dirty_subtotals = true;
  1472. price_data[ 'bundled_item_' + bundled_item.bundled_item_id + '_recurring_totals' ] = recurring_totals;
  1473. }
  1474.  
  1475. } );
  1476.  
  1477. if ( typeof( price_data_array ) !== 'undefined' ) {
  1478. return price_data;
  1479. }
  1480. };
  1481.  
  1482. /**
  1483. * Adds bundle subtotals and calculates bundle totals.
  1484. */
  1485. this.calculate_totals = function( price_data_array ) {
  1486.  
  1487. if ( typeof( price_data_array ) === 'undefined' ) {
  1488. bundle.$bundle_data.triggerHandler( 'woocommerce-product-bundle-calculate-totals', [ bundle ] );
  1489. }
  1490.  
  1491. var price_data = typeof( price_data_array ) === 'undefined' ? bundle.price_data : price_data_array,
  1492. totals_changed = false;
  1493.  
  1494. // Non-recurring (sub)totals.
  1495. var subtotals, totals = {
  1496. price: price_data[ 'base_price_totals' ].price,
  1497. regular_price: price_data[ 'base_price_totals' ].regular_price,
  1498. price_incl_tax: price_data[ 'base_price_totals' ].price_incl_tax,
  1499. price_excl_tax: price_data[ 'base_price_totals' ].price_excl_tax
  1500. };
  1501.  
  1502. $.each( bundle.bundled_items, function( index, bundled_item ) {
  1503.  
  1504. if ( bundled_item.is_unavailable() ) {
  1505. return true;
  1506. }
  1507.  
  1508. var item_totals = price_data[ 'bundled_item_' + bundled_item.bundled_item_id + '_totals' ];
  1509.  
  1510. if ( typeof item_totals !== 'undefined' ) {
  1511.  
  1512. totals.price += item_totals.price;
  1513. totals.regular_price += item_totals.regular_price;
  1514. totals.price_incl_tax += item_totals.price_incl_tax;
  1515. totals.price_excl_tax += item_totals.price_excl_tax;
  1516. }
  1517.  
  1518. } );
  1519.  
  1520. // Recurring (sub)totals, grouped by recurring id.
  1521. var bundled_subs = bundle.get_bundled_subscriptions(),
  1522. recurring_totals = {};
  1523.  
  1524. if ( bundled_subs ) {
  1525.  
  1526. $.each( bundled_subs, function( index, bundled_sub ) {
  1527.  
  1528. var bundled_item_id = bundled_sub.bundled_item_id;
  1529.  
  1530. if ( price_data[ 'quantities' ][ bundled_item_id ] === 0 ) {
  1531. return true;
  1532. }
  1533.  
  1534. var recurring_key = price_data[ 'recurring_keys' ][ bundled_item_id ],
  1535. recurring_item_totals = price_data[ 'bundled_item_' + bundled_item_id + '_recurring_totals' ];
  1536.  
  1537. if ( typeof( recurring_totals[ recurring_key ] ) === 'undefined' ) {
  1538.  
  1539. recurring_totals[ recurring_key ] = {
  1540. html: price_data[ 'recurring_html' ][ bundled_item_id ],
  1541. price: recurring_item_totals.price,
  1542. regular_price: recurring_item_totals.regular_price,
  1543. price_incl_tax: recurring_item_totals.price_incl_tax,
  1544. price_excl_tax: recurring_item_totals.price_excl_tax
  1545. };
  1546.  
  1547. } else {
  1548.  
  1549. recurring_totals[ recurring_key ].price += recurring_item_totals.price;
  1550. recurring_totals[ recurring_key ].regular_price += recurring_item_totals.regular_price;
  1551. recurring_totals[ recurring_key ].price_incl_tax += recurring_item_totals.price_incl_tax;
  1552. recurring_totals[ recurring_key ].price_excl_tax += recurring_item_totals.price_excl_tax;
  1553. }
  1554.  
  1555. } );
  1556. }
  1557.  
  1558. subtotals = totals;
  1559.  
  1560. // Filter the totals.
  1561. totals = bundle.filters.apply_filters( 'bundle_totals', [ totals, price_data, bundle ] );
  1562.  
  1563. totals_changed = bundle.totals_changed( price_data[ 'totals' ], totals );
  1564.  
  1565. if ( ! totals_changed && bundled_subs ) {
  1566.  
  1567. var recurring_totals_pre = JSON.stringify( price_data[ 'recurring_totals' ] ),
  1568. reccuring_totals_post = JSON.stringify( recurring_totals );
  1569.  
  1570. if ( recurring_totals_pre !== reccuring_totals_post ) {
  1571. totals_changed = true;
  1572. }
  1573. }
  1574.  
  1575. // Render.
  1576. if ( totals_changed || false === bundle.is_initialized ) {
  1577.  
  1578. price_data[ 'subtotals' ] = subtotals;
  1579. price_data[ 'totals' ] = totals;
  1580. price_data[ 'recurring_totals' ] = recurring_totals;
  1581.  
  1582. if ( typeof( price_data_array ) === 'undefined' ) {
  1583. this.updated_totals();
  1584. }
  1585. }
  1586.  
  1587. return price_data;
  1588. };
  1589.  
  1590. /**
  1591. * Schedules a UI bundle price string refresh.
  1592. */
  1593. this.updated_totals = function() {
  1594.  
  1595. clearTimeout( bundle.update_price_timer );
  1596.  
  1597. bundle.update_price_timer = setTimeout( function() {
  1598. bundle.updated_totals_task();
  1599. }, 5 );
  1600. };
  1601.  
  1602. /**
  1603. * Build the non-recurring price html component.
  1604. */
  1605. this.get_price_html = function( price_data_array ) {
  1606.  
  1607. var price_data = typeof( price_data_array ) === 'undefined' ? bundle.price_data : price_data_array,
  1608. recalc_totals = false,
  1609. qty = bundle.is_composited() ? bundle.composite_data.component.get_selected_quantity() : 1,
  1610. tag = 'p';
  1611.  
  1612. if ( bundle.has_addons() ) {
  1613.  
  1614. price_data = $.extend( true, {}, price_data );
  1615. recalc_totals = true;
  1616.  
  1617. var addons_raw_price = price_data[ 'addons_price' ] ? price_data[ 'addons_price' ] : bundle.get_addons_raw_price(),
  1618. addons_raw_regular_price = price_data[ 'addons_regular_price' ] ? price_data[ 'addons_regular_price' ] : addons_raw_price;
  1619.  
  1620. // Recalculate price html with add-ons price embedded in base price.
  1621. if ( addons_raw_price > 0 ) {
  1622. price_data.base_price = Number( price_data.base_price ) + Number( addons_raw_price );
  1623. }
  1624.  
  1625. if ( addons_raw_regular_price > 0 ) {
  1626. price_data.base_regular_price = Number( price_data.base_regular_price ) + Number( addons_raw_regular_price );
  1627. }
  1628. }
  1629.  
  1630. if ( bundle.is_composited() ) {
  1631.  
  1632. tag = 'span';
  1633.  
  1634. if ( 'yes' === price_data.composited_totals_incl_qty ) {
  1635. recalc_totals = true;
  1636. }
  1637. }
  1638.  
  1639. if ( recalc_totals ) {
  1640. // Recalculate price html with qty embedded.
  1641. price_data = bundle.calculate_subtotals( false, price_data, qty );
  1642. price_data = bundle.calculate_totals( price_data );
  1643. }
  1644.  
  1645. var bundle_price_html = '',
  1646. total_string = 'yes' === price_data.show_total_string && wc_bundle_params.i18n_total ? '<span class="total">' + wc_bundle_params.i18n_total + '</span>' : '';
  1647.  
  1648. // Non-recurring price html data.
  1649. var formatted_price = price_data[ 'totals' ].price === 0.0 && price_data[ 'show_free_string' ] === 'yes' ? wc_bundle_params.i18n_free : wc_pb_price_format( price_data[ 'totals' ].price ),
  1650. formatted_regular_price = wc_pb_price_format( price_data[ 'totals' ].regular_price ),
  1651. formatted_suffix = bundle.get_formatted_price_suffix( price_data );
  1652.  
  1653. if ( price_data[ 'totals' ].regular_price > price_data[ 'totals' ].price ) {
  1654. formatted_price = wc_bundle_params.i18n_strikeout_price_string.replace( '%f', formatted_regular_price ).replace( '%t', formatted_price );
  1655. }
  1656.  
  1657. bundle_price_html = wc_bundle_params.i18n_price_format.replace( '%t', total_string ).replace( '%p', formatted_price ).replace( '%s', formatted_suffix );
  1658. bundle_price_html = '<' + tag + ' class="price">' + price_data[ 'price_string' ].replace( '%s', bundle_price_html ) + '</' + tag + '>';
  1659.  
  1660. return bundle_price_html;
  1661. };
  1662.  
  1663. /**
  1664. * Builds the recurring price html component for bundles that contain subscription products.
  1665. */
  1666. this.get_recurring_price_html = function( price_data_array ) {
  1667.  
  1668. var price_data = typeof( price_data_array ) === 'undefined' ? bundle.price_data : price_data_array;
  1669.  
  1670. var bundle_recurring_price_html = '',
  1671. bundled_subs = bundle.get_bundled_subscriptions();
  1672.  
  1673. if ( bundled_subs ) {
  1674.  
  1675. $.each( price_data[ 'recurring_totals' ], function( recurring_component_key, recurring_component_data ) {
  1676.  
  1677. var formatted_recurring_price = recurring_component_data.price == 0 ? wc_bundle_params.i18n_free : wc_pb_price_format( recurring_component_data.price ),
  1678. formatted_regular_recurring_price = wc_pb_price_format( recurring_component_data.regular_price ),
  1679. formatted_recurring_price_html = '',
  1680. formatted_suffix = bundle.get_formatted_price_suffix( price_data, {
  1681. price_incl_tax: recurring_component_data[ 'price_incl_tax' ],
  1682. price_excl_tax: recurring_component_data[ 'price_excl_tax' ]
  1683. } );
  1684.  
  1685. if ( recurring_component_data.regular_price > recurring_component_data.price ) {
  1686. formatted_recurring_price = wc_bundle_params.i18n_strikeout_price_string.replace( '%f', formatted_regular_recurring_price ).replace( '%t', formatted_recurring_price );
  1687. }
  1688.  
  1689. formatted_recurring_price_html = wc_bundle_params.i18n_price_format.replace( '%t', '' ).replace( '%p', formatted_recurring_price ).replace( '%s', formatted_suffix );
  1690. formatted_recurring_price_html = '<span class="bundled_sub_price_html">' + recurring_component_data.html.replace( '%s', formatted_recurring_price_html ) + '</span>';
  1691.  
  1692. bundle_recurring_price_html = ( bundle_recurring_price_html !== '' ? ( bundle_recurring_price_html + '<span class="plus"> + </span>' ) : bundle_recurring_price_html ) + formatted_recurring_price_html;
  1693. } );
  1694. }
  1695.  
  1696. return bundle_recurring_price_html;
  1697. };
  1698.  
  1699. /**
  1700. * Determines whether to show a bundle price html string.
  1701. */
  1702. this.show_price_html = function() {
  1703.  
  1704. var show_price = false;
  1705.  
  1706. if ( false === bundle.get_bundled_subscriptions() ) {
  1707. show_price = wc_pb_number_round( bundle.price_data[ 'totals' ].price ) !== wc_pb_number_round( bundle.price_data[ 'raw_bundle_price_min' ] ) || bundle.price_data[ 'raw_bundle_price_min' ] !== bundle.price_data[ 'raw_bundle_price_max' ];
  1708. } else {
  1709. $.each( bundle.bundled_items, function( index, bundled_item ) {
  1710. if ( bundle.price_data[ 'recurring_prices' ][ bundled_item.bundled_item_id ] > 0 ) {
  1711. if ( bundled_item.is_subscription( 'variable' ) || bundled_item.is_optional() || false === bundled_item.$self.find( '.quantity' ).hasClass( 'quantity_hidden' ) ) {
  1712. show_price = true;
  1713. return false;
  1714. }
  1715. }
  1716. } );
  1717. }
  1718.  
  1719. if ( show_price ) {
  1720. $.each( bundle.bundled_items, function( index, bundled_item ) {
  1721. if ( bundled_item.is_unavailable() && bundled_item.is_required() ) {
  1722. show_price = false;
  1723. return false;
  1724. }
  1725. } );
  1726. }
  1727.  
  1728. if ( bundle.is_composited() ) {
  1729.  
  1730. if ( ! show_price ) {
  1731. if ( bundle.composite_data.composite.api.is_component_priced_individually( this.composite_data.component.step_id ) ) {
  1732. show_price = true;
  1733. }
  1734. }
  1735.  
  1736. if ( show_price ) {
  1737. if ( false === this.composite_data.component.is_selected_product_price_visible() ) {
  1738. show_price = false;
  1739. } else if ( false === bundle.composite_data.composite.api.is_component_priced_individually( this.composite_data.component.step_id ) ) {
  1740. show_price = false;
  1741. }
  1742. }
  1743. }
  1744.  
  1745. return show_price;
  1746. };
  1747.  
  1748. /**
  1749. * Refreshes the bundle price string in the UI.
  1750. */
  1751. this.updated_totals_task = function() {
  1752.  
  1753. var show_price = bundle.show_price_html();
  1754.  
  1755. if ( bundle.passes_validation() && show_price ) {
  1756.  
  1757. var bundle_price_html = bundle.get_price_html(),
  1758. bundle_recurring_price_html = bundle.get_recurring_price_html();
  1759.  
  1760. bundle_price_html = bundle_price_html.replace( '%r', bundle_recurring_price_html );
  1761.  
  1762. // Pass the price string through a filter.
  1763. bundle_price_html = bundle.filters.apply_filters( 'bundle_total_price_html', [ bundle_price_html, bundle ] );
  1764.  
  1765. bundle.$bundle_price.html( bundle_price_html );
  1766.  
  1767. if ( bundle_recurring_price_html ) {
  1768. bundle.$bundle_price.find( '.bundled_subscriptions_price_html' ).show();
  1769. }
  1770.  
  1771. bundle.$bundle_price.slideDown( 200 );
  1772.  
  1773. } else {
  1774. bundle.$bundle_price.slideUp( 200 );
  1775. }
  1776.  
  1777. bundle.$bundle_data.triggerHandler( 'woocommerce-product-bundle-updated-totals', [ bundle ] );
  1778. };
  1779.  
  1780. this.updated_addons_handler = function( event ) {
  1781. bundle.updated_totals_task();
  1782. };
  1783.  
  1784. this.has_addons = function() {
  1785. return this.$addons_totals && this.$addons_totals.length > 0;
  1786. };
  1787.  
  1788. this.has_pct_addons = function( bundled_item ) {
  1789.  
  1790. var is_bundled_item = typeof( bundled_item ) !== 'undefined',
  1791. obj = is_bundled_item ? bundled_item : this,
  1792. has = false;
  1793.  
  1794. if ( ! obj.has_addons ) {
  1795. return has;
  1796. }
  1797.  
  1798. var addons = obj.$addons_totals.data( 'price_data' );
  1799.  
  1800. $.each( addons, function( i, addon ) {
  1801. if ( 'percentage_based' === addon.price_type ) {
  1802. has = true;
  1803. return false;
  1804. }
  1805.  
  1806. } );
  1807.  
  1808. return has;
  1809. };
  1810.  
  1811. this.get_addons_raw_price = function( bundled_item, price_prop ) {
  1812.  
  1813. var is_bundled_item = typeof( bundled_item ) !== 'undefined',
  1814. price_type = 'regular' === price_prop ? 'regular': '',
  1815. obj = is_bundled_item ? bundled_item : this,
  1816. qty = is_bundled_item ? bundled_item.get_quantity() : 1,
  1817. tax_ratios = is_bundled_item ? bundle.price_data.prices_tax[ bundled_item.bundled_item_id ] : bundle.price_data.base_price_tax,
  1818. addons_raw_price = 0.0;
  1819.  
  1820. if ( ! obj.has_addons() ) {
  1821. return 0;
  1822. }
  1823.  
  1824. if ( ! qty ) {
  1825. return 0;
  1826. }
  1827.  
  1828. if ( bundle.is_composited() ) {
  1829. qty = bundle.composite_data.component.get_selected_quantity();
  1830. }
  1831.  
  1832. var addons = obj.$addons_totals.data( 'price_data' );
  1833.  
  1834. $.each( addons, function( i, addon ) {
  1835.  
  1836. if ( addon.is_custom_price ) {
  1837.  
  1838. var addon_raw_price = 0.0,
  1839. tax_ratio_incl = tax_ratios && typeof( tax_ratios.incl ) !== 'undefined' ? Number( tax_ratios.incl ) : false,
  1840. tax_ratio_excl = tax_ratios && typeof( tax_ratios.excl ) !== 'undefined' ? Number( tax_ratios.excl ) : false;
  1841.  
  1842. if ( 'incl' === wc_bundle_params.tax_display_shop && 'no' === wc_bundle_params.prices_include_tax ) {
  1843. addon_raw_price = addon.cost_raw / ( tax_ratio_incl ? tax_ratio_incl : 1 );
  1844. } else if ( 'excl' === wc_bundle_params.tax_display_shop && 'yes' === wc_bundle_params.prices_include_tax ) {
  1845. addon_raw_price = addon.cost_raw / ( tax_ratio_excl ? tax_ratio_excl : 1 );
  1846. } else {
  1847. addon_raw_price = addon.cost_raw;
  1848. }
  1849.  
  1850. addons_raw_price += addon_raw_price / qty;
  1851.  
  1852. } else {
  1853.  
  1854. if ( 'quantity_based' === addon.price_type ) {
  1855. addons_raw_price += addon.cost_raw_pu;
  1856. } else if ( 'flat_fee' === addon.price_type ) {
  1857. addons_raw_price += addon.cost_raw / qty;
  1858. } else if ( 'percentage_based' === addon.price_type ) {
  1859.  
  1860. var raw_price;
  1861.  
  1862. if ( 'regular' === price_type ) {
  1863. raw_price = is_bundled_item ? bundle.price_data.regular_prices[ bundled_item.bundled_item_id ] : bundle.price_data.base_regular_price;
  1864. } else {
  1865. raw_price = is_bundled_item ? bundle.price_data.prices[ bundled_item.bundled_item_id ] : bundle.price_data.base_price;
  1866. }
  1867.  
  1868. addons_raw_price += addon.cost_raw_pct * raw_price;
  1869. }
  1870. }
  1871.  
  1872. } );
  1873.  
  1874. return addons_raw_price;
  1875. };
  1876.  
  1877. /**
  1878. * Comparison of totals.
  1879. */
  1880. this.totals_changed = function( totals_pre, totals_post ) {
  1881.  
  1882. if ( typeof( totals_pre ) === 'undefined' || totals_pre.price !== totals_post.price || totals_pre.regular_price !== totals_post.regular_price || totals_pre.price_incl_tax !== totals_post.price_incl_tax || totals_pre.price_excl_tax !== totals_post.price_excl_tax ) {
  1883. return true;
  1884. }
  1885.  
  1886. return false;
  1887. };
  1888.  
  1889. /**
  1890. * True if the bundle is part of a composite product.
  1891. */
  1892. this.is_composited = function() {
  1893. return false !== this.composite_data;
  1894. };
  1895.  
  1896. /**
  1897. * Replace totals in price suffix.
  1898. */
  1899. this.get_formatted_price_suffix = function( price_data_array, totals ) {
  1900.  
  1901. var price_data = typeof( price_data_array ) === 'undefined' ? bundle.price_data : price_data_array,
  1902. suffix = '';
  1903.  
  1904. totals = typeof( totals ) === 'undefined' ? price_data.totals : totals;
  1905.  
  1906. if ( price_data.suffix_exists ) {
  1907.  
  1908. suffix = price_data.suffix;
  1909.  
  1910. if ( price_data.suffix_contains_price_incl ) {
  1911. suffix = suffix.replace( '{price_including_tax}', wc_pb_price_format( totals.price_incl_tax ) );
  1912. }
  1913.  
  1914. if ( price_data.suffix_contains_price_excl ) {
  1915. suffix = suffix.replace( '{price_excluding_tax}', wc_pb_price_format( totals.price_excl_tax ) );
  1916. }
  1917. }
  1918.  
  1919. return suffix;
  1920. };
  1921.  
  1922. /**
  1923. * Find and return WC_PB_Bundled_Item objects that are subs.
  1924. */
  1925. this.get_bundled_subscriptions = function( type ) {
  1926.  
  1927. var bundled_subs = {},
  1928. has_sub = false;
  1929.  
  1930. $.each( bundle.bundled_items, function( index, bundled_item ) {
  1931.  
  1932. if ( bundled_item.is_subscription( type ) ) {
  1933.  
  1934. bundled_subs[ index ] = bundled_item;
  1935. has_sub = true;
  1936. }
  1937.  
  1938. } );
  1939.  
  1940. if ( has_sub ) {
  1941. return bundled_subs;
  1942. }
  1943.  
  1944. return false;
  1945. };
  1946.  
  1947. /**
  1948. * Adds a validation message.
  1949. */
  1950. this.add_validation_message = function( message ) {
  1951.  
  1952. this.validation_messages.push( message.toString() );
  1953. };
  1954.  
  1955. /**
  1956. * Validation messages getter.
  1957. */
  1958. this.get_validation_messages = function() {
  1959.  
  1960. return this.validation_messages;
  1961. };
  1962.  
  1963. /**
  1964. * Validation state getter.
  1965. */
  1966. this.passes_validation = function() {
  1967.  
  1968. if ( this.validation_messages.length > 0 ) {
  1969. return false;
  1970. }
  1971.  
  1972. return true;
  1973. };
  1974.  
  1975. /**
  1976. * Check group mode feature support.
  1977. */
  1978. this.group_mode_supports = function( $feature ) {
  1979. return $.inArray( $feature, this.price_data[ 'group_mode_features' ] ) > -1;
  1980. };
  1981. }
  1982.  
  1983. /**
  1984. * Bundled Item object.
  1985. */
  1986. function WC_PB_Bundled_Item( bundle, $bundled_item, index ) {
  1987.  
  1988. this.initialize = function() {
  1989.  
  1990. this.$self = $bundled_item;
  1991. this.$bundled_item_cart = $bundled_item.find( '.cart' );
  1992. this.$bundled_item_content = $bundled_item.find( '.bundled_item_optional_content, .bundled_item_cart_content' );
  1993. this.$bundled_item_image = $bundled_item.find( '.bundled_product_images' );
  1994. this.$bundled_item_title = $bundled_item.find( '.bundled_product_title_inner' );
  1995. this.$bundled_item_qty = $bundled_item.find( 'input.bundled_qty' );
  1996.  
  1997. this.$addons_totals = $bundled_item.find( '#product-addons-total' );
  1998. this.$required_addons = false;
  1999. this.$nyp = $bundled_item.find( '.nyp' );
  2000.  
  2001. this.$attribute_select = false;
  2002. this.$attribute_select_config = false;
  2003.  
  2004. this.$reset_bundled_variations = false;
  2005.  
  2006. this.show_addons_totals = false;
  2007. this.addons_totals_html = '';
  2008.  
  2009. this.bundled_item_index = index;
  2010. this.bundled_item_id = this.$bundled_item_cart.data( 'bundled_item_id' );
  2011. this.bundled_item_title = this.$bundled_item_cart.data( 'title' );
  2012. this.bundled_item_title_raw = this.bundled_item_title ? $( '<div/>' ).html( this.bundled_item_title ).text() : '';
  2013. this.bundled_item_product_title = this.$bundled_item_cart.data( 'product_title' );
  2014. this.bundled_item_product_title_raw = this.bundled_item_title ? $( '<div/>' ).html( this.bundled_item_title ).text() : '';
  2015. this.bundled_item_optional_suffix = typeof( this.$bundled_item_cart.data( 'optional_suffix' ) ) === 'undefined' ? wc_bundle_params.i18n_optional : this.$bundled_item_cart.data( 'optional_suffix' );
  2016.  
  2017. this.product_type = this.$bundled_item_cart.data( 'type' );
  2018. this.product_id = typeof( bundle.price_data[ 'product_ids' ][ this.bundled_item_id ] ) === 'undefined' ? '' : bundle.price_data[ 'product_ids' ][ this.bundled_item_id ].toString();
  2019. this.nyp = typeof( bundle.price_data[ 'product_ids' ][ this.bundled_item_id ] ) === 'undefined' ? false : bundle.price_data[ 'is_nyp' ][ this.bundled_item_id ] === 'yes';
  2020. this.sold_individually = typeof( bundle.price_data[ 'product_ids' ][ this.bundled_item_id ] ) === 'undefined' ? false : bundle.price_data[ 'is_sold_individually' ][ this.bundled_item_id ] === 'yes';
  2021. this.variation_id = '';
  2022.  
  2023. this.has_wc_core_gallery_class = this.$bundled_item_image.hasClass( 'images' );
  2024.  
  2025. if ( typeof( this.bundled_item_id ) === 'undefined' ) {
  2026. this.bundled_item_id = this.$bundled_item_cart.attr( 'data-bundled-item-id' );
  2027. }
  2028.  
  2029. this.initialize_addons();
  2030. };
  2031.  
  2032. this.initialize_addons = function() {
  2033.  
  2034. if ( this.has_addons() ) {
  2035.  
  2036. // Totals visible?
  2037. if ( 1 == this.$addons_totals.data( 'show-sub-total' ) ) {
  2038. // Ensure addons ajax is not triggered at all, as we calculate tax on the client side.
  2039. this.$addons_totals.data( 'show-sub-total', 0 );
  2040. this.show_addons_totals = true;
  2041. }
  2042.  
  2043. this.$required_addons = this.$bundled_item_cart.find( '.wc-pao-required-addon [required]' );
  2044.  
  2045. } else {
  2046. this.$addons_totals = false;
  2047. }
  2048. };
  2049.  
  2050. this.get_title = function( strip_tags ) {
  2051. strip_tags = typeof( strip_tags ) === 'undefined' ? false : strip_tags;
  2052. return strip_tags ? this.bundled_item_title_raw : this.bundled_item_title;
  2053. };
  2054.  
  2055. this.get_product_title = function( strip_tags ) {
  2056. strip_tags = typeof( strip_tags ) === 'undefined' ? false : strip_tags;
  2057. return strip_tags ? this.bundled_item_product_title_raw : this.bundled_item_product_title;
  2058. };
  2059.  
  2060. this.get_optional_suffix = function() {
  2061. return this.bundled_item_optional_suffix;
  2062. };
  2063.  
  2064. this.get_product_id = function() {
  2065. return this.product_id;
  2066. };
  2067.  
  2068. this.get_variation_id = function() {
  2069. return this.variation_id;
  2070. };
  2071.  
  2072. this.get_variation_data = function() {
  2073. return this.$bundled_item_cart.data( 'product_variations' );
  2074. };
  2075.  
  2076. this.get_product_type = function() {
  2077. return this.product_type;
  2078. };
  2079.  
  2080. this.get_quantity = function() {
  2081. var qty = this.$bundled_item_qty.val();
  2082. return isNaN( qty ) ? 0 : parseInt( qty, 10 );
  2083. };
  2084.  
  2085. this.is_optional = function() {
  2086. return ( this.$bundled_item_cart.data( 'optional' ) === 'yes' || this.$bundled_item_cart.data( 'optional' ) === 1 );
  2087. };
  2088.  
  2089. this.is_unavailable = function() {
  2090. return 'yes' === this.$bundled_item_cart.data( 'custom_data' ).is_unavailable;
  2091. };
  2092.  
  2093. this.is_required = function() {
  2094. return ! this.is_optional() && 'no' !== this.$bundled_item_cart.data( 'custom_data' ).is_required;
  2095. };
  2096.  
  2097. this.is_visible = function() {
  2098. return ( this.$bundled_item_cart.data( 'visible' ) === 'yes' || this.$bundled_item_cart.data( 'visible' ) === 1 );
  2099. };
  2100.  
  2101. this.is_selected = function() {
  2102.  
  2103. var selected = true;
  2104.  
  2105. if ( this.is_optional() ) {
  2106. if ( this.$bundled_item_cart.data( 'optional_status' ) === false ) {
  2107. selected = false;
  2108. }
  2109. }
  2110.  
  2111. return selected;
  2112. };
  2113.  
  2114. this.set_selected = function( status ) {
  2115.  
  2116. if ( this.is_optional() ) {
  2117. this.$bundled_item_cart.data( 'optional_status', status );
  2118.  
  2119. if( this.is_nyp() ) {
  2120. this.$nyp.data( 'optional_status', status );
  2121. }
  2122. }
  2123. };
  2124.  
  2125. this.init_scripts = function() {
  2126.  
  2127. // Init PhotoSwipe if present.
  2128. if ( typeof PhotoSwipe !== 'undefined' && 'yes' === wc_bundle_params.photoswipe_enabled ) {
  2129. this.init_photoswipe();
  2130. }
  2131.  
  2132. // Init dependencies.
  2133. this.$self.find( '.bundled_product_optional_checkbox input' ).change();
  2134. this.$self.find( 'input.bundled_qty' ).change();
  2135.  
  2136. if ( ( this.product_type === 'variable' || this.product_type === 'variable-subscription' ) && ! this.$bundled_item_cart.hasClass( 'variations_form' ) ) {
  2137.  
  2138. // Variations reset wrapper.
  2139. this.$reset_bundled_variations = this.$bundled_item_cart.find( '.reset_bundled_variations' );
  2140.  
  2141. if ( this.$reset_bundled_variations.length === 0 ) {
  2142. this.$reset_bundled_variations = false;
  2143. }
  2144.  
  2145. // Initialize variations script.
  2146. this.$bundled_item_cart.addClass( 'variations_form' ).wc_variation_form();
  2147.  
  2148. // Set cached selects.
  2149. this.$attribute_select = this.$bundled_item_cart.find( '.variations .attribute_options select' );
  2150. this.$attribute_select_config = this.$attribute_select.filter( function() {
  2151. return false === $( this ).parent().hasClass( 'bundled_variation_attribute_options_wrapper' );
  2152. } );
  2153.  
  2154. // Trigger change event.
  2155. if ( this.$attribute_select.length > 0 ) {
  2156. this.$attribute_select.first().change();
  2157. }
  2158. }
  2159.  
  2160. this.$self.find( 'div' ).stop( true, true );
  2161. this.update_selection_title();
  2162. };
  2163.  
  2164. this.init_photoswipe = function() {
  2165.  
  2166. this.$bundled_item_image.wc_product_gallery( { zoom_enabled: false, flexslider_enabled: false } );
  2167.  
  2168. var $placeholder = this.$bundled_item_image.find( 'a.placeholder_image' );
  2169.  
  2170. if ( $placeholder.length > 0 ) {
  2171. $placeholder.on( 'click', function() {
  2172. return false;
  2173. } );
  2174. }
  2175. };
  2176.  
  2177. this.update_selection_title = function( reset ) {
  2178.  
  2179. if ( this.$bundled_item_title.length === 0 ) {
  2180. return false;
  2181. }
  2182.  
  2183. var bundled_item_qty_val = parseInt( this.get_quantity(), 10 );
  2184.  
  2185. if ( isNaN( bundled_item_qty_val ) ) {
  2186. return false;
  2187. }
  2188.  
  2189. reset = typeof( reset ) === 'undefined' ? false : reset;
  2190.  
  2191. if ( reset ) {
  2192. bundled_item_qty_val = parseInt( this.$bundled_item_qty.attr( 'min' ), 10 );
  2193. }
  2194.  
  2195. var selection_title = this.bundled_item_title,
  2196. selection_qty_string = bundled_item_qty_val > 1 ? wc_bundle_params.i18n_qty_string.replace( '%s', bundled_item_qty_val ) : '',
  2197. selection_optional_string = ( this.is_optional() && this.get_optional_suffix() !== '' ) ? wc_bundle_params.i18n_optional_string.replace( '%s', this.get_optional_suffix() ) : '',
  2198. selection_title_incl_qty = wc_bundle_params.i18n_title_string.replace( '%t', selection_title ).replace( '%q', selection_qty_string ).replace( '%o', selection_optional_string );
  2199.  
  2200. this.$bundled_item_title.html( selection_title_incl_qty );
  2201. };
  2202.  
  2203. this.reset_selection_title = function() {
  2204. this.update_selection_title( true );
  2205. };
  2206.  
  2207. this.is_subscription = function( type ) {
  2208.  
  2209. if ( 'simple' === type ) {
  2210. return this.product_type === 'subscription';
  2211. } else if ( 'variable' === type ) {
  2212. return this.product_type === 'variable-subscription';
  2213. } else {
  2214. return this.product_type === 'subscription' || this.product_type === 'variable-subscription';
  2215. }
  2216. };
  2217.  
  2218. this.has_addons = function() {
  2219. return this.$addons_totals && this.$addons_totals.length > 0;
  2220. };
  2221.  
  2222. this.has_required_addons = function() {
  2223. return this.$required_addons && this.$required_addons.length > 0;
  2224. };
  2225.  
  2226. this.has_single_variation = function() {
  2227.  
  2228. if ( typeof this.get_variation_data() !== 'undefined' ) {
  2229. return 1 === this.get_variation_data().length;
  2230. }
  2231.  
  2232. return false;
  2233. };
  2234.  
  2235. this.is_nyp = function() {
  2236. return this.nyp;
  2237. };
  2238.  
  2239. this.is_nyp_valid = function() {
  2240.  
  2241. var status = true;
  2242.  
  2243. if ( $.fn.wc_nyp_get_script_object ) {
  2244.  
  2245. var nyp_script = this.$nyp.wc_nyp_get_script_object();
  2246.  
  2247. if ( nyp_script && false === nyp_script.isValid() ) {
  2248. status = false;
  2249. }
  2250. }
  2251.  
  2252. return status;
  2253.  
  2254. };
  2255.  
  2256. this.is_sold_individually = function() {
  2257. return this.sold_individually;
  2258. };
  2259.  
  2260. this.maybe_add_wc_core_gallery_class = function() {
  2261. if ( ! this.has_wc_core_gallery_class ) {
  2262. this.$bundled_item_image.addClass( 'images' );
  2263. }
  2264. };
  2265.  
  2266. this.maybe_remove_wc_core_gallery_class = function() {
  2267. if ( ! this.has_wc_core_gallery_class ) {
  2268. this.$bundled_item_image.removeClass( 'images' );
  2269. }
  2270. };
  2271.  
  2272. this.initialize();
  2273. }
  2274.  
  2275. /**
  2276. * Filters API.
  2277. */
  2278. function WC_PB_Filters_Manager() {
  2279.  
  2280. /*
  2281. *--------------------------*
  2282. * *
  2283. * Filters Reference *
  2284. * *
  2285. *--------------------------*
  2286. *
  2287. *
  2288. * Filter 'bundle_subtotals_data':
  2289. *
  2290. * Filters the bundle price data array after calculating subtotals.
  2291. *
  2292. * @param array price_data Price data array.
  2293. * @param object bundle Bundle object.
  2294. * @return array
  2295. *
  2296. * @hooked void
  2297. *
  2298. *
  2299. *
  2300. * Filter 'bundle_total_price_html':
  2301. *
  2302. * Filters the price html total.
  2303. *
  2304. * @param string totals Markup to display.
  2305. * @param object bundle Bundle object.
  2306. * @return string
  2307. *
  2308. * @hooked void
  2309. */
  2310.  
  2311. var manager = this,
  2312. filters = {},
  2313. functions = {
  2314.  
  2315. add_filter: function( hook, callback, priority, context ) {
  2316.  
  2317. var hookObject = {
  2318. callback : callback,
  2319. priority : priority,
  2320. context : context
  2321. };
  2322.  
  2323. var hooks = filters[ hook ];
  2324. if ( hooks ) {
  2325. hooks.push( hookObject );
  2326. hooks = this.sort_filters( hooks );
  2327. } else {
  2328. hooks = [ hookObject ];
  2329. }
  2330.  
  2331. filters[ hook ] = hooks;
  2332. },
  2333.  
  2334. remove_filter: function( hook, callback, context ) {
  2335.  
  2336. var handlers, handler, i;
  2337.  
  2338. if ( ! filters[ hook ] ) {
  2339. return;
  2340. }
  2341. if ( ! callback ) {
  2342. filters[ hook ] = [];
  2343. } else {
  2344. handlers = filters[ hook ];
  2345. if ( ! context ) {
  2346. for ( i = handlers.length; i--; ) {
  2347. if ( handlers[ i ].callback === callback ) {
  2348. handlers.splice( i, 1 );
  2349. }
  2350. }
  2351. } else {
  2352. for ( i = handlers.length; i--; ) {
  2353. handler = handlers[ i ];
  2354. if ( handler.callback === callback && handler.context === context) {
  2355. handlers.splice( i, 1 );
  2356. }
  2357. }
  2358. }
  2359. }
  2360. },
  2361.  
  2362. sort_filters: function( hooks ) {
  2363.  
  2364. var tmpHook, j, prevHook;
  2365. for ( var i = 1, len = hooks.length; i < len; i++ ) {
  2366. tmpHook = hooks[ i ];
  2367. j = i;
  2368. while( ( prevHook = hooks[ j - 1 ] ) && prevHook.priority > tmpHook.priority ) {
  2369. hooks[ j ] = hooks[ j - 1 ];
  2370. --j;
  2371. }
  2372. hooks[ j ] = tmpHook;
  2373. }
  2374.  
  2375. return hooks;
  2376. },
  2377.  
  2378. apply_filters: function( hook, args ) {
  2379.  
  2380. var handlers = filters[ hook ], i, len;
  2381.  
  2382. if ( ! handlers ) {
  2383. return args[ 0 ];
  2384. }
  2385.  
  2386. len = handlers.length;
  2387.  
  2388. for ( i = 0; i < len; i++ ) {
  2389. args[ 0 ] = handlers[ i ].callback.apply( handlers[ i ].context, args );
  2390. }
  2391.  
  2392. return args[ 0 ];
  2393. }
  2394.  
  2395. };
  2396.  
  2397. /**
  2398. * Adds a filter.
  2399. */
  2400. this.add_filter = function( filter, callback, priority, context ) {
  2401.  
  2402. if ( typeof filter === 'string' && typeof callback === 'function' ) {
  2403. priority = parseInt( ( priority || 10 ), 10 );
  2404. functions.add_filter( filter, callback, priority, context );
  2405. }
  2406.  
  2407. return manager;
  2408. };
  2409.  
  2410. /**
  2411. * Applies all filter callbacks.
  2412. */
  2413. this.apply_filters = function( filter, args ) {
  2414.  
  2415. if ( typeof filter === 'string' ) {
  2416. return functions.apply_filters( filter, args );
  2417. }
  2418. };
  2419.  
  2420. /**
  2421. * Removes the specified filter callback.
  2422. */
  2423. this.remove_filter = function( filter, callback ) {
  2424.  
  2425. if ( typeof filter === 'string' ) {
  2426. functions.remove_filter( filter, callback );
  2427. }
  2428.  
  2429. return manager;
  2430. };
  2431.  
  2432. }
  2433.  
  2434. /*-----------------------------------------------------------------*/
  2435. /* Initialization. */
  2436. /*-----------------------------------------------------------------*/
  2437.  
  2438. jQuery( document ).ready( function($) {
  2439.  
  2440. /**
  2441. * QuickView compatibility.
  2442. */
  2443. $( 'body' ).on( 'quick-view-displayed', function() {
  2444. $( '.quick-view .bundle_form .bundle_data' ).each( function() {
  2445.  
  2446. var $bundle_data = $( this ),
  2447. $composite_form = $bundle_data.closest( '.composite_form' );
  2448.  
  2449. // If part of a composite, let the composite initialize it.
  2450. if ( $composite_form.length === 0 ) {
  2451. $bundle_data.wc_pb_bundle_form();
  2452. }
  2453.  
  2454. } );
  2455. } );
  2456.  
  2457. /**
  2458. * Script initialization on '.bundle_data' jQuery objects.
  2459. */
  2460. $.fn.wc_pb_bundle_form = function() {
  2461.  
  2462. if ( ! $( this ).hasClass( 'bundle_data' ) ) {
  2463. return true;
  2464. }
  2465.  
  2466. var $bundle_data = $( this ),
  2467. container_id = $bundle_data.data( 'bundle_id' );
  2468.  
  2469. if ( typeof( container_id ) === 'undefined' ) {
  2470. container_id = $bundle_data.attr( 'data-bundle-id' );
  2471.  
  2472. if ( container_id ) {
  2473. $bundle_data.data( 'bundle_id', container_id );
  2474. } else {
  2475. return false;
  2476. }
  2477. }
  2478.  
  2479. var $bundle_form = $bundle_data.closest( '.bundle_form' ),
  2480. $composite_form = $bundle_form.closest( '.composite_form' ),
  2481. composite_data = false,
  2482. bundle_script_id = container_id;
  2483.  
  2484. // If part of a composite product, get a unique id for the script object and prepare variables for integration code.
  2485. if ( $composite_form.length > 0 ) {
  2486.  
  2487. var $component = $bundle_form.closest( '.component' ),
  2488. component_id = $component.data( 'item_id' );
  2489.  
  2490. if ( component_id > 0 && $.isFunction( $.fn.wc_get_composite_script ) ) {
  2491.  
  2492. var composite_script = $composite_form.wc_get_composite_script();
  2493.  
  2494. if ( false !== composite_script ) {
  2495.  
  2496. var component = composite_script.api.get_step( component_id );
  2497.  
  2498. if ( false !== component ) {
  2499. composite_data = {
  2500. composite: composite_script,
  2501. component: component
  2502. };
  2503. bundle_script_id = component_id;
  2504. }
  2505. }
  2506. }
  2507. }
  2508.  
  2509. if ( typeof( wc_pb_bundle_scripts[ bundle_script_id ] ) !== 'undefined' ) {
  2510. wc_pb_bundle_scripts[ bundle_script_id ].shutdown();
  2511. }
  2512.  
  2513. wc_pb_bundle_scripts[ bundle_script_id ] = new WC_PB_Bundle( { $bundle_form: $bundle_form, $bundle_data: $bundle_data, bundle_id: container_id, composite_data: composite_data } );
  2514.  
  2515. $bundle_form.data( 'script_id', bundle_script_id );
  2516.  
  2517. wc_pb_bundle_scripts[ bundle_script_id ].initialize();
  2518. };
  2519.  
  2520. /*
  2521. * Initialize form script.
  2522. */
  2523. $( '.bundle_form .bundle_data' ).each( function() {
  2524.  
  2525. var $bundle_data = $( this ),
  2526. $composite_form = $bundle_data.closest( '.composite_form' );
  2527.  
  2528. // If part of a composite, let the composite initialize it.
  2529. if ( $composite_form.length === 0 ) {
  2530. $bundle_data.wc_pb_bundle_form();
  2531. }
  2532.  
  2533. } );
  2534.  
  2535. } );
  2536.  
  2537. } ) ( jQuery );
  2538.  
Advertisement
Add Comment
Please, Sign In to add comment