boldsupport
2/10/2017 - 5:21 PM

Javascript file to help automate RO installations

Recurring Orders liquid Javascript file to help automate RO installations linked from: https://support.boldcommerce.com/hc/en-us/articles/115002865226--Deprecated-Recurring-Orders-Install-Instructions-Widget-1-X

/*
BOLD-RO.JS.LIQUID

Last Updated: June 27, 2017

Updated installation file to help automate most of the installation tasks & common customizations for Recurring Orders


SUMMARY:
Uses the BOLD.common event model to assist in loading Recurring Orders.  Creates a number of event hooks that additional
customizations can take advanatge of.

REQUIRES:
  * bold-product (updated Nov. 29 or later)
  * bold-variant (Any version for general functionality, updated Feb. 8 or later for subscription only checks)
  * bold-ro-init
  * bold-common

NEW EVENTS TRIGGERED:
  * BOLD_RECURRING_ORDERS_cart_widget_loaded        Triggered when the widget loads in a cart
  * BOLD_RECURRING_ORDERS_widget_loaded				Triggered when the widget loads on a product
  * BOLD_RECURRING_ORDERS_add_to_existing_loaded	Triggered when the 'Add to Existing Order' link loads
  * BOLD_RECURRING_ORDERS_prepaid_change			Triggered when the 'Prepaid' option is changed

NEW EVENT LISTENERS:
  * BOLD_COMMON_variant_changed						BOLD.recurring_orders.updateRecurringWidget
  * BOLD_COMMON_cart_loaded							BOLD.recurring_orders.updateRecurringCartWidget
  													BOLD.recurring_orders.checkRecurringCart
  * BOLD_RECURRING_ORDERS_widget_loaded				BOLD.recurring_orders.onRecurringWidgetLoaded
  * BOLD_RECURRING_ORDERS_cart_widget_loaded		BOLD.recurring_orders.setupAdditionalCheckoutButtons
  * BOLD_RECURRING_ORDERS_prepaid_change			BOLD.recurring_orders.printPrepaidPrice
  													BOLD.recurring_orders.updatePrepaidSubscriptionButton
  * BOLD_OPTIONS_total_changed						BOLD.recurring_orders.updateRecurringWidgetPrice

NEW JQUERY DOCUMENT-LEVEL EVENT LISTENERS
  * When changing a variant selector, get the variant information and emit a BOLD_COMMON_variant_changed event
  * When submitting a product form, check to see if the variant is subscription only & respond appropriately
  * When submitting a cart form or clicking a checkout link, check if the cart is recurring & respond appropriately

*/


//Ensure all objects are initialized
var BOLD = BOLD || {};

BOLD.products = BOLD.products || {};
BOLD.variant_lookup = BOLD.variant_lookup || {};
BOLD.recurring_orders = BOLD.recurring_orders || {};
BOLD.recurring_orders.AddToExistingListener = false;

BOLD.shop = BOLD.shop || {};
BOLD.helpers = BOLD.helpers || {};
BOLD.language = BOLD.language || {};

BOLD.recurring_orders.base_checkout_url = 'https://recurringcheckout.com';
BOLD.recurring_orders.base_script_url = 'https://ro.boldapps.net';

BOLD.recurring_orders.csrf_token = BOLD.recurring_orders.csrf_token || '775e4634405371661f03beaac36d1c1b';

{% raw %}
BOLD.helpers.moneyFormatter = BOLD.helpers.moneyFormatter || {
	formatMoneyFromTemplate: function formatMoneyFromTemplate(format, price, quote) {
		_this = this;
		if (quote === undefined) {
			quote = "'";
		}

		if (quote == "'") {
			format = format.replace('"', "'");
		} else {
			format = format.replace("'", '"');
		}
		var money = format;

		if (price === "") {
			//format to be $12.00
			if (format.indexOf("{{amount}}") != -1) {
				money = money.replace("{{amount}}", "");
			}
			//format with decimal so $12,00
			if (format.indexOf("{{amount_with_comma_separator}}") != -1) {
				money = money.replace("{{amount_with_comma_separator}}", "");
			}
			//format no decimal with space separator so 1 342
			if (format.indexOf("{{amount_no_decimals_with_space_separator}}") != -1) {
				money = money.replace("{{amount_no_decimals_with_space_separator}}", "");
			}
			//format no decimal with space separator so 1,342
			if (format.indexOf("{{amount_no_decimals_with_comma_separator}}") != -1) {
				money = money.replace("{{amount_no_decimals_with_comma_separator}}", "");
			}
			//format with decimal so $12
			if (format.indexOf("{{amount_no_decimals}}") != -1) {
				money = money.replace("{{amount_no_decimals}}", "");
			}
		} else {
			//format to be $12.00
			if (format.indexOf("{{amount}}") != -1) {
				money = money.replace("{{amount}}", _this.formatMoney(price, 2, '.', ','));
			}
			//format with decimal so $12,00
			if (format.indexOf("{{amount_with_comma_separator}}") != -1) {
				money = money.replace("{{amount_with_comma_separator}}", _this.formatMoney(price, 2, ',', ','));
			}
			//format no decimal with space separator so 1 342
			if (format.indexOf("{{amount_no_decimals_with_space_separator}}") != -1) {
				money = money.replace("{{amount_no_decimals_with_space_separator}}", _this.formatMoney(price, 0, '', ' '));
			}
			//format no decimal with space separator so 1,342
			if (format.indexOf("{{amount_no_decimals_with_comma_separator}}") != -1) {
				money = money.replace("{{amount_no_decimals_with_comma_separator}}", _this.formatMoney(price, 0, '', ' '));
			}
			//format with decimal so $12
			if (format.indexOf("{{amount_no_decimals}}") != -1) {
				money = money.replace("{{amount_no_decimals}}", _this.formatMoney(price, 0, '', ','));
			}
		}

		return money;
	},
	formatMoney: function formatMoney(n, c, d, t) {
		var n = n,
			c = isNaN(c = Math.abs(c)) ? 2 : c,
			d = d == undefined ? "." : d,
			t = t == undefined ? "," : t,
			s = n < 0 ? "-" : "",
			i = parseInt(n = Math.abs(+n || 0).toFixed(c)) + "",
			j = (j = i.length) > 3 ? j % 3 : 0;
		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) : "");
	}
};
{% endraw %}



//Set up events & listeners
BOLD.recurring_orders.setupEventListeners = function(){
  if(BOLD.common && BOLD.common.eventEmitter){
    BOLD.common.eventEmitter.off('BOLD_COMMON_cart_loaded', BOLD.helpers.updateRawCart);
    BOLD.common.eventEmitter.on('BOLD_COMMON_cart_loaded', BOLD.helpers.updateRawCart);

    BOLD.common.eventEmitter.off('BOLD_COMMON_checkout', BOLD.recurring_orders.onCheckout);
    BOLD.common.eventEmitter.on('BOLD_COMMON_checkout', BOLD.recurring_orders.onCheckout);

    BOLD.common.eventEmitter.off('BOLD_COMMON_add_to_cart', BOLD.recurring_orders.preventSubscriptionOnlyWithoutSubscription);
    BOLD.common.eventEmitter.on('BOLD_COMMON_add_to_cart', BOLD.recurring_orders.preventSubscriptionOnlyWithoutSubscription);

    BOLD.common.eventEmitter.off('BOLD_COMMON_variant_changed', BOLD.recurring_orders.updateRecurringWidget);
    BOLD.common.eventEmitter.on('BOLD_COMMON_variant_changed', BOLD.recurring_orders.updateRecurringWidget);

    BOLD.common.eventEmitter.off('BOLD_COMMON_cart_loaded', BOLD.recurring_orders.checkRecurringCart);
    BOLD.common.eventEmitter.on('BOLD_COMMON_cart_loaded', BOLD.recurring_orders.checkRecurringCart);

    if(BOLD.recurring_orders.modes.current_mode == BOLD.recurring_orders.modes.recurring_cart){
      BOLD.common.eventEmitter.off('BOLD_COMMON_cart_loaded', BOLD.recurring_orders.updateRecurringCartWidget);
      BOLD.common.eventEmitter.on('BOLD_COMMON_cart_loaded', BOLD.recurring_orders.updateRecurringCartWidget);
     BOLD.common.eventEmitter.off('BOLD_RECURRING_ORDERS_cart_widget_loaded',BOLD.recurring_orders.setupAdditionalCheckoutButtons);
     BOLD.common.eventEmitter.on('BOLD_RECURRING_ORDERS_cart_widget_loaded',BOLD.recurring_orders.setupAdditionalCheckoutButtons);
    }

    BOLD.common.eventEmitter.off('BOLD_RECURRING_ORDERS_widget_loaded', BOLD.recurring_orders.onRecurringWidgetLoaded);
    BOLD.common.eventEmitter.on('BOLD_RECURRING_ORDERS_widget_loaded', BOLD.recurring_orders.onRecurringWidgetLoaded);

    BOLD.common.eventEmitter.off('BOLD_RECURRING_ORDERS_prepaid_change', BOLD.recurring_orders.printPrepaidPrice);
    BOLD.common.eventEmitter.on('BOLD_RECURRING_ORDERS_prepaid_change', BOLD.recurring_orders.printPrepaidPrice);
    BOLD.common.eventEmitter.off('BOLD_RECURRING_ORDERS_prepaid_change', BOLD.recurring_orders.updatePrepaidSubscriptionButton);
    BOLD.common.eventEmitter.on('BOLD_RECURRING_ORDERS_prepaid_change', BOLD.recurring_orders.updatePrepaidSubscriptionButton);

    BOLD.common.eventEmitter.on('BOLD_OPTIONS_total_changed', function(a){
      var form = a.data.option_product.form;
      var variant = BOLD.helpers.getVariantByForm(form);
      if(variant && form){
        BOLD.recurring_orders.updateRecurringWidgetPrice(variant, form);
      }
    });


    if(window.jQuery && typeof jQuery().on === 'function' && !BOLD.recurring_orders.jQuerySetup){

      //Remove any existing document-level events before attempting to set them up - we don't want to accidentally run things twice if the initialization runs more than once
      jQuery(document).off('click', '.swatch,[class*="single-option"]', BOLD.helpers.triggerVariantChange);
      jQuery(document).off('change', '[name^="id"],[class*="single-option"]', BOLD.helpers.triggerVariantChange);
      jQuery(document).off('submit', 'form[action*="/cart/add"]', BOLD.helpers.triggerAddToCartEvent);
      jQuery(document).off('click', 'form[action*="/cart/add"] [type="submit"]:not(.bold_clone)', BOLD.helpers.triggerAddToCartEvent);
      jQuery(document).off('submit', 'form[action*="/checkout"]:not([action*="/tools/checkout"], [action*="/checkout/recurring_product"])', BOLD.helpers.triggerCheckoutEvent);
      jQuery(document).off('click', '[name="checkout"]:not(.bold_clone):not([action*="/tools/checkout"]),[href*="/checkout"]:not(.bold_clone):not([action*="/tools/checkout"])', BOLD.helpers.triggerCheckoutEvent);
      jQuery(document).off('change', '[name=quantity]', BOLD.helpers.triggerProductQuantityChange);
      jQuery(document).off('change', '[name^="updates"]', BOLD.helpers.triggerCartQuantityChange );

      jQuery(document).on('click', '.swatch,[class*="single-option"]', BOLD.helpers.triggerVariantChange);
      jQuery(document).on('change', '[name^="id"],[class*="single-option"]', BOLD.helpers.triggerVariantChange);
      jQuery(document).on('submit', 'form[action*="/cart/add"]', BOLD.helpers.triggerAddToCartEvent);
      jQuery(document).on('click', 'form[action*="/cart/add"] [type="submit"]:not(.bold_clone)', BOLD.helpers.triggerAddToCartEvent);
      jQuery(document).on('submit', 'form[action*="/checkout"]:not([action*="/tools/checkout"], [action*="/checkout/recurring_product"])', BOLD.helpers.triggerCheckoutEvent);
      jQuery(document).on('click', '[name="checkout"]:not(.bold_clone):not([action*="/tools/checkout"]),[href*="/checkout"]:not(.bold_clone):not([action*="/tools/checkout"]):not([href*="/tools/checkout"])', BOLD.helpers.triggerCheckoutEvent);
      jQuery(document).on('change', '[name=quantity]', BOLD.helpers.triggerProductQuantityChange);
      jQuery(document).on('change', '[name^="updates"]', BOLD.helpers.triggerCartQuantityChange );


      //Options V1: Update recurring widget when a priced option changes
      jQuery(document).on('change', '.shappify_option input, .shappify_option select', function(){
        if(this.form && this.form.variant){
          BOLD.recurring_orders.updateRecurringWidgetPrice(this.form.variant, this.form);
        }
      });
      BOLD.recurring_orders.jQuerySetup = true;

    }
  }
}


