Skip to content

Instantly share code, notes, and snippets.

@boldsupport
Last active October 11, 2017 15:22
Show Gist options
  • Save boldsupport/e99c12960db9de7f99d151550b7e4d50 to your computer and use it in GitHub Desktop.
Save boldsupport/e99c12960db9de7f99d151550b7e4d50 to your computer and use it in GitHub Desktop.
<!-- bold-conditional-logic-patch.liquid -->
{% comment %}
BOLD-CONDITIONAL-LOGIC-PATCH.LIQUID
Include this file on a page that uses Conditional Logic with Product Options to fix the following reported issues:
* Checkbox groups not triggering the correct conditional options
* Nested conditions not always disappearing properly when user changes an option at the beginning of the option tree
* Conditional options that are not available add blank properties to the cart. (Visible options left blank by the user
will still submit an empty value)
To use this file, add the following code to the bottom of theme.liquid (immediately before the </body> tag):
Example: {% include 'bold-conditional-logic-patch' %}
Changing the liquid variable 'bold_debug' to true will overwrite the CSS used to hide options so that they always display,
but have a different colour/background than the normal options.
{% endcomment %}
{% assign bold_debug = true %}
<script>
var original_options_rendered = shappify_options_rendered || null;
var shappify_options_rendered = function(prod_id){
/* When options loads, patch the conditional_rules function, then run any shappify_options_rendered function that had been previously created */
if(typeof(conditional_rules)==='function'){
conditional_rules = BOLD.conditional_logic.conditional_rules;
BOLD.conditional_logic.initConditionalPropertyNames();
conditional_rules(prod_id);
}
if(jQuery){
jQuery(document).on('change', '.shappify_option_value input, .shappify_option_value select, .shappify_option_textarea', conditional_rules);
}
if(typeof(original_options_rendered)==='function')
original_options_rendered(prod_id);
}
var BOLD = BOLD || {};
BOLD.conditional_logic = {
conditional_rules:function(prod_id) {
var form = this.form || BOLD.conditional_logic.getForm(prod_id);
if(!form) return;
if(isNaN(parseInt(prod_id))){
if(form.getAttribute('data-product-id'))
prod_id = form.getAttribute('data-product-id');
else{
var prod_id_element_check = this.parentElement;
while(typeof prod_id_element_check.id === 'string' && prod_id_element_check.id.indexOf('shappify_custom_options_') == -1){
prod_id_element_check = prod_id_element_check.parentElement;
}
if(prod_id_element_check.id.indexOf('shappify_custom_options_') != -1){
prod_id = prod_id_element_check.id.replace('shappify_custom_options_', '');
}
}
}
BOLD.conditional_logic.resetConditions(form);
var condition_obj = BOLD.conditional_logic.getConditions(form, prod_id);
var results = {};
for(var cond_set in condition_obj){
results[cond_set] = {
'condition':condition_obj[cond_set],
'result':BOLD.conditional_logic.evaluateConditionSet(prod_id, condition_obj[cond_set], form)
};
if(results[cond_set].result)
BOLD.conditional_logic.conditionPassed(prod_id, cond_set, form);
else
BOLD.conditional_logic.conditionFailed(prod_id, cond_set, form);
}
BOLD.conditional_logic.conditionalCheckCleanup(form);
return results;
},
getForm:function(prod_id){
var options_div = document.getElementById('shapp_options_' + prod_id);
if(!options_div) return;
if(!options_div.form){
var formcheck = options_div.parentElement;
while(formcheck.tagName != 'FORM' && formcheck.type != 'BODY')
formcheck = formcheck.parentElement;
if(formcheck.tagName == 'FORM')
options_div.form = formcheck;
}
return options_div.form;
},
resetConditions:function(form){
var field_list = form.querySelectorAll('[data-bold-condition-check]');
for(var field = 0; field < field_list.length; field++){
field_list[field].removeAttribute('data-bold-condition-check');
}
},
getConditions:function(form, prod_id){
var condition_sets = form.querySelectorAll('#shappify_custom_options_' + prod_id + ' #conditional_rules>div');
var condition_obj = {};
for(var cond = 0; cond < condition_sets.length; cond++){
if(!condition_sets[cond].getAttribute) continue;
var condition_name = condition_sets[cond].getAttribute('name');
if(condition_name){
condition_obj[condition_name] = form.querySelector('[name="' + condition_name + '"]'); /* Just get the first instance - all of the children share a name with the parent, and that gets mucky */
}
}
return condition_obj;
},
evaluateConditionSet:function(prod_id, condition_set, form){
var verify_all = condition_set.attributes['data-verify-all'].value == '1'; /* Casting string into a boolean */
for(var rule in condition_set.childNodes){
var result = BOLD.conditional_logic.evaluateCondition(prod_id, condition_set.childNodes[rule], form);
if (typeof(result)=='undefined') continue; /* Skip over elements that aren't validation rules */
if(result && !verify_all)
return true; /* We verified at least one of the rules */
else if(!result && verify_all)
return false; /* We needed to verify all rules and one failed */
}
return verify_all; /* We either needed to verify all rules and all passed or we needed to pass at least one of them and they all failed - Whether we needed to verify all or one was already determined at the beginning of the function */
},
evaluateCondition:function(prod_id, condition, form){
if(!condition.innerHTML)
return; /* No rule, return undefined */
var field_num = condition.getAttribute('data-field-num');
var related_fields = BOLD.conditional_logic.getRelatedFields(prod_id, field_num, form);
for(var field = 0; field < related_fields.length; field ++) {
if(!related_fields[field].type) continue; /* Skip over anything that's not an HTML element with a value */
var is_checked = true; /* So that we can treat non-checkbox fields as 'checked' for ease of boolean comparison below */
var value = related_fields[field].getAttribute('data-conditional-value') || (!isNaN(related_fields[field].selectedIndex) ? related_fields[field][related_fields[field].selectedIndex].getAttribute('data-conditional-value') : false) || encodeURIComponent(related_fields[field].value);
if( (related_fields[field].attributes['data-bold-condition-check'] && related_fields[field].attributes['data-bold-condition-check'].value==="false" && related_fields[field].className.indexOf('_show') > -1) || (related_fields[field].attributes['data-bold-condition-check'] && related_fields[field].attributes['data-bold-condition-check'].value==="true" && related_fields[field].className.indexOf('_hide') > -1))
value = '';
if(related_fields[field].type == 'radio' || related_fields[field].type == 'checkbox' ){
is_checked = related_fields[field].checked;
if(value == "%E2%9C%93") value = "checked"; /* Manual replacement for the checkmark ASCII character to match what shows up in the conditional rules */
}
var result = eval(condition.innerHTML.replace("~~value1~~", value) );
if(is_checked && result)
return true;
}
return false;
},
getRelatedFields:function(prod_id, field_num, form){
return form.querySelectorAll('#shapp_option_list_' + prod_id + ' [id^="' + field_num + '"]');
},
conditionPassed:function(prod_id, condition_id, form){
var affected_fields = form.querySelectorAll('.' + condition_id + '_show, .' + condition_id + '_hide');
for(var field = 0; field < affected_fields.length; field++){
if(!affected_fields[field].setAttribute) continue; /* We only care about actual HTML fields */
affected_fields[field].setAttribute('data-bold-condition-check', true);
}
},
conditionFailed:function(prod_id, condition_id, form){
var affected_fields = form.querySelectorAll('.' + condition_id + '_show, .' + condition_id + '_hide');
for(var field = 0; field < affected_fields.length; field++){
if(!affected_fields[field].setAttribute) continue; /* We only care about actual HTML fields */
if(!affected_fields[field].getAttribute('data-bold-condition-check')) /* Don't overwrite a passing condition */
affected_fields[field].setAttribute('data-bold-condition-check', false);
}
},
conditionalCheckCleanup:function(form){
BOLD.conditional_logic.applyConditionalClass(form);
BOLD.conditional_logic.setConditionalPropertyNames(form);
},
applyConditionalClass:function(form){
var hidden_fields = form.querySelectorAll('.condition_hide[data-bold-condition-check=true],.condition_show[data-bold-condition-check=false]');
var visible_fields = form.querySelectorAll('.condition_show[data-bold-condition-check=true],condition_hide[data-bold-condition-check=false]');
for(var field = 0; field < hidden_fields.length; field++){
if(!hidden_fields[field].className) continue; /* We only care about actual HTML fields */
hidden_fields[field].className = ' ' + hidden_fields[field].className + ' '; /* Lazy way to ensure conditional (with spaces) gets found */
hidden_fields[field].className = hidden_fields[field].className.replace(/ conditional /g, ' ').trim() + ' conditional'; /* Make sure we don't end up with multiple conditional classes */
// clear_failed_condition_data(hidden_fields[field]); /* Clear data from hidden fields so that they can't validate further-down rules */
hidden_fields[field].className = hidden_fields[field].className.trim();
}
for(var field = 0; field < visible_fields.length; field++){
if(!visible_fields[field].className) continue; /* We only care about actual HTML fields */
visible_fields[field].className = ' ' + visible_fields[field].className + ' '; /* Lazy way to ensure conditional (with spaces) gets found */
visible_fields[field].className = visible_fields[field].className.replace(/ conditional /g, ' ');
visible_fields[field].className = visible_fields[field].className.trim();
}
},
initConditionalPropertyNames:function(){
var bold_option_fields = document.querySelectorAll('.shapp_option_set [name^="properties"]');
for(var field in bold_option_fields){
if(typeof(bold_option_fields[field].setAttribute)!=='function') continue; /* Make sure we can do what we're trying to do first */
bold_option_fields[field].setAttribute('data-property-name', bold_option_fields[field].attributes['name'].value);
}
},
setConditionalPropertyNames:function(form){
var bold_option_fields = form.querySelectorAll('[data-property-name]');
for(var field in bold_option_fields){
if(typeof(bold_option_fields[field].setAttribute)!=='function') continue; /* Make sure we can do what we're trying to do first */
bold_option_fields[field].setAttribute('name', bold_option_fields[field].attributes['data-property-name'].value);
}
bold_option_fields = form.querySelectorAll('.conditional [data-property-name]');
for(var field in bold_option_fields){
if(typeof(bold_option_fields[field].setAttribute)!=='function') continue; /* Make sure we can do what we're trying to do first */
bold_option_fields[field].setAttribute('name', '');
}
}
}
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment