1console.log('frm query front') 2 3jQuery(document).ready(function($) { 4 ccfrm_query_fields_init() 5}) 6 7function ccfrm_query_fields_init() { 8 let $ = jQuery 9 let frmQueryFields = {} 10 let cache = {} 11 12 // When a radio field is changed, we need to empty the hidden input[type=text] field for the "other" value 13 $('.frm_radio input[type="radio"]').on('change', function() { 14 let other = $(this).closest('.frm_opt_container').find('input[type="text"].frm_other_input') 15 if (other.length) other.val('') 16 }) 17 18 // Retrieve all dynamic fields of query fields 19 $('.frm-field-query').each(function() { 20 if ($(this).hasClass('init-completed')) return 21 let id = $(this).attr('id') 22 // console.log('init frm query', id) 23 let self = $(this) 24 frmQueryFields[id] = { 25 form: $(this).data('form'), 26 field: $(this).data('field'), 27 value_field: $(this).data('value-field'), 28 where: typeof $(this).data('where') == 'string' ? JSON.parse($(this).data('where')) : $(this).data('where'), 29 } 30 // console.log('new frmQueryField '+id, JSON.stringify(frmQueryFields[id])) 31 for (let k in frmQueryFields[id].where) { 32 let v = frmQueryFields[id].where[k] 33 if (/\[.+\]/g.test(v)) { 34 let fieldIdsToWatch = Array.from(v.matchAll(/\[(\d+)\]/g), el => el.length > 1 ? el[1] : null).filter(el => !!el); 35 for (let frmId of fieldIdsToWatch) { 36 // console.log('try to find watchel', `[name="item_meta[${frmId}]"]`) 37 let watchEl = $(`[name="item_meta[${frmId}]"]`) 38 if (!watchEl.length) { 39 console.log('cannot find watchel', `[name="item_meta[${frmId}]"]`) 40 continue; 41 } 42 watchEl.on('change', function() {computeQuery(id)}) 43 } 44 } 45 } 46 47 // listen to hide/show events of the field 48 let style_mem = $(this).parent().attr('style') 49 // console.log('style_mem', style_mem) 50 let observer = new MutationObserver(function(mutations) { 51 mutations.forEach(function(mutation) { 52 // console.log('new parent style?', style_mem, self.parent().attr('style')) 53 if (mutation.type === "attributes" && style_mem != self.parent().attr('style')) { 54 // console.log('attributes changed, recomputing query field '+id) 55 style_mem = self.parent().attr('style') 56 if (self.parent().css('display') == 'none') self.val('') 57 else computeQuery(id); 58 } 59 }); 60 }); 61 observer.observe($(this).parent()[0], {attributes: true}); 62 63 // console.log('new frmQueryField2 '+id, JSON.stringify(frmQueryFields[id])) 64 computeQuery(id); 65 let viz_mem = $(this).parent().css('display') 66 $(this).parent().on('change', function() { 67 if (viz_mem == $(this).css('display')) return; 68 // console.log('parent changed display=', id, $(this).css('display')) 69 viz_mem = $(this).css('display') 70 computeQuery(id); 71 }) 72 setTimeout(_ => computeQuery(id), 2000) 73 74 $(this).addClass('init-completed') 75 }) 76 77 async function computeQuery(queryFieldId) { 78 // console.log('computeQuery', queryFieldId) 79 let qEl = $('#'+queryFieldId) 80 if (qEl.parent().css('display') == 'none') return qEl.val('').trigger('change') 81 82 if (!qEl.length || !frmQueryFields[queryFieldId]) return console.warn("Cannot find Query field with id ", queryFieldId, frmQueryFields) 83 84 let data = JSON.parse(JSON.stringify(frmQueryFields[queryFieldId])) 85 // console.log('data', data, JSON.stringify(frmQueryFields[queryFieldId])) 86 for (let k in data.where) { 87 let v = frmQueryFields[queryFieldId].where[k] 88 if (/\[.+\]/g.test(v)) { 89 let fieldIds = Array.from(v.matchAll(/\[(\d+)\]/g), el => el.length > 1 ? el[1] : null).filter(el => !!el); 90 let computedVal = v; 91 for (let frmId of fieldIds) { 92 let el = $(`[name="item_meta[${frmId}]"]`) 93 // console.log('el', frmId, el.length, getVal(el)) 94 if (!el.length) { 95 data.where[k] = '' 96 continue; 97 } 98 let new_val = getVal(frmId); 99 // if (!/^\d+$/.test(new_val)) new_val = JSON.stringify(new_val) 100 computedVal = computedVal.replaceAll('['+frmId+']', new_val) 101 } 102 // console.log('final computed val', computedVal) 103 // computedVal = eval('(function() {return ' + computedVal + '})()') 104 data.where[k] = computedVal 105 } 106 } 107 // Add the target user ID if current user has admin rights
108 // if (frmFieldQuery.user_id) data.user_id = frmFieldQuery.user_id 109 data.limit = qEl.data('limit') || -1 110 if (qEl.data('all')) data.field = null 111 112 let hash = await computeHash(data) 113 let qResult = null; 114 if (cache[hash]) { 115 qResult = cache[hash] 116 } else { 117 qResult = await frmfieldquery_request('frm-field-query/v1/form-query', { 118 method: 'POST', 119 data, 120 }) 121 cache[hash] = qResult 122 } 123 124 console.log('computeQuery', qResult, data) 125 // if (typeof qResult == 'object') qResult = JSON.stringify(qResult.map(e => ({id: e.id, ...e.metas, label: e.metas[frmQueryFields[queryFieldId].field] })) ) 126 let qResultString = (typeof qResult == 'object') ? JSON.stringify(qResult) : qResult 127 qEl.val(qResultString).trigger('change') 128 129 // To trigger Formidable Forms change detection 130 let triggerChange = (el) => { 131 let evt = new Event("change"); 132 Object.defineProperty(evt, 'currentTarget', el[0]); 133 Object.defineProperty(evt, 'originalEvent', {writable: true, value: new Event('change')}); 134 // qEl[0].className = "frm_chzn"; // TODO delete, obsolete 135 let qFieldId = el.attr('name').replace(/^item_meta\[(.+)\]$/g, '$1') 136 jQuery( document ).trigger( 'frmFieldChanged', [ el[0], qFieldId, evt ]); 137 } 138 139 // Update the radio options 140 let current_val = qEl.attr('value') 141 console.log('computeQuery current_val', current_val, data.field) 142 if (qEl.find('.frm_radio').length) { 143 qResult.forEach(opt => { 144 let radio = qEl.find(`input[type="radio"][value="${opt.value}"]`) 145 if (radio.length) { 146 let label = radio.closest('label') 147 // replace label textnode with new value but do not touch the radio input inside the label el 148 label.contents().filter(function() { 149 return this.nodeType === 3 150 }).replaceWith(opt.label || opt.value) 151 radio.addClass('dynamic') 152 } else { 153 let newRadio = jQuery(`<div class="frm_radio dynamic"><label>${opt.label || opt.value}</label></div>`) 154 let newRadioInput = qEl.find('input[type="radio"]').eq(0).clone() 155 newRadioInput.val(opt.value).prop('checked', false) 156 newRadio.find('label').prepend(newRadioInput) 157 qEl.find('.frm_radio').eq(0).before(newRadio) // TODO choose between append and before 158 radio = qEl.find(`input[type="radio"][value="${opt.value}"]`) 159 } 160 // Check the radio if it is equal to the current value 161 if (opt.value == current_val && radio.length) { 162 qEl.find('input[type="text"].frm_other_input').val('') 163 qEl.find('input[type="radio"]').prop('checked', false) 164 radio.prop('checked', true) 165 qEl.val(current_val) 166 triggerChange(radio) 167 } 168 }) 169 // Remove all "dynamic" radios that are not in the result 170 qEl.find('.frm_radio.dynamic').each(function() { 171 let val = $(this).find('input[type="radio"]').val() 172 if (!qResult.find(e => e.value == val)) $(this).remove() 173 }) 174 } 175 176 177 } 178 179 function getVal(frmId) { 180 let el = jQuery(`[name="item_meta[${frmId}]"]`) 181 // console.log('getVal', frmId, el.length, el.val(), el, el.eq(0).attr('type')) 182 if (!el.length) return ''; 183 let type = el.eq(0).attr('type')?.toLowerCase() || ''; 184 if (el[0].tagName == "SELECT" || ['text', 'hidden', 'email', 'date', 'number'].includes(type)) return el.val(); 185 if (type == 'radio') { 186 let result = null; 187 // console.log("EL ATTR", frmId) 188 jQuery(`[name="item_meta[${frmId}]"]`).each(function() { 189 if (jQuery(this).is(':checked')) result = jQuery(this).val() 190 }) 191 return result; 192 } 193 } 194 195 async function computeHash(message) { 196 if (typeof message != 'string') message = JSON.stringify(message); 197 const msgUint8 = new TextEncoder().encode(message); // encode comme (utf-8) Uint8Array 198 const hashBuffer = await crypto.subtle.digest('SHA-256', msgUint8); // fait le condensé 199 const hashArray = Array.from(new Uint8Array(hashBuffer));
199 // convertit le buffer en tableau d'octet 200 const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join(''); // convertit le tableau en chaîne hexadélimale 201 return hashHex; 202 } 203 204} 205 206const FRMFIELDQUERY_CACHE = {} 207async function frmfieldquery_options_get(field_id, where) { 208 let field = await ccnfrm_field_get(field_id); 209 if (!field) return []; 210 let options = field.field_options; 211 let data = { 212 form: options.opt_form, 213 field: options.opt_field, 214 value_field: options.opt_value_field, 215 where, 216 limit: options.opt_limit, 217 order_by: options.opt_order_by, 218 order_dir: options.opt_order, 219 } 220 let hash = ccnfrm_hash(data) 221 if (FRMFIELDQUERY_CACHE[hash]) { 222 this.queryValueList = FRMFIELDQUERY_CACHE[hash] 223 return FRMFIELDQUERY_CACHE[hash]; 224 } 225 let qResult = await frmfieldquery_request('frm-field-query/v1/form-query', { 226 method: 'POST', 227 data, 228 }) 229 console.log('qResult', qResult, data) 230 FRMFIELDQUERY_CACHE[hash] = qResult 231 this.queryValueList = qResult 232 return qResult; 233} 234 235 236 237 238 239/** 240 * Sends a request to the backoffice 241 * 242 * @param string url 243 * @param {} config 244 * @param {} headers 245 * @returns 246 */ 247 async function frmfieldquery_request(url, config = {}, headers = {}) { 248 config = { 249 method: 'GET', 250 ...config 251 } 252 headers = { 253 'X-WP-Nonce': frmFieldQuery.nonce, 254 ...headers 255 } 256 return new Promise((resolve, reject) => { 257 jQuery.ajax({ 258 url: frmFieldQuery.root_ep + url, // frmFieldQuery.root_ep 259 beforeSend: function( xhr ) { 260 for(let k in headers) xhr.setRequestHeader(k, headers[k]); 261 }, 262 type: config.method.toUpperCase(), 263 dataType: config.dataType || "json", 264 data: config.data, 265 }).fail(err => reject(err.responseJSON || err)) 266 .done(res => { 267 if (res['error']) reject(res); 268 if (config.redirect) document.location = config.redirect; 269 resolve(res) 270 }); 271 }) 272}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.