BOLD.helpers.triggerCommonEvent = function(bold_event_name, original_event, additional_data){
  if(window.BOLD && BOLD.common && BOLD.common.eventEmitter){
    BOLD.common.eventEmitter.emit((bold_event_name.indexOf('BOLD_') > -1 ? bold_event_name : 'BOLD_COMMON_' + bold_event_name), {target:(original_event ? original_event.target : null), original_event:original_event, data: additional_data});
  }
};

BOLD.helpers.triggerCheckoutEvent = function(evt){
  var original_target = evt.target;
  var form = (original_target.tagName == 'FORM' ? original_target : (original_target.form ? original_target.form : null));
  if(form){
    var checkoutInput = BOLD.helpers.newHiddenInput('checkout','checkout');
    form.appendChild(checkoutInput);
  }
  var cart = (BOLD.common && BOLD.common.cartDoctor ? BOLD.common.cartDoctor.rawCart : BOLD.rawCart);
  BOLD.helpers.triggerCommonEvent('checkout', evt, {form:form, cart:cart});
};

BOLD.helpers.triggerAddToCartEvent = function(evt){
  var original_target = evt.target;
  var form = (original_target.tagName == 'FORM' ? original_target : (original_target.form ? original_target.form : null));
  var variant = (form && form.variant ? form.variant : (form ? BOLD.helpers.getVariantByForm(form) : null));
  BOLD.helpers.triggerCommonEvent('add_to_cart', evt, {form:form, variant:variant});
};

BOLD.helpers.triggerCartQuantityChange = function(evt){
    var original_target = evt.target;
    var form = (original_target.form ? original_target.form : (typeof jQuery == 'function' && jQuery().closest ? jQuery(original_target).closest('form')[0] : null));
    var cart = (BOLD.common && BOLD.common.cartDoctor ? BOLD.common.cartDoctor.rawCart : BOLD.rawCart);

    var item, line, variant;
    //If there's a form, try to find out which item/line was updated
    if(form){
      var all_inputs = form.querySelectorAll('[name^="updates"]');
      if(all_inputs.length === cart.items.length){
        var item_count = cart.items.length;
        for(var i = 0; i < item_count; i++){
          if(all_inputs[i] == original_target){
            item = cart.items[i];
            line = i + 1;
            break;
          }
        }
      }
    }

    //If we have an item, try to get the variant details as well
    if(item){
      variant = BOLD.helpers.getVariantByItem(item);
    }

    BOLD.helpers.triggerCommonEvent('cart_quantity_changed', evt, {cart:cart, form:form, item:item, line:line, variant:variant, input:original_target, quantity:parseInt(original_target.value)});
};

BOLD.helpers.triggerProductQuantityChange = function(evt){
  var original_target = event.target;
  var form = (original_target.form ? original_target.form : (typeof jQuery == 'function' && jQuery().closest ? jQuery(original_target).closest('form')[0] : null));
  var variant = (form && form.variant ? form.variant : BOLD.helpers.getVariantByForm(form));
  var product = (BOLD.variant_lookup && variant ? BOLD.products[BOLD.variant_lookup[variant.id]] : null);

  BOLD.helpers.triggerCommonEvent('product_quantity_changed', evt, {product:product, variant:variant, form:form, input:original_target, quantity:parseInt(original_target.value)})

};

BOLD.helpers.triggerVariantChange = function(evt){
  var original_target = evt.target;
  var form = (original_target.form ? original_target.form : (typeof jQuery == 'function' && jQuery().closest ? jQuery(original_target).closest('form')[0] : null));
  if(form){
    setTimeout(function(){
      //Brief timeout to make sure whatever is happening on the page to change the variant has completed - we don't want to be one change behind
      var variant = BOLD.helpers.getVariantByForm(form);
      if(variant){
        BOLD.common.eventEmitter.emit('BOLD_COMMON_variant_changed', {variant:variant});
      }
    }, 50);
  }
};


//Try to prevent form submission if a subscription-only product is being added to the cart without a subscription
BOLD.recurring_orders.preventSubscriptionOnlyWithoutSubscription = function(evt){
  var original_target = evt.target;
  var form = (original_target.form ? original_target.form : (typeof jQuery == 'function' && jQuery().closest ? jQuery(original_target).closest('form')[0] : null));
  var variant = BOLD.helpers.getVariantByForm(form);
  if(evt && evt.oritinal_event && variant && variant.ro_lookup && variant.ro_lookup.subscription_only && (!form['recurring_radio_btn'] || !form['recurring_radio_btn'].value)){
    evt.original_event.preventDefault();
    evt.original_event.stopImmediatePropagation();
    return false;
  }
};

BOLD.recurring_orders.onCheckout = function(evt){
  if(evt && evt.data && evt.data.form){
    BOLD.recurring_orders.redirectToRecurringCheckout(evt.data.form);
  }else{
    var form = document.createElement('form');
    form.setAttribute('method', 'post');
    form.setAttribute('enctype', 'multipart/form-data');
    form.style.display = 'none';
    if(BOLD.recurring_orders.redirectToRecurringCheckout(form)){
      if(evt && evt.original_event){
        evt.original_event.preventDefault();
        evt.original_event.stopImmediatePropagation();
      }
      document.body.appendChild(form);
      form.submit();
    }
  }
}

BOLD.recurring_orders.redirectToRecurringCheckout = function(form){
  var current_mode = BOLD.recurring_orders.modes.current_mode;
  if((current_mode == BOLD.recurring_orders.modes.mixed_cart && window.mixed_cart) || (current_mode == BOLD.recurring_orders.modes.recurring_cart && form.recurring_radio_btn && form.recurring_radio_btn.value == 1)){
    var url = 'https://recurringcheckout.com/s/' + BOLD.common.Shopify.shop.permanent_domain.replace('.myshopify.com','') + '/checkout/recurring' + (current_mode == BOLD.recurring_orders.modes.mixed_cart ? '/' + BOLD.common.Shopify.cart.token : '_full_cart') + '?shop_url=' + BOLD.common.Shopify.shop.permanent_domain + (typeof google_analytics_get_param_string === 'function' ? google_analytics_get_param_string() : '');
    form.setAttribute('action', url);

    if(!form['shopify_cart']){
      var cart_field = document.createElement('input');
      cart_field.setAttribute('type','hidden');
      cart_field.setAttribute('name','shopify_cart');

     //If Product Options is in play, make sure to use the expanded cart so that we capture all of the priced options
      var recurring_cart = (BOLD.common && BOLD.common.cartDoctor ? BOLD.common.cartDoctor.fix(BOLD.common.cartDoctor.rawCart, true) : BOLD.rawCart);
      for(var i=0; i < recurring_cart.items.length; i++){
        recurring_cart.items[i].properties = recurring_cart.items[i].properties_all || recurring_cart.items[i].properties;
      }
      cart_field.setAttribute('value',encodeURIComponent(JSON.stringify(recurring_cart)));
      form.appendChild(cart_field);
    }

    if(!form['csrf_bold_token']){
      var csrf_field = document.createElement('input');
      csrf_field.setAttribute('type','hidden');
      csrf_field.setAttribute('name','csrf_bold_token');
      csrf_field.setAttribute('value', BOLD.recurring_orders.csrf_token);
      form.appendChild(csrf_field);
    }

    BOLD.recurring_orders.appendCustomerFields(form);
    return true;
  }
};

