/* mulsu.js, part of nitpo, by Thomas Krichel */ const html_ns = 'http://www.w3.org/1999/xhtml'; const fields = ['emad']; const labins = ['label', 'input']; /* STATE VARIABLES */ /* subscriber email address if set as an emad */ var set_emad = null; /* the potential emad input by the user */ var emad_input = null; /* the current repcode */ var set_repcode = null; /* the signup text without decoration, usually 'Sign up' */ var bare_signup_text = null; /* the current emad set on the signup elements */ var emad_signup = null; /* the label on the emad input, before entry */ var label_emfal = null; /* has the user made a emad_input */ // var have_i_seen_an_emad_input = false; /* the regular expression for emads */ // const emad_regex = new RegExp("(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|\"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*\")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])"); // covers more cases but does not parse. // const emad_regex = new RegExp("(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])"); const emad_regex = new RegExp("([!#-'*+/-9=?A-Z^-~-]+(\.[!#-'*+/-9=?A-Z^-~-]+)*|\"\(\[\]!#-[^-~ \t]|(\\[\t -~]))+\")@([!#-'*+/-9=?A-Z^-~-]+(\.[!#-'*+/-9=?A-Z^-~-]+)*|\[[\t -Z^-~]*])"); /* CONF variables, read with read_conf() */ /* the nitpo_url, found in the HTML id="nitpo_url" */ /* this is the king of configs. When it is zero, all the configs are read */ var nitpo_url = null; /* the nitpo path start, found in the HTML id="nitpo_pathstart" */ //var nitpo_pathstart = null; /* nitpo timeout, in miliseconds found in the HTML id="nitpo_timeout" */ //var nitpo_timeout = null; /* mulsu wait phrase, displayed while the fetch is is progress found in the HTML id="mulsu_wait_start" */ //var mulsu_wait_start = null; /* mulsu wait success phrase, displayed when the signup is successful found in the HTML id="mulsu_wait_success" */ // var mulsu_wait_success = null; /* mulsu wait failure phrase, displayed when the signup has failed found in the HTML id="mulsu_wait_failure" */ // var mulsu_wait_success = null; /* label for the input when succesfully entered */ //var label_emcor = null; var mulsu_conf = {}; // /* Phrase variables, read from the HTML file as text contents */ // function read_conf() { // /* conf variables, set to null at the start, set with read_conf */ // /* the nitpo address, found in the HTML id="nitpo_url" // if this is null, invoque read_conf */ // nitpo_url = read_a_conf('nitpo_url'); // mulsu_wait_start = read_a_conf('mulsu_wait_start'); // nitpo_wait_success = read_a_conf('mulsu_wait_success'); // mulsu_wait_failure = read_a_conf('mulsu_wait_failure'); // label_emcor = read_a_conf('label_emcor'); // } // // */ function emad_key(e) { if(typeof set_repcode === 'undefined') { return; } const eminp_elid = 'nitpo_' + set_repcode + '_emad_input'; const eminp_ele = document.getElementById(eminp_elid); const rebuto_elid = 'nitpo_' + set_repcode + '_rebuto'; const rebuto_ele = document.getElementById(rebuto_elid); const sigup_elid = 'nitpo_' + set_repcode + '_signup'; const sigup_ele = document.getElementById(sigup_elid); if(eminp_ele === null) { console.log('not found'); return; } eminp_text = eminp_ele.value; if(! is_it_emad(eminp_text)) { rebuto_ele.style.display = 'none'; sigup_ele.style.display = 'initial'; return; } rebuto_ele.style.display = 'initial'; rebuto_ele.textContent = "Sign up " + eminp_text; sigup_ele.style.display = 'none'; console.log(eminp_text); }; window.addEventListener('DOMContentLoaded', (event) => { /* find all elements in the class adrep to adjust width to the repcode */ if(! window.fetch) { console.log("fetch is not available, no buttons."); return; } // the above conditions checks if fetch is available. Now // we know that it is, let's make the on_demand elements // visible. on_demand_eles = document.getElementsByClassName('on_demand'); for (const on_demand_ele of on_demand_eles) { on_demand_ele.style.display = 'initial'; } // rebuto: add even on input eminp_eles = document.getElementsByClassName('emad_input'); for (const eminp_ele of eminp_eles) { addEventListener('keyup', emad_key); } // still run seperately, set the adrep elements to // be the minium width of e repcode adrep_eles = document.getElementsByClassName('adrep'); for (const adrep_ele of adrep_eles) { const elid = adrep_ele.getAttribute('id'); const repcode = get_repcode_from_elid(elid); repcode_ele = document.getElementById(repcode); if(repcode_ele === null) { continue } const dims = repcode_ele.getBoundingClientRect(); const width = dims['width']; // this test should be run for the subscribe buttons if(window.fetch) { adrep_ele.style.minWidth = width + 'px'; } else { adrep_ele.style.display = 'none'; } } // check whether this ran if(! document.getElementsByClassName('adrep')) { button_eles = document.getElementsByTagName('buttons'); for (const button_ele of button_eles) { button_ele.style.display = 'none'; } } }); function read_conf() { /* conf variables, set to null at the start, set with read_conf */ /* the nitpo address, found in the HTML id="nitpo_url" if this is null, invoque read_conf */ conf_ele = document.getElementById('mulsu_conf'); divs = conf_ele.getElementsByTagName('div'); for (const div of divs) { mulsu_conf[div.getAttribute('id')] = div.textContent; } // console.log('conf ' + mulsu_conf); } function read_a_conf(elid) { let ele = document.getElementById(elid); if(ele === null) { return null; } out = ele.textContent; return out; } /* called by HTML */ async function on_signup(button_ele) { /* must have id of form nitpo_REPCODE_signup */ /* read the configuration if the nitpo address is not set */ if(mulsu_conf['nitpo_url'] === undefined) { read_conf(); } elid = button_ele.getAttribute('id'); set_bare_text_on_former_selection(); clear_feedback(); /* if the email input on the current element has focus, we should */ /* do nothing here, but this is not (yet) implemented */ /* this is for setting at the start, should be onload= */ if(bare_signup_text === null) { bare_signup_text = button_ele.textContent; } const old_repcode = set_repcode; /* when the emad is not set, close the form there */ set_repcode = get_repcode_from_elid(elid); /* open the emad_input_form at start, or emad_input is not an emad */ if(set_emad === null) { if(old_repcode !== null) { close_emad_input_form(old_repcode); } /* special function to activate return */ const emad_input_id = 'nitpo_' + set_repcode + '_emad_input'; const emad_input_ele = document.getElementById(emad_input_id); if(emad_input_ele !== null) { add_char_treatment(emad_input_ele); // console.log("I add char treatment to " + emad_input_id); } else { // console.log("I can't find " + emad_input_id); } open_emad_input(button_ele); return false; } focus_on_signup(set_repcode); /* only redecorate if emad_signup is not up-to-date */ if((emad_signup !== null) && (emad_signup !== set_emad)) { /* remove the fidba */ // console.log('remove feeback'); const fidba_elid = 'nitpo_' + set_repcode + '_emad_fidba'; const fidba_ele = document.getElementById(fidba_elid); if(fidba_ele !== null) { fidba_ele.textContent = ''; fidba_ele.style.display = 'none'; } else { alert('no fidba'); } decorate_signups(emad_input); focus_on_signup(set_repcode); } const url = form_url(); console.log('CALL ' + url); button_ele.oldText = button_ele.textContents; button_ele.textContent = mulsu_conf['wait_start'] + ' ' + set_emad + "…"; const response = await fetchWithTimeout(url, button_ele, options = {}); let end = ''; if(! response.ok) { catastrophic(); } else { end = mulsu_conf['wait_success']; } const json_data = await response.json(); // Get JSON value from the response body const message = json_data['message']; if(message === null) { console.log('no message from server.'); return false; } const fidba_elid = 'nitpo_' + set_repcode + '_emad_fidba'; const fidba_ele = document.getElementById(fidba_elid); if(button_ele !== null) { fidba_ele.textContent = message; fidba_ele.style.display = 'initial'; } console.log('result ' + message); console.log('CALLed ' + url); button_ele.textContent += ' ' + end; button_ele.disable = true; button_ele.style.display = 'none'; return true; } /* called by HTML */ function on_emad_input(ele) { /* must have id of form nitpo_REPCODE_emad_input */ const elid = ele.getAttribute('id'); emad_input = ele.value.trim(); const is_emad = is_it_emad(emad_input); if(emad_input !== null) { ele.value = emad_input; } if(is_emad === false) { cant_signup(set_repcode, emad_input); set_emad = null; decorate_signups(); when_decosi('show'); return; } // console.log('* dig for emad' + elid); // set_emad = dig_for_emad(emad_input); set_emad = emad_input; // console.log('* docorate ' + elid); decorate_signups(); relabel_emad_input(); // console.log('clear feedback'); clear_feedback(); when_decosi('clear'); /* move current decosi into viewport */ const decosi_elid = 'nitpo_' + set_repcode + '_decosi'; decosi_ele = document.getElementById(decosi_elid); if(! is_it_in_viewport(decosi_ele)) { decosi_ele.scrollIntoView(); } ele.style.width = ele.value.length + 'ch'; if(mulsu_conf['do_adjust_input_to_decosi']) { /* adjust the emad_input to the length of the decosi, for decoration */ const emad_input_elid = 'nitpo_' + set_repcode + '_emad_input'; /* 8px is an approximation for the difference between input and button */ set_width(decosi_elid, emad_input_elid, '-8px'); } return false; } /* function called to disable further signup attempt after a non-200 response */ function catastrophic() { button_eles = document.getElementsByTagNameNS(html_ns, 'button'); for (const button_ele of button_eles) { button_ele.style.display = 'none'; } const label_elid = 'nitpo_' + set_repcode + '_emad_label'; label_ele = document.getElementById(label_elid) if(label_ele !== null) { label_ele.style.display = 'none'; } const emad_input_elid = 'nitpo_' + set_repcode + '_emad_input'; const emad_input_ele = document.getElementById(emad_input_elid); if(emad_input_ele !== null) { emad_input_ele.style.display = 'none'; } const emad_label_elid = 'nitpo_' + set_repcode + '_emad_label'; const emad_label_ele = document.getElementById(emad_label_elid); if(emad_label_ele !== null) { // console.log('hidden'); emad_label_ele.style.visibilty = 'hidden'; } const fidba_elid = 'nitpo_' + set_repcode + '_emad_fidba'; const ele = document.getElementById(fidba_elid); ele.textContent = mulsu_conf['catastrophic']; ele.style.display = 'initial'; } function clear_feedback() { fidba_eles = document.getElementsByClassName('fidba'); for (const fidba_ele of fidba_eles) { const elid = fidba_ele.getAttribute('id'); // console.log('clear_feedback ' + elid); fidba_ele.style.display = 'none'; } } function set_bare_text_on_former_selection() { /* happens of we don't have a valid email on it, before it is changed */ if(set_emad !== null) { return false; } if(set_repcode === null) { return false; } if(bare_signup_text === null) { return false; } relabel_emad_input(); const elid = 'nitpo_' + set_repcode + '_emad_input' let signup_button_ele = document.getElementById(elid); if(signup_button_ele === null) { // console.log('g 4'); return false; } // console.log('reset ' + set_repcode); signup_button_ele.textContent = bare_signup_text; // console.log('done reset ' + set_repcode + ' ' + bare_signup_text); return true; } // keep this for the moment // function dig_for_emad(string) { // /* look for the email in something like */ // /* Thomas Krichel */ // const words = string.split(" "); // let found = null; // for (const word of words) { // if(! is_it_emad(word)) { // continue; // } // found = word; // } // if(found === null) { // return null; // } // found = found.replace('<',''); // found = found.replace('>',''); // // console.log('dug emad "' + found + '"'); // return found; // } function form_url() { if(nitpo_url === null) { read_conf(); } nitpo_url = mulsu_conf['nitpo_url']; url = nitpo_url + '/' + set_repcode + '/' + set_emad; return url; } function open_emad_input(signup_ele) { let is_focus_set = false; /* make infid visible */ console.log('start'); const infid_elid = 'nitpo_' + set_repcode + '_infid'; const infid_ele = document.getElementById(infid_elid); if(infid_ele !== null) { infid_ele.style.display = 'initial'; } for (const labin of labins) { for (const field of fields) { const elid = 'nitpo_' + set_repcode + '_' + field + '_' + labin const size_elid = 'nitpo_' + set_repcode + '_hezel'; // console.log("looking for " + elid); const target_ele = document.getElementById(elid); if(target_ele === null) { continue; } /* use inline-block so we can fix the width */ target_ele.style.display = 'inline-block'; // 2022-09-01 try scale the label if(labin == 'label') { const hezel_elid = 'nitpo_' + set_repcode + '_hezel'; set_width(hezel_elid, elid); console.log(hezel_elid + " -> " + elid); } if(labin !== 'input') { continue; } // console.log('set focus in target_form ' + elid); // bad target here set_width(size_elid, elid); target_ele.focus(); is_focus_set = true; if(emad_input !== null) { target_ele.value = emad_input; } } } /* close_other_forms(set_repcode);*/ if(set_emad !== null && ! is_focus_set) { console.log('focus on ' + set_repcode); focus_on_signup(set_repcode); } console.log('done'); } function relabel_emad_input() { label_elid = 'nitpo_' + set_repcode + '_emad_label'; label_ele = document.getElementById(label_elid); label_ele.style.margin = '0px'; if(label_ele === null) { console.log('no ' + label_elid); return false; } /* save old contents */ if(label_emfal === null) { label_emfal = label_ele.textContent; } /* no email set ... revert to early stage */ /* emcor means email correct */ /* emfal means email false */ if(set_emad === null) { label_ele.classList.add('label_emfal'); label_ele.classList.remove('label_emcor'); label_ele.textContent = label_emfal; return; } console.log('emcor ' + label_elid + ' ' + mulsu_conf['label_emcor']); label_ele.classList.add('label_emcor'); label_ele.classList.remove('label_emfal'); // console.log('add ' + mulsu_conf['label_emcor']); label_ele.textContent = mulsu_conf['label_emcor']; return false; } function cant_signup(repcode, string) { const signup_elid = 'nitpo_' + repcode + '_signup'; const fidba_elid = 'nitpo_' + repcode + '_emad_fidba'; const input_elid = 'nitpo_' + repcode + '_emad_input'; const input_ele = document.getElementById(input_elid); /* set input length if(input_ele !== null) { const input_length = input_ele.value.length; // multiply by 1.25 for extra space const show_input = 1.25 * input_length; console.log('show_input' + show_input); input_ele.style.width = show_input + 'ch'; } */ /* if we have the fidba element, print fidba there */ const signup_ele = document.getElementById(signup_elid); const fidba_ele = document.getElementById(fidba_elid); if(signup_ele === null && fidba_ele === null) { return; } let target_ele = signup_ele; if(fidba_ele !== null) { prefix_blank(fidba_ele); target_ele = fidba_ele; target_ele.style.display = 'initial'; } fidba_text = mulsu_conf['cant_signup_prefix'] + string + mulsu_conf['cant_signup_postfix']; if(string === '') { /* just show the default */ signup_ele.textContent = bare_signup_text; target_ele.textContent = fidba_text; return false; } target_ele.textContent = fidba_text; if(repcode === set_repcode) { console.log('in cant, set focus on ' + input_elid); input_ele.focus(); } return false; } function focus_on_signup(repcode) { elid = 'nitpo_' + repcode + '_signup'; let signup_ele = document.getElementById(elid); if(signup_ele === null) { console.log("I can't see the element " + elid); return false; } signup_ele.focus(); signup_ele.value = emad_input; return false; } function close_emad_input_form(repcode) { if(set_repcode === null) { console.log("With no set repcode, I keep the input open."); return false; } for (const field of fields) { for (const labin of labins) { const elid = 'nitpo_' + repcode + '_' + field + '_' + labin; const target_ele = document.getElementById(elid); if(target_ele === null) { continue; } target_ele.style.display = 'none'; } } return true; } function when_decosi(string) { console.log('start when_decosi'); if(string == 'clear') { eles = document.getElementsByClassName('clear_when_decosi'); for (const ele of eles) { ele.style.display = 'none'; } return true; } if(string == 'show') { eles = document.getElementsByClassName('clear_when_decosi'); for (const ele of eles) { ele.style.display = 'initial'; } return true; } return false; } function decorate_signups() { relabel_emad_input(); // console.log('start of decorate at ' + set_repcode); button_eles = document.getElementsByTagNameNS(html_ns, 'button'); /* don't overwrite the current signup button */ const current_signup_elid = 'nitpo_' + set_repcode + '_signup'; /* decorated signup, if different */ const current_decosi_elid = 'nitpo_' + set_repcode + '_decosi'; const have_i_decosi = have_i(current_decosi_elid); for (const button_ele of button_eles) { if(button_ele.disabled) { continue; } const button_id = button_ele.getAttribute('id'); // console.log(button_id + ' ' + have_i_decosi); if(button_id.substr(0,6) !== 'nitpo_') { continue; } button_type = decosi_or_signup(button_ele); if(! ( button_type == 'signup' || button_type == 'decosi')) { console.log('bad button ' + button_id + ' ' + button_type); continue; } const repcode = get_repcode_from_elid(button_id); if(set_emad === null) { if(button_type === 'signup') { if(have_i_decosi) { button_ele.style.display = 'initial'; button_ele.textContent = bare_signup_text; } else if(button_ele.getAttribute('id') != current_signup_elid) { button_ele.textContent = bare_signup_text; } } if(button_type === 'decosi') { button_ele.style.display = 'none'; } } else { /* set the focus */ // console.log('set_emad'); button_ele.textContent = bare_signup_text + " " + set_emad; if(button_type === 'decosi') { // console.log('decosi ' + button_id); button_ele.style.display = 'initial'; if(repcode == set_repcode) { console.log('set focus in decorate ' + set_repcode + ' ' + button_id); //console.log(signup_button_ele); button_ele.focus(); } prefix_blank(button_ele); // var text = document.createTextNode(" "); // button_ele.parentNode.insertBefore(text, button_ele.nextSibling); // console.log("Inseerted"); } if(button_type === 'signup') { button_ele.textContent = bare_signup_text + " " + set_emad; if(have_i_decosi) { button_ele.style.display = 'none'; } else { /* just in case */ button_ele.style.display = 'initial'; } // repcode = get_repcode_from_elid(button_id); } } } emad_signup = set_emad; return true; } function prefix_blank(ele) { const text = document.createTextNode(" "); ele.parentNode.insertBefore(text, ele.nextSibling); // console.log("Inserted at " + ele); } function have_i(elid) { const out = document.getElementById(elid); console.log("have i " + elid); if(out === null) { return false; } return true; } /* decosi or signup ? */ function decosi_or_signup(ele) { elid = ele.getAttribute('id'); return elid.substr(-6); } function is_it_emad(string) { /* this is the same check as used server-side, don't change */ const is_emad = emad_regex.test(string); return is_emad; } /* function has_emad(string) { const last_part = last_word(string); if(is_it_emad(last_part)) { return last_part; } return null; } function last_word(anchor_string) { const words = anchor_string.split(" "); return words[words.length - 1]; } */ function get_repcode_from_elid(elid) { // starts with nitpo_ const from_repcode_start = elid.substring(6); const end_of_repcode = from_repcode_start.indexOf('_') const repcode = from_repcode_start.substring(0, end_of_repcode) return repcode; } async function call_fetch(url) { try { let res = await fetch(url); return await res; } catch (error) { console.log(error); } } /* from https://dmitripavlutin.com/timeout-fetch-reqpuest/ */ async function fetchWithTimeout(url, button_ele, options = {}) { const { timeout = mulsu_conf['nitpo_timeout'] } = options; const controller = new AbortController(); const timeout_id = setTimeout(() => controller.abort(), timeout); const response = await call_fetch(url, { signal: controller.signal }); clearTimeout(timeout_id); return response; } function add_char_treatment(ele) { ele.addEventListener('keyup', function(event) { const code = event.keyCode; if(! (code == 13 || code == 32)) { /* if(code != 13) { */ return; } const on_ele = document.activeElement; const on_elid = on_ele.getAttribute('id'); const to_elid = 'nitpo_' + set_repcode + '_signup'; const to_ele = document.getElementById(to_elid); on_ele.blur(); to_ele.focus(); }); return false; } function set_width(elid_from, elid_to, adjust_pixel) { if(typeof adjust_pixel === 'undefined') { adjust_pixel = '0px'; } const from = document.getElementById(elid_from); const to = document.getElementById(elid_to); if(from === null) { console.log("I can't see '" + elid_from + "'"); return; } const style = window.getComputedStyle(from); const target_width = style.getPropertyValue('width'); const target_number = parseInt(target_width); const adjust_number = parseInt(adjust_pixel); const width = target_number + adjust_number + 'px'; console.log('width set to ' + width + ' of ' + elid_from); to.style.width = width; } function is_it_in_viewport(ele) { if(ele === null) { return true; } /* https://gomakethings.com/how-to-test-if-an-element-is-in-the-viewport-with-vanilla-javascript/ */ var rect = ele.getBoundingClientRect(); return ( rect.top >= 0 && rect.left >= 0 && rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && rect.right <= (window.innerWidth || document.documentElement.clientWidth) ); } // rebuto addition