BOLD.recurring_orders.updateRecurringWidget = function(data){
  //Make sure we have a variant
  var variant = data.variant;
  if(!variant){
    return;
  }

  //Make sure we can find the product
  var product = BOLD.products[BOLD.variant_lookup[variant.id]];
  if(!product){
    console.warn('Recurring orders: Could not find product for variant ID ' + variant.id);
    return;
  }

  //Make sure we have a form to work with
  var form = BOLD.helpers.getFormByVariant(variant);
  if(!form){
    return;
  }
  form.variant = variant;

  if(!(BOLD.recurring_orders.modes.current_mode && (BOLD.recurring_orders.modes.current_mode == BOLD.recurring_orders.modes.single_product || BOLD.recurring_orders.modes.current_mode == BOLD.recurring_orders.modes.mixed_cart))){
      if(BOLD.customer.id && !BOLD.recurring_orders.AddToExistingListener){
        var a2e_url = BOLD.recurring_orders.base_script_url + '/recurring_settings/add_to_order?&shop_url=' + BOLD.common.Shopify.shop.permanent_domain + '&group_id=0&customer_id=' + (BOLD.customer.id || '') + '&product_id='+ product.id + '&variant_id=' + variant.id + '&v=2';
        BOLD.helpers.loadScript(a2e_url, function(){ BOLD.common.eventEmitter.emit('BOLD_RECURRING_ORDERS_add_to_existing_loaded', {form:form, product:product, variant:variant}) });
        BOLD.recurring_orders.AddToExistingListener = true;
      }
      return;
  }

  //Make sure the 'addtocart' class is on the submit button
  var submit_button = form.querySelector('[type="submit"]') || {};
  if(submit_button && submit_button.className.indexOf('addtocart') == -1){
    submit_button.className += ' addtocart';
  }
  //Create a default container for the case where there is no submit button in the form
  var parent_container = submit_button.parentElement || form;

  //Find the RO div. If we can't find one, create one
  var roDiv = form.getElementsByClassName('product_rp_div')[0] || null;
  var convertibleWidget = form.getElementsByClassName('ro_widget')[0] || null;
  var custom_button=form.querySelector('[class*=custom_button]');
  var ro_loading_text = BOLD.language.recurring_orders_loading || {{ settings.recurring-orders-loading | json }} || '<span class="ro_loading">Loading subscription info...</\span>';

  if(!roDiv){
    roDiv = BOLD.recurring_orders.newRecurringWidget(product.id);
    if(submit_button.previousElementSibling){
      submit_button.previousElementSibling.appendChild(roDiv);
    }else{
      parent_container.appendChild(roDiv);
    }
  }
  if(!custom_button){
    var custom_button = BOLD.recurring_orders.newCustomButton(product.id);
    if(submit_button.previousElementSibling && submit_button.previousElementSibling.tagName != 'INPUT' && submit_button.previousElementSibling.tagName != 'BUTTON' && (!submit_button.previousElementSibling.className || submit_button.previousElementSibling.className.indexOf('product_rp_div') === -1)){
      submit_button.previousElementSibling.appendChild(custom_button);
    }else{
      parent_container.appendChild(custom_button);
    }
  }

  //Clear the widget if the variant isn't recurring
  if(!variant.ro_lookup){
    roDiv.innerHTML = '';
    BOLD.recurring_orders.removeHiddenInputFields(form);
    if(window.jQuery){
      jQuery('[class*="' + product.id + '_custom_button"]').hide();
      jQuery('.addtocart').show();
    }
    return;
  }

 //Update the single-product mode variable
  window['$' + product.id + '_rp_variant_id'] = variant.id;


  //If the widget is already drawn for the correct group ID, just update the display price
  if(variant.ro_lookup.group_id && form.current_group_id == variant.ro_lookup.group_id && (roDiv.innerHTML && roDiv.innerHTML != ro_loading_text || convertibleWidget && convertibleWidget.innerHTML)){
    BOLD.recurring_orders.updateRecurringWidgetPrice(variant,form);
    BOLD.recurring_orders.updateRecurringWidgetDiscount(variant, form);
    return;
  }

  //Group ID has changed - time to load the widget
  form.current_group_id = variant.ro_lookup.group_id;

  //Is this a chached widget?
  var is_cached = (BOLD.recurring_orders.cached_group && BOLD.recurring_orders.cached_group[variant.ro_lookup.group_id]);
  if(is_cached){
    //The new cached widgets do not draw into the old RO div
    roDiv.innerHTML = '';
  }else{
    roDiv.innerHTML = ro_loading_text;
  }

  //Make sure the RO div has the correct class
  if(roDiv.getAttribute('class').indexOf(product.id) === -1){
    roDiv.setAttribute('class', roDiv.getAttribute('class').split(' p')[0] + ' p' + product.id);
  }
  //Make sure the RO button has the correct class
  var roButton = form.getElementsByClassName('_custom_button')[0] || null;
  if(roButton){
    if(roButton.getAttribute('class').indexOf(product.id) === -1){
      roButton.setAttribute('class', roButton.getAttribute('class').split('_custom_button')[0] + ' ' + product.id + '_custom_button');
    }
  }

  //Remove old hidden input fields
  BOLD.recurring_orders.removeHiddenInputFields(form);

  //Disable & hide add-to-cart buttons while the widget loads
  var add_to_cart_buttons = form.querySelectorAll('[type="submit"]');
  for(var btn = 0; btn < add_to_cart_buttons.length; btn++){
    add_to_cart_buttons[btn].setAttribute('disabled','disabled');
    add_to_cart_buttons[btn].style.visibility = 'hidden';
  }

  //Prune old event listeners
  if(typeof jQuery === 'function'){
    jQuery('#customButton, .' + product.id + '_custom_button').off("click");
    jQuery(document).off('change', "[name='id'],.single-option-selector");
  }

  //Load scripts required to print the widgets
  if(is_cached){
    BOLD.common.eventEmitter.emit('BOLD_RECURRING_ORDERS_widget_loaded', {form:form, product:product, variant:variant});
  }else{
    var url = BOLD.recurring_orders.base_script_url + '/recurring_settings/generate_rp?shop_url=' + BOLD.common.Shopify.shop.permanent_domain + '&group_id='+ variant.ro_lookup.group_id +'&customer_id=' + (BOLD.customer.id || '') + '&product_id='+product.id + '&variant_id=' + variant.id + '&v=2';
    BOLD.helpers.loadScript(url, function(){ BOLD.common.eventEmitter.emit('BOLD_RECURRING_ORDERS_widget_loaded', {form:form, product:product, variant:variant}) });
  }
  if(BOLD.customer.id){
    var a2e_url = BOLD.recurring_orders.base_script_url + '/recurring_settings/add_to_order?&shop_url=' + BOLD.common.Shopify.shop.permanent_domain + '&group_id='+ variant.ro_lookup.group_id +'&customer_id=' + (BOLD.customer.id || '') + '&product_id='+ product.id + '&variant_id=' + variant.id + '&v=2';
    BOLD.helpers.loadScript(a2e_url, function(){ BOLD.common.eventEmitter.emit('BOLD_RECURRING_ORDERS_add_to_existing_loaded', {form:form, product:product, variant:variant}) });
  }
};
BOLD.recurring_orders.onRecurringWidgetLoaded = function(data){

  var variant = data.variant,
      form = data.form,
      product = data.product;

  if(!variant || !variant.ro_lookup){
    return;
  }

  //Check to make sure the RO div doesn't still have the 'loading' text
  var roDiv = form.getElementsByClassName('product_rp_div')[0] || null;
  var ro_loading_text = BOLD.language.recurring_orders_loading || {{ settings.recurring-orders-loading | json }} || '<span class="ro_loading">Loading subscription info...</\span>';
  if(roDiv && roDiv.innerHTML == ro_loading_text){
    roDiv.innerHTML = '';
  }


  if(BOLD.recurring_orders.modes.current_mode == BOLD.recurring_orders.modes.single_product){
    BOLD.recurring_orders.appendCustomerFields(form);
  }

  //Save the discount percentage (in case there are multiple groups loading on the page)
  if(BOLD.recurring_orders.config){
    BOLD.recurring_orders.group_id_lookup = BOLD.recurring_orders.group_id_lookup || {};
    BOLD.recurring_orders.group_id_lookup[variant.ro_lookup.group_id] = {multiplier: BOLD.recurring_orders.config.discount_percentage};
  }

  //Re-enable the add-to-cart button
  if(form){
    var add_to_cart_buttons = form.querySelectorAll('[type="submit"]');
    for(var btn = 0; btn < add_to_cart_buttons.length; btn++){
      add_to_cart_buttons[btn].removeAttribute('disabled');
      add_to_cart_buttons[btn].style.visibility = null;
    }
  }

  //RECURRING ORDERS - PRINT PREPAID DISCOUNTED PRICE WHEN PREPAID OPTION IS SELECTED/CHANGED
  var ro_prepaid_toggle = form.getElementsByClassName('prepaid_checkbox');
  if(ro_prepaid_toggle.length){
    ro_prepaid_toggle[0].removeEventListener('change', BOLD.recurring_orders.prepaidChange);
    ro_prepaid_toggle[0].addEventListener('change', BOLD.recurring_orders.prepaidChange);
  }
  var ro_prepaid_selector = form.getElementsByClassName('prepaid_length_select');
  if(ro_prepaid_selector.length){
    ro_prepaid_selector[0].removeEventListener('change', BOLD.recurring_orders.prepaidChange);
    ro_prepaid_selector[0].addEventListener('change', BOLD.recurring_orders.prepaidChange);
    ro_prepaid_selector[0].setAttribute('name', 'bold_ro_prepaid_duration');
  }

  if(window.jQuery){
    jQuery('[name="recurring_radio_btn"]',form).first().trigger('click');
    jQuery('.prepaid_checkbox',form).trigger('change');
    jQuery('.prepaid_length_select',form).trigger('change');
  }

  BOLD.recurring_orders.updateRecurringWidgetPrice(form.variant, form);
  BOLD.recurring_orders.toggleCustomButton(form);
}

BOLD.recurring_orders.prepaidChange = function(e){
  var form = this.form;
  var variant = form.variant || BOLD.helpers.getVariantByForm(form);
  BOLD.common.eventEmitter.emit('BOLD_RECURRING_ORDERS_prepaid_change', {value:this.value, form:form, variant: variant});
};
BOLD.recurring_orders.printPrepaidPrice = function(data){
  if(!data.variant)
    return;

  var variant = data.variant;
  var form = data.form || BOLD.helpers.getFormByVariant(variant);
  if(!form){
    return;
  }

  var selected_prepaid_duration = form.bold_ro_prepaid_duration || form.getElementsByClassName('prepaid_length_select')[0];
  if(!selected_prepaid_duration){
    return;
  }

  var price = variant.price;
  var selected_prepaid_option = selected_prepaid_duration[selected_prepaid_duration.selectedIndex];
  var prepaid_discount = (100 - parseInt(selected_prepaid_option.getAttribute('data-discount-percentage'))) * 0.01;
  var prepaid_duration = parseInt(selected_prepaid_option.text);

  var prepaid_div = form.getElementsByClassName('prepaid_length_div')[0];

  var price_node = document.createElement('span');
  price_node.className = 'prepaid_total';
  price_node.innerHTML = 'Total: ' + BOLD.common.Shopify.formatMoney(price * prepaid_duration * prepaid_discount, BOLD.common.Shopify.shop.money_format);

  if(prepaid_div.getElementsByClassName('prepaid_total').length){
    prepaid_div.removeChild(prepaid_div.getElementsByClassName('prepaid_total')[0]);
  }
  prepaid_div.appendChild(price_node);
};
BOLD.recurring_orders.updatePrepaidSubscriptionButton = function(data){
  BOLD.recurring_orders.toggleCustomButton(data.form)
}
BOLD.recurring_orders.toggleCustomButton = function(form){
  if(!form){
    return;
  }
  var submit_button = form.querySelector('[type=submit]');
  var custom_button = form.querySelector('[class*="custom_button"]');
  var prepaid_chkbox = form.querySelector('.prepaid_checkbox');

  var custom_button_visible;
  var submit_button_visible;

  //If convertable widget and subscription selected, show neither
  if(form['bold-ro__selector_radio_button'] && form['bold-ro__selector_radio_button'].value != 0){
    custom_button_visible = false;
  }
  if((BOLD.recurring_orders.modes.current_mode == BOLD.recurring_orders.modes.single_product && form['bold-ro__selector_radio_button'] && form['bold-ro__selector_radio_button'].value != 0) ||
  (form['bold-ro__selector_radio_button'] && form['bold-ro__selector_radio_button'].value == 3)){
    submit_button_visible = false;
  }
  //If single product mode and subscription selected OR if prepaid selected, hide submit/show custom button
  else if((BOLD.recurring_orders.modes.current_mode == BOLD.recurring_orders.modes.single_product && form.recurring_radio_btn && form.recurring_radio_btn.value !=0) || (prepaid_chkbox && prepaid_chkbox.checked)){
    submit_button_visible = false;
    custom_button_visible = true;
  }
  //Hide custom button/show submit
  else{
    submit_button_visible = true;
    custom_button_visible = false;
  }
  submit_button.style.display = (submit_button_visible ? null : 'none');
  custom_button.style.display = (custom_button_visible ? null : 'none');
  BOLD.helpers.triggerCommonEvent('BOLD_RECURRING_ORDERS_product_button_display', null, {submit_button_visible: submit_button_visible, subscription_button_visible: custom_button_visible, form: form});
}

BOLD.recurring_orders.newRecurringWidget = function(product_id){
  var roDiv =  document.createElement('div');
  roDiv.className='product_rp_div p' + product_id;
  return roDiv;
}
BOLD.recurring_orders.newCustomButton = function(product_id){
  var custom_button = document.createElement('a');
  custom_button.className = product_id + '_custom_button';

  {% unless settings.recurring-orders-additional-button-classes == blank %}custom_button.className += ' ' + {{ settings.recurring-orders-additional-button-classes | json }};{% endunless %}
  custom_button.setAttribute('data-toggle', 'modal');
  custom_button.setAttribute('data-target','#myModal');
  custom_button.style.display = 'none';
  custom_button.innerHTML = {{ 'products.product.add_to_cart' | t | json }};

  var add_to_cart_button = document.querySelector('form[action*="/cart/add"] [type="submit"]');
  if(add_to_cart_button){
    {% if settings.recurring-orders-inherit-classes %}custom_button.className += ' ' + add_to_cart_button.className.split('addtocart').join(' ');{% endif %}
    custom_button.addEventListener('click', function(e){ e.stopPropagation(); })
  }
  return custom_button;
};
BOLD.recurring_orders.newRecurringCartDiv = function(){
  var cartDiv = document.createElement('div');
  cartDiv.className = 'product_rp_cart_div';
  return cartDiv;
};

BOLD.recurring_orders.appendCustomerFields = function(form){
  if(!form || !form.appendChild) return;

  form.appendChild(BOLD.helpers.newHiddenInput('shopify_customer_id', BOLD.customer.id, 'ro_customer_field'));
  form.appendChild(BOLD.helpers.newHiddenInput('email', BOLD.customer.email, 'ro_customer_field'));
  form.appendChild(BOLD.helpers.newHiddenInput('address1',BOLD.customer.address1, 'ro_customer_field'));
  form.appendChild(BOLD.helpers.newHiddenInput('address',BOLD.customer.address, 'ro_customer_field'));
  form.appendChild(BOLD.helpers.newHiddenInput('city',BOLD.customer.city, 'ro_customer_field'));
  form.appendChild(BOLD.helpers.newHiddenInput('company',BOLD.customer.company, 'ro_customer_field'));
  form.appendChild(BOLD.helpers.newHiddenInput('country',BOLD.customer.country, 'ro_customer_field'));
  form.appendChild(BOLD.helpers.newHiddenInput('first_name',BOLD.customer.first_name, 'ro_customer_field'));
  form.appendChild(BOLD.helpers.newHiddenInput('last_name',BOLD.customer.last_name, 'ro_customer_field'));
  form.appendChild(BOLD.helpers.newHiddenInput('phone',BOLD.customer.phone, 'ro_customer_field'));
  form.appendChild(BOLD.helpers.newHiddenInput('province',BOLD.customer.province, 'ro_customer_field'));
  form.appendChild(BOLD.helpers.newHiddenInput('zip',BOLD.customer.zip, 'ro_customer_field'));
};
BOLD.recurring_orders.updateRecurringWidgetPrice = function(variant, form){
  if(!variant && form && form.variant){
    variant = form.variant;
  }
  if(variant && variant.ro_lookup){
    var ro_price = BOLD.helpers.calcTotalPrice(variant, form, {always_discount:true,update_variant_price:false});
    var ro_discount_price_field = form.getElementsByClassName('new_discounted_price');
    if(ro_discount_price_field.length){
      ro_discount_price_field[0].innerHTML = BOLD.common.Shopify.formatMoney(ro_price, BOLD.common.Shopify.shop.money_format);
    }

    var ro_amount_off_field = form.getElementsByClassName('new_amount_discounted');
    if(ro_amount_off_field.length){
      ro_amount_off_field[0].innerHTML = BOLD.common.Shopify.formatMoney(variant.price - ro_price, BOLD.common.Shopify.shop.money_format);
    }
  }
};
BOLD.recurring_orders.removeHiddenInputFields = function(form){
  var fields = form.querySelectorAll('.frequency_type_text, .group_id, .discounted_price, .ro_discount_percentage, .ro_unformatted_price, .ro_customer_field');
  for(var f=0; f<fields.length; f++){
    fields[f].parentElement.removeChild(fields[f]);
  }
};
BOLD.recurring_orders.updateRecurringWidgetDiscount = function(variant, form){
  if(form && form.getElementsByClassName){
    var discount_fields = form.getElementsByClassName('ro_discount_percentage');
    for(var df=0; df < discount_fields.length; df++){
      discount_fields[df].value = (1.00 - BOLD.helpers.calcROMultiplier(variant, form, true) ) * 100;
    }
  }
};
BOLD.recurring_orders.updateRecurringCartWidget = function(cart){
  var cartForms = document.querySelectorAll('form[action*="/cart"]:not([action*="/cart/add"]), form[action*="/checkout"]');
  for(var cf = 0; cf < cartForms.length; cf++){
    var form = cartForms[cf];
    var recurring_cart_div = form.querySelector('.product_rp_cart_div');

    if(!recurring_cart_div){
      recurring_cart_div = BOLD.recurring_orders.newRecurringCartDiv();

      var submit_button = form.querySelector('[type="submit"]') || {};
      var parent_element = submit_button.parentElement || form;
      parent_element.appendChild(recurring_cart_div);
    }
  }

  var url = BOLD.recurring_orders.base_script_url + '/recurring_settings/generate_rp_cart?&shop_url=' + BOLD.common.Shopify.shop.permanent_domain + '&customer_id=' + BOLD.customer.id + '&subtotal=' + cart.total_price;
  BOLD.helpers.loadScript(url, function(){BOLD.common.eventEmitter.emit('BOLD_RECURRING_ORDERS_cart_widget_loaded')});
};
BOLD.recurring_orders.setupAdditionalCheckoutButtons = function(){
  var cartForms = document.querySelectorAll('form[action*="/cart"]:not([action*="/cart/add"]), form[action*="/checkout"]');
  for(var cf = 0; cf < cartForms.length; cf++){
    var form = cartForms[cf];
    var rcDiv = form.querySelector('.product_rp_cart_div');
    if(rcDiv){
      rcDiv.removeEventListener('click', BOLD.recurring_orders.hideAdditionalCheckoutButtons);
      rcDiv.addEventListener('click', BOLD.recurring_orders.hideAdditionalCheckoutButtons);
    }
  }
}
BOLD.recurring_orders.hideAdditionalCheckoutButtons = function(){
  var cartForms = document.querySelectorAll('form[action*="/cart"]:not([action*="/cart/add"]), form[action*="/checkout"]');
  for(var cf = 0; cf < cartForms.length; cf++){
    var form = cartForms[cf];
    var additional_checkout_buttons = document.querySelectorAll('[class*="additional-checkout"], [class*="additional_checkout"]');
    if(!additional_checkout_buttons.length){
      return;
    }
    var hide = (form['recurring_radio_btn'].value == 1 || (BOLD.recurring_orders.modes.current_mode == BOLD.recurring_orders.modes.mixed_cart && window.mixed_cart));
    for(var acb=0; acb < additional_checkout_buttons.length; acb++){
      additional_checkout_buttons[acb].style.display = (hide ? 'none' : null);
    }
  }
};
BOLD.recurring_orders.checkRecurringCart = function(cart){
  if(typeof cart != 'object') return;
  for(var i = 0; i < cart.items.length; i++){
    var item = cart.items[i];
    if((item.properties && item.properties.frequency_type) || (item.Bold && item.Bold.frequency_type)){
      window.mixed_cart = true;
      return;
    }
  }
  window.mixed_cart = false;
};

BOLD.helpers.loadScript = function(url, callback){
  var script = document.createElement("script")
  script.type = "text/javascript";
  if (script.readyState){  //IE
    script.onreadystatechange = function(){
      if (script.readyState == "loaded" || script.readyState == "complete"){
        script.onreadystatechange = null;
        if(typeof callback === 'function')
          callback();
      }
    };
  } else {  //Others
    script.onload = function(){
      if(typeof callback === 'function')
        callback();
    };
  }
  script.src = url;
  document.getElementsByTagName("head")[0].appendChild(script);
};
BOLD.helpers.calcUnitPrice = function(variant, form){
  if(typeof variant !== 'object' || !variant) return null;
  if(typeof variant.bold_original_price === 'undefined') variant.bold_original_price = variant.price;
  if(!variant.btm_lookup || typeof(mathEval)!='function') return variant.bold_original_price;
  parsedFormula = variant.btm_lookup.formula;
  for(var key in variant.btm_lookup){
    var field = variant.btm_lookup[key];
    var field_value = (form['properties[' + field + ']'] ? form['properties[' + field + ']'].value : 0);
    parsedFormula = parsedFormula.split('{' + field + '}').join(field_value);
  }
  return (Math.ceil(mathEval(parsedFormula)) || 1) * variant.bold_original_price;
};
BOLD.helpers.calcOptionsTotal = function(form){
  //Total with Options V2
  if(BOLD.options && BOLD.options.app && BOLD.options.app.getOptionProductByForm && BOLD.options.app.getOptionProductByForm(form)){
    return (BOLD.options.app.getOptionProductByForm(form).priceHandler ? BOLD.options.app.getOptionProductByForm(form).priceHandler.total : 0);
  }
  //Total with Options V1
  var extrasList = form.querySelectorAll('.shappify_option_value [data-variant]:checked');
  var totalExtras = 0;
  for(var index = 0; index < extrasList.length; index++){
    if(extrasList[index].getAttribute && !isNaN(parseFloat(extrasList[index].getAttribute('data-price'))))
      totalExtras += Math.round(parseFloat(extrasList[index].getAttribute('data-price')) * 100.0, 0);
  }
  return parseInt(totalExtras);
};

BOLD.helpers.calcROMultiplier = function(variant, form, always_discount){
  var multiplier = 1.00;
  if(form['recurring_radio_btn'] && (form['recurring_radio_btn'].value == 2 || always_discount) && BOLD.recurring_orders.group_id_lookup && form.variant && form.variant.ro_lookup && form.variant.ro_lookup.group_id && typeof BOLD.recurring_orders.group_id_lookup[form.variant.ro_lookup.group_id] != 'undefined'){
    multiplier = BOLD.recurring_orders.group_id_lookup[form.variant.ro_lookup.group_id].multiplier;
  }
  else if(form['recurring_radio_btn'] && (form['recurring_radio_btn'].value == 2 || always_discount) && form.getElementsByClassName('ro_discount_percentage').length && form.getElementsByClassName('ro_discount_percentage')[0].value){
    var discount_amount =  parseFloat(form.getElementsByClassName('ro_discount_percentage')[0].value);
    if(!isNaN(discount_amount))
      multiplier -= discount_amount * 0.01;
  }
  return (multiplier >= 0 ? multiplier : 1.00);
};
BOLD.helpers.calcTotalPrice = function(variant, form, options){
  if(typeof options !== 'object') options = {};
  if(!form && this.form) form = this.form;
  if(!form){
    form = BOLD.helpers.getFormByVariant(variant);
  }
  if(!form) return;

  if(!variant && form.variant) variant = form.variant;
  if(!variant) return;

  if(typeof BOLD.shop.dynamic_pricing === 'undefined'){
    BOLD.shop.dynamic_pricing = true;
  }
  if(typeof options.update_variant_price === 'undefined'){
    options.update_variant_price = true;
  }
  var total_price = (BOLD.helpers.calcUnitPrice(variant,form) + BOLD.helpers.calcOptionsTotal(form)) * BOLD.helpers.calcROMultiplier(variant, form, options.always_discount);
  if(options.update_variant_price && BOLD.shop.dynamic_pricing){
    variant.price = total_price;
  }
  return total_price;
};
BOLD.helpers.getVariantByForm = function(form){
  //Check to make sure we can find the product/variant
  if(!(form.id && form.id.value && BOLD.variant_lookup && BOLD.products)){
    return null;
  }
  var variantId = form.id.value;
  var product_handle = BOLD.variant_lookup[variantId];
  var product = BOLD.products[product_handle];

  if(!product) {
    return null;
  }

  var variant;
  for(var vindex in product.variants){
    if(product.variants[vindex].id == variantId){
      variant = product.variants[vindex];
      form.variant = variant;
      break;
    }
  }
  return variant;
};
BOLD.helpers.getFormByVariant = function(variant){
  //Make sure we have a form to work with
  var variantIdFields = document.querySelectorAll('[name="id"]');
  var variantIdField = {};
  for(var vif = 0; vif < variantIdFields.length; vif++){
    if(variantIdFields[vif].value==variant.id){
      variantIdField = variantIdFields[vif];
    }
  }
  return variantIdField.form;
}
BOLD.helpers.initializeForm = function(form){
  var variant = BOLD.helpers.getVariantByForm(form);
  if(variant){
    BOLD.common.eventEmitter.emit('BOLD_COMMON_variant_changed', {variant:variant});
  }
};
BOLD.helpers.initializeAllForms = function(){
  var allForms = document.querySelectorAll('form[action*="/cart/add"]');
  for(var f=0; f < allForms.length; f++){
    BOLD.helpers.initializeForm(allForms[f]);
  }
};
BOLD.helpers.newHiddenInput = function(name, value, classname){
  var input = document.createElement('input');
  input.setAttribute('type', 'hidden');
  input.setAttribute('name', name);
  input.setAttribute('value', value);
  input.className = 'bold_hidden_input ' + name + ' ' + classname;
  return input;
};
BOLD.helpers.updateRawCart = function(cart){
  if(cart && cart.items){
    BOLD.rawCart = cart;
  }
};
BOLD.recurring_orders.setupEventListeners();
if(window.addEventListener){
  window.addEventListener('load', BOLD.recurring_orders.setupEventListeners);

  document.addEventListener('DOMContentLoaded', BOLD.helpers.hideAdditionalCheckoutButtons);
  document.addEventListener('DOMContentLoaded', BOLD.recurring_orders.setupEventListeners);
  document.addEventListener('DOMContentLoaded', BOLD.helpers.initializeAllForms);
  document.addEventListener('DOMContentLoaded', function(){ BOLD.common.eventEmitter.emit('BOLD_COMMON_cart_loaded', BOLD.rawCart || BOLD.common.Shopify.cart)});
}else{
  window.attachEvent('onload', BOLD.recurring_orders.setupEventListeners)
  window.addEventListener('onload', BOLD.helpers.initializeAllForms)
}