function CR_formdataappend(v1,v2) { if (v1 == ""){return v2;}; if (v2 == ""){return v1;}; return v1+"&"+v2; } function CR_objectToFormData(obj) { var formstring = ""; for(var property in obj) { if(obj.hasOwnProperty(property)) { // if the property is an object, but not a File, // use recursivity. if(typeof obj[property] === 'object') { formstring = CR_formdataappend(formstring,CR_objectToFormData(obj[property])); } else { // if it's a string or a File object formstring = CR_formdataappend(formstring,property+"="+obj[property]); } } } return formstring; }; function CR_post(link,postdata,callback=null,callbackdata=null) { var xmlhttp = new XMLHttpRequest(); xmlhttp.onreadystatechange = function() { if (this.readyState == 4 && this.status == 200) { if (callback !== null) callback(this.responseText,callbackdata); } }; xmlhttp.open("POST", link, true); xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); if (postdata && typeof postdata === 'object') { xmlhttp.send(CR_objectToFormData(postdata)); } else xmlhttp.send(postdata); }; var cr_tables=[]; cr_tables['update_delay']=2000; var cr_table_log=false; var cr_modal, cr_modal_text; function cr_cookies_get() { return document.cookie.split(';').map(function split(e) { var tmp = e.trim().split('='); return tmp; }); } function cr_cookies_index(needle, cks=null) { if ( cks == null ) cks = cr_cookies_get(); let keys = cks.map(function primul(e) {return e[0]}); return keys.indexOf(needle); } function table_parse_config(name,cfg) { cr_tables[name]=cfg; cr_tables[name]["columns_visible"]=new Array(); cr_tables[name]["columns_sort"]=new Array(); cr_tables[name]["columns_filter"]=new Array(); cr_tables[name]["TotalRecordCount"]=0; cr_tables[name]["pageNo"]=1; if (cr_tables[name]['actions']['insert']) { if (cr_tables[name]['insert_label']) cr_tables[name]['insert_label']= ' '+cr_tables[name]['insert_label']; else cr_tables[name]['insert_label']= ' Add line'; } if (cr_tables[name]["paging"]) { if (isNaN(cr_tables[name]["pageSize"])) cr_tables[name]["pageSize"]=50; if ((!cr_tables[name]["pageSizes"]) || (cr_tables[name]["pageSizes"].constructor !== Array)) cr_tables[name]["pageSizes"]=[25,50,100,200,500,1000]; } for(var propt in cr_tables[name]["columns"]) { if (! cr_tables[name]["columns"][propt].hasOwnProperty("id")) delete cr_tables[name]["columns"][propt]; else { if (!(cr_tables[name]["columns"][propt]["show"]===false)) cr_tables[name]["columns_visible"].push(propt); if (cr_tables[name]["columns"][propt]["key"]===true) cr_tables[name]["key"]=cr_tables[name]["columns"][propt]["id"]; } } } function table_create(name,divid,cfg) { var cookies_array = cr_cookies_get(); table_parse_config(name,cfg); cr_tables[name]['divid']=document.getElementById(divid); cr_tables[name]['divid'].classList.add('cr_table_div'); var tbl = document.createElement('table'); cr_tables[name]['tbl']=tbl; tbl.classList.add("cr_table"); // table header name var th = tbl.createTHead(); var tr = th.insertRow(); var td = tr.insertCell(); if (cr_tables[name]['actions']['print']) td.innerHTML = cr_tables[name]['title'] + "
"; else td.innerHTML = cr_tables[name]['title']; td.classList.add('cr_table_title'); td.setAttribute('colSpan', cr_tables[name]["columns_visible"].length); if (cr_tables[name]['actions']['insert']) { var addrow=document.createElement("div"); addrow.style.float = "right" addrow.style.cursor='copy' addrow.innerHTML=cr_tables[name]['insert_label']; addrow.setAttribute("onclick", "cr_click_add_row('" + name + "')"); td.appendChild(addrow); } // if we have filtering, need to create the modal window if (cr_tables[name]["filtering"] && (! cr_modal)) { cr_modal=document.createElement("div"); cr_modal.className="modal"; var cd=document.createElement("div"); cd.className="modal-content"; cr_modal.appendChild(cd); var cls=document.createElement("span"); cls.className="close"; cls.innerHTML="×"; cls.setAttribute("onclick", "cr_close_filter_button()"); cd.appendChild(cls); var pg=document.createElement("p"); pg.innerHTML="Filter:"; cd.appendChild(pg); cr_modal_filterdiv = document.createElement("div"); cd.appendChild(cr_modal_filterdiv); var okbtn = document.createElement("input"); okbtn.setAttribute("id", "cr_modal_btn"); okbtn.setAttribute("type", "button"); okbtn.setAttribute("name", "ok" ); okbtn.setAttribute("value", "Apply Filter" ); okbtn.setAttribute("onclick", "cr_close_filter_button()"); cd.appendChild(okbtn); document.body.appendChild(cr_modal); } // table header columns var tr = th.insertRow(); for(var j = 0; j < cr_tables[name]["columns_visible"].length; j++) { var col=cr_tables[name]["columns"][cr_tables[name]["columns_visible"][j]]; var td = tr.insertCell(); td.appendChild(document.createTextNode(col["title"])); if (col.width) td.style.width=col.width; var thbtnsdiv=document.createElement("div"); //add the sorting buttons on header row if (cr_tables[name]["sorting"] && (col["sorting"]===true)) { var tdata="\u21F3"; if (col["sort"]=='asc') tdata="\u21E7"; else if (col["sort"]=='desc') { tdata="\u21E9";} if ( (tempindex = cr_cookies_index(`cr_table_sort_${name}_${col["id"]}`,cookies_array))>-1) tdata=cookies_array[tempindex][1]; var btsort = document.createElement("button"); btsort.name = "btnsort"+col["id"]; btsort.dataset['cid']=col["id"]; btsort.value = cr_tables[name]["columns_visible"][j]; btsort.setAttribute("onclick", "cr_click_sorting_button('" + name + "', this)"); btsort.innerHTML=tdata; cr_tables[name]["columns_sort"].push(btsort); thbtnsdiv.appendChild(btsort); } //add the filter buttons on top of header row if (cr_tables[name]["filtering"] && (col["filter"]===true)) { var btnfilter = document.createElement("button"); btnfilter.name = "btnfilter"+col["id"]; btnfilter.dataset['cid']=col["id"]; btnfilter.dataset['search']=''; if ( (tempindex = cr_cookies_index(`cr_table_filter_${name}_${col["id"]}`,cookies_array))>-1) btnfilter.dataset['search']=cookies_array[tempindex][1]; btnfilter["FT"] = "IN"; if (!col["filtertype"]) col["filtertype"]="search"; if ( (col["filtertype"]=="search") || (col["filtertype"]=="date")) { fltr = document.createElement("input"); fltr.setAttribute("type", col["filtertype"]); fltr.setAttribute("name", "cr_modal" ); fltr.value=btnfilter.dataset['search']; if (btnfilter.dataset['search'] !='') btnfilter.style.background="red"; fltr.addEventListener("keyup", function(event) {event.preventDefault(); if (event.keyCode === 13) {cr_close_filter_button();} }); btnfilter['filtru']=fltr; btnfilter.dataset['type']=col["filtertype"]; } if (col["filtertype"]=="checkbox") { fltrdiv = document.createElement("div"); for(i=0; i`; } //if it is an object - drop down list we create the element else if ( typeof(cr_tables[name]["columns"][columnid]['values'])=="object" ) { var opts=cr_tables[name]["columns"][columnid]['values']; new_tbody += ``; } else {//else we create the edit as an input string new_tbody += ``; } } else {//the cell is not editable so we ad it as an paragraph new_tbody += `

${datashow}

`; } } new_tbody += ""; } if (cr_tables[name]["grandtotal"]) { new_tbody += `

TOTAL:

`; new_tbody += `

0

`; } if (data["Updates"]) cr_table_ajax_update_process_data(data["Updates"]); } //console.log(new_tbody); //clear any rows tbl.innerHTML=new_tbody; } //if an element was changed we reset the update request back to the timeout value function cr_table_input_change(name,element) { element.style.background="#fdd"; tmid = setTimeout(cr_table_do_ajax_update, cr_tables['update_delay'], name, element); if (element.dataset.timer>0) { clearTimeout(element.dataset.timer); } element.dataset.timer=tmid; } //function called with the return data from an ajax update request to update values in table function cr_table_ajax_update_process_data(data) { for(var dr = 0; dr < data.length; dr++) { var ky = Object.keys(data[dr])[0]; var ppp=document.getElementById(ky); switch (ppp.tagName) { case "P": ppp.textContent=data[dr][ky]; break; case "INPUT": ppp.value=data[dr][ky]; break; } } } //function called to update the cell on server by ajax request function cr_table_do_ajax_update(name, element) { if (cr_table_log) document.getElementById("log").appendChild(document.createTextNode(element.dataset.key + " ~ " + element.name + " ~ " + element.value + "\n")); element.style.background=""; element.dataset.timer=0; var http = new XMLHttpRequest(); // xmlhttp.open("POST", cr_tables[name]["actions"]["update"], true); var url = cr_tables[name]["actions"]["update"]; if ((element.tagName=="INPUT") && (element.type == "checkbox")) element.value=( element.checked ? 1 : 0); var params = "ky="+element.dataset.key + "&cl=" + element.name + "&nv=" + element.value; http.open("POST", url, true); //Send the proper header information along with the request http.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); http.onreadystatechange = function() {//Call a function when the state changes. if(http.readyState == 4 && http.status == 200) { var rspns = JSON.parse(http.responseText); if (rspns.Updates) cr_table_ajax_update_process_data(rspns.Updates); if (cr_table_log) document.getElementById("log").appendChild(document.createTextNode(http.responseText + "\n")); } } http.send(params); } //reload data from ajax provider function cr_table_reload_fromserver(name) { var xmlhttp = new XMLHttpRequest(); var postvar = 'u=cri'; xmlhttp.onreadystatechange = function() { if (this.readyState == 4 && this.status == 200) { var myObj = JSON.parse(this.responseText); cr_table_update(name,myObj); cr_update_nav_div_buttons(name); } }; if (cr_tables[name]["paging"]) { postvar += "&rangepage="+cr_tables[name]["currentPage"] postvar += "&rangesize="+cr_tables[name]["pageSize"]; } if (cr_tables[name]["sorting"]) { for(var dr = 0; dr < cr_tables[name]["columns_sort"].length; dr++) { if (cr_tables[name]["columns_sort"][dr].innerHTML=="\u21E7") postvar += "&sort[]=A"+cr_tables[name]["columns_sort"][dr].dataset['cid']; if (cr_tables[name]["columns_sort"][dr].innerHTML=="\u21E9") postvar += "&sort[]=D"+cr_tables[name]["columns_sort"][dr].dataset['cid']; } } if (cr_tables[name]["filtering"]) { for(var dr = 0; dr < cr_tables[name]["columns_filter"].length; dr++) { if (cr_tables[name]["columns_filter"][dr].dataset['search']!="") { postvar += "&filter[]=FLTR"+cr_tables[name]["columns_filter"][dr].dataset['cid']; postvar += "&FLTR"+ cr_tables[name]["columns_filter"][dr].dataset['cid']+"="+cr_tables[name]["columns_filter"][dr].dataset['search']; } } } xmlhttp.open("POST", cr_tables[name]["actions"]["list"], true); xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); xmlhttp.send(postvar); } function cr_click_add_row(name) { var xmlhttp = new XMLHttpRequest(); xmlhttp.onreadystatechange = function() { if (this.readyState == 4 && this.status == 200) { var myObj = JSON.parse(this.responseText); cr_table_update(name,myObj); cr_tables[name]["navupdaterequired"]=true; cr_update_nav_div_buttons(name); } }; var postvar="rangestart="+(cr_tables[name]["pageNo"]-1)*cr_tables[name]["pageSize"]; postvar += "&rangesize="+cr_tables[name]["pageSize"]; xmlhttp.open("POST", cr_tables[name]["actions"]["insert"], true); xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); xmlhttp.send(postvar); } //------------------ page navigation buttons and drop downs--------------------- //function called when navigation buttons are pressed function cr_click_nav_buttons(name,btn) { cr_tables[name]["currentPage"]=btn; cr_tables[name]["navupdaterequired"]=true; cr_table_reload_fromserver(name); } function cr_click_nav_pagesize(name,newsize=-1) { if (newsize>0) cr_tables[name]["pageSize"]=newsize; cr_tables[name]["navupdaterequired"]=true; cr_table_reload_fromserver(name); } function cr_update_nav_div_buttons(name) { if (cr_tables[name]["navupdaterequired"]) { let nopages = ~~((cr_tables[name]["TotalRecordCount"]-1)/cr_tables[name]["pageSize"])+1; let options = Array(); let currentpage=cr_tables[name]["currentPage"]; options.push(currentpage); options.push(currentpage-1,currentpage+1); options.push(currentpage-2,currentpage+2); options.push(currentpage-10,currentpage+10); options.push(currentpage-100,currentpage+100); options.push(currentpage-1000,currentpage+1000); options.push(currentpage-10000,currentpage+10000); options.push(currentpage-100000,currentpage+100000); options.push(1,nopages); options = options.filter(item => (item > 0) && (item <= nopages)).sort((a, b) => a - b).filter((v, i, a) => a.indexOf(v) === i); var stringBuilder = ["Page: "]; for(var i = 0; i < options.length; i++) { var val = ``; stringBuilder.push(val); } stringBuilder.push(`
Lines/page: `); for(var i = 0; i < cr_tables[name]["pageSizes"].length; i++) { var val = ``; stringBuilder.push(val); } cr_tables[name]["navDiv"].innerHTML=stringBuilder.join(''); cr_tables[name]["navupdaterequired"]=false; } } //-------------- sorting buttons ------------------------------- function cr_click_sorting_button(name,btn) { var btntext=btn.innerHTML; var tdata; if (btntext=="\u21F3") tdata="\u21E7"; if (btntext=="\u21E7") tdata="\u21E9"; if (btntext=="\u21E9") tdata="\u21F3"; btn.innerHTML = tdata; cr_tables[name]["navupdaterequired"]=true; cr_table_reload_fromserver(name); document.cookie = `cr_table_sort_${name}_${btn.dataset.cid}=${tdata}`; } //-------------- filter buttons ------------------------------- function cr_click_filter_button(name,btn) { cr_modal_filterdiv.innerHTML=""; cr_modal_filterdiv.append(btn['filtru']); cr_modal.dataset['index']=btn.dataset['index']; cr_modal.dataset['name']=name; cr_modal.style.display="block"; btn['filtru'].focus(); } function cr_close_filter_button() { var name=cr_modal.dataset['name']; var btn=cr_tables[name]["columns_filter"][cr_modal.dataset['index']]; cr_modal.style.display="none"; switch (btn.FT) { case "IN": if (btn.filtru.value != btn.dataset.search) { btn.dataset.search = btn.filtru.value; cr_tables[name]["navupdaterequired"]=true; cr_table_reload_fromserver(name); } break; case "BC": list = []; for (var i = 0; i< btn.filtru.children.length; i++) { if (btn.filtru.children[i].checked) list.push(btn.filtru.children[i].value); } if (list.join() != btn.dataset.search) { btn.dataset.search = list.join(); cr_tables[name]["navupdaterequired"]=true; cr_table_reload_fromserver(name); } break; } if (btn.dataset.search !='') btn.style.background="red"; else btn.style.background=""; document.cookie = `cr_table_filter_${name}_${btn.dataset.cid}=${btn.dataset.search}`; } function cr_click_print(name) { var xmlhttp = new XMLHttpRequest(); var postvar = ''; if (cr_tables[name]["sorting"]) { for(var dr = 0; dr < cr_tables[name]["columns_sort"].length; dr++) { if (cr_tables[name]["columns_sort"][dr].innerHTML=="\u21E7") postvar += "&sort[]=A"+cr_tables[name]["columns_sort"][dr].dataset['cid']; if (cr_tables[name]["columns_sort"][dr].innerHTML=="\u21E9") postvar += "&sort[]=D"+cr_tables[name]["columns_sort"][dr].dataset['cid']; } } if (cr_tables[name]["filtering"]) { for(var dr = 0; dr < cr_tables[name]["columns_filter"].length; dr++) { if (cr_tables[name]["columns_filter"][dr].dataset['search']!="") { postvar += "&filter[]=FLTR"+cr_tables[name]["columns_filter"][dr].dataset['cid']; postvar += "&FLTR"+ cr_tables[name]["columns_filter"][dr].dataset['cid']+"="+cr_tables[name]["columns_filter"][dr].dataset['search']; } } } xmlhttp.open("POST", cr_tables[name]["actions"]["print"], true); xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); xmlhttp.responseType = 'blob'; xmlhttp.onload = function () { var blob = this.response; var contentDispo = this.getResponseHeader('Content-Disposition'); var fileName = contentDispo.match(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/)[1]; fileName = fileName.replace(/"/g,""); saveBlob(blob, fileName); } xmlhttp.send(postvar); } function saveBlob(blob, fileName) { var a = document.createElement('a'); a.href = window.URL.createObjectURL(blob); a.download = fileName; a.dispatchEvent(new MouseEvent('click')); } function showlogout() { if (confirm("Are you sure you want to leave?")) { CR_post("/main/logout.php",'',function() {window.location.href="/main/index.html";}) } }; //showlogout function chg_pass() { $.msgBox({ type: "prompt", title: "Change Password", inputs: [{ header: "Current Password", type: "password", name: "cp" }, { header: "New Password (at least 5 characters)", type: "password", name: "p1" }, { header: "Confirm New Password", type: "password", name: "p2" }], content: "Are you sure you want to change your password?", buttons: [{ value: "Yes" }, { value: "No"}], success: function (result, values) { if (result=="Yes") { if(values[0]["value"] == '') { alert("You need to provide your current Password"); return false; } if(values[1]["value"].length < 5 ) { alert("Please enter New Password, minimum 5 chars"); return false; } if(values[1]["value"] != values[2]["value"]) { alert("New Password and Confirm New Password should be same"); return false; } $.ajax({ type: "POST", data: "oldpass=" + values[0]["value"] + "&newpass=" + values[1]["value"], url: "/ajax/main/chpasswd_do.php", cache: false}).done(function(msg) { alert(msg); }); } } }); }; //showlogout class CustomSelect extends HTMLElement { constructor() { super(); // Create shadow DOM this.attachShadow({ mode: 'open' }); // Create HTML elements this.shadowRoot.innerHTML = `
`; // Elements this.inputElement = this.shadowRoot.querySelector('.custom-select-input'); this.dropdownElement = this.shadowRoot.querySelector('.custom-select-dropdown'); // Data this.options = []; this.filteredOptions = []; // Event listeners this.inputElement.addEventListener('input', this.handleInput.bind(this)); this.dropdownElement.addEventListener('click', this.handleOptionSelect.bind(this)); } connectedCallback() { // Get options from attributes const optionsAttribute = this.getAttribute('options'); if (optionsAttribute) { this.options = JSON.parse(optionsAttribute); this.filteredOptions = this.options; this.renderOptions(); } } handleInput(event) { const searchTerm = event.target.value.trim().toLowerCase(); if (searchTerm.length > 2) { // Make AJAX request to fetch options this.fetchOptions(searchTerm) .then(options => { this.options = options; this.filteredOptions = this.options; this.renderOptions(); }) .catch(error => { console.error('Error fetching options:', error); }); } else { // Reset options this.options = []; this.filteredOptions = []; this.renderOptions(); } } handleOptionSelect(event) { const selectedOptionText = event.target.textContent; const selectedOption = this.options.find(option => option.text === selectedOptionText ); if (selectedOption) { this.inputElement.value = selectedOption.text; this.dispatchEvent(new CustomEvent('select', { detail: selectedOption })); } this.filteredOptions = this.options; this.renderOptions(); } fetchOptions(searchTerm) { return new Promise((resolve, reject) => { const url = `https://example/ajaxrequest.php?search=${encodeURIComponent(searchTerm)}`; const xhr = new XMLHttpRequest(); xhr.open('GET', url); xhr.onload = () => { if (xhr.status === 200) { try { const response = JSON.parse(xhr.responseText); resolve(response.options); } catch (error) { reject(error); } } else { reject(new Error(`Request failed with status ${xhr.status}`)); } }; xhr.onerror = () => { reject(new Error('Request failed')); }; xhr.send(); }); } renderOptions() { this.dropdownElement.innerHTML = this.filteredOptions .map(option => `
${option.text}
`) .join(''); } } // Define the custom element window.customElements.define('custom-select', CustomSelect); class EnhanancedSelect extends HTMLElement { constructor() { super(); this.value = 666; this.innerHTML += `
Select..
`; const selected = this.querySelector(".selected"); const optionsContainer = this.querySelector(".options-container"); const searchBox = this.querySelector(".select-box input"); selected.addEventListener("click", (e) => { optionsContainer.style.display="block"; searchBox.style.display="block"; searchBox.value = ""; searchBox.focus(); selected.classList.add("active"); }); this._datalist = this.querySelector('datalist'); this._allowedValues = []; if ( this._datalist.options.length > 0 ) { var options = ''; for (const option of this._datalist.options) { options += '
'+ option.innerHTML +'
'; this._allowedValues.push(option.value); if (option.hasAttribute('selected')){ //TODO in caz de valoare implicita, trebuie procesat selected.innerHTML = option.innerHTML; this.value = option.id; } } optionsContainer.innerHTML = options; } this.optionsList = this.querySelectorAll(".option"); // this.optionsList.addEventListener('click', this.handleOptionSelect.bind(this)); this.optionsList.forEach(o => { o.addEventListener("click", () => { selected.innerHTML = o.innerHTML; this.value = o.id; optionsContainer.style.display="none"; searchBox.style.display="none"; this.dispatchEvent(new CustomEvent('change', { detail: this.value })); }); }); searchBox.optionsList = this.optionsList; searchBox.addEventListener("keyup", function(e) { let searchTerm=e.target.value; searchTerm = searchTerm.toLowerCase(); e.currentTarget.optionsList.forEach(option => { let label = option.innerText.toLowerCase(); if (label.indexOf(searchTerm) != -1) { option.style.display = "block"; } else { option.style.display = "none"; } }); }); searchBox.addEventListener("focusout", function(e) { searchBox.style.display="none"; selected.classList.remove("active"); setTimeout(() => { optionsContainer.style.display="none"; }, 500); }); this._ajaxenable=false; if (this.hasAttribute('data-source')) { this._ajaxsource = this.getAttribute('data-source'); if (this.hasAttribute('data-filter')) this._ajaxfilter = this.getAttribute('data-filter'); else this._ajaxfilter = ''; this._ajaxenable=true; }; this._allowuserinput=false; if (this.hasAttribute('data-userinput')) { this._allowuserinput=true; } } } window.customElements.define('enhanced-select', EnhanancedSelect); const grid_no_links=4, points_subdivision=50; var width, height, canvas, ctx, points, points_subdivisionY=50; // Main initGrid(); addListeners(); drawGrid(); function initGrid() { canvas = document.getElementById('grid_bg'); height = window.innerHeight width = window.innerWidth canvas.width = width; canvas.height = height; ctx = canvas.getContext('2d'); points_subdivisionY=Math.round(points_subdivision*height/width); // create points points = []; for(var x = 0; x < points_subdivision; x++) { for(var y = 0; y < points_subdivisionY; y++) { var px = x/points_subdivision + Math.random()/points_subdivision; var py = y/points_subdivisionY + Math.random()/points_subdivisionY; var p = {x: px, originX: px, y: py, originY: py }; points.push(p); } } // for each point find the required closest pointsto draw lines for(var i = 0; i < points.length; i++) { var closest = []; var p1 = points[i]; for(var j = 0; j < points.length; j++) { var p2 = points[j] if(!(p1 == p2)) { var placed = false; for(var k = 0; k < grid_no_links; k++) { if(!placed) { if(closest[k] == undefined) { closest[k] = p2; placed = true; } } } for(var k = 0; k < grid_no_links; k++) { if(!placed) { if(getDistance(p1, p2) < getDistance(p1, closest[k])) { closest[k] = p2; placed = true; } } } } } p1.closest = closest; } // assign a circle to each point for(var i in points) { var c = new Circle(points[i], 2+Math.random()*2, 'rgba(255,255,255,0.3)'); points[i].circle = c; points[i].active = Math.random()/3; points[i].circle.active = Math.random()/2; } } function addListeners() { window.addEventListener('resize', resize); } function resize() { width = window.innerWidth; height = window.innerHeight; canvas.width = width; canvas.height = height; drawGrid(); } function drawGrid() { ctx.clearRect(0,0,width,height); for(var i in points) { drawLines(points[i]); points[i].circle.draw(); } } function drawLines(p) { if(!p.active) return; for(var i in p.closest) { ctx.beginPath(); ctx.moveTo(p.x*width, p.y*height); ctx.lineTo(p.closest[i].x*width, p.closest[i].y*height); ctx.strokeStyle = 'rgba(156,217,249,'+ p.active+')'; ctx.stroke(); } } function Circle(pos,rad,color) { var _this = this; // constructor (function() { _this.pos = pos || null; _this.radius = rad || null; _this.color = color || null; })(); this.draw = function() { if(!_this.active) return; ctx.beginPath(); ctx.arc(_this.pos.x*width, _this.pos.y*height, _this.radius, 0, 2 * Math.PI, false); ctx.fillStyle = 'rgba(156,217,249,'+ _this.active+')'; ctx.fill(); }; } function getDistance(p1, p2) { return Math.pow(p1.x - p2.x, 2) + Math.pow((p1.y - p2.y)*height/width, 2); } class MultiInput extends HTMLElement { constructor() { super(); // This is a hack :^(. // ::slotted(input)::-webkit-calendar-picker-indicator doesn't work in any browser. // ::slotted() with ::after doesn't work in Safari. this.innerHTML += ``; this._shadowRoot = this.attachShadow({mode: 'open'}); this._shadowRoot.innerHTML = ` `; const input = document.createElement('input') input.type = "hidden"; input.classList.add("hidden-input"); this.appendChild(input); this._forminput=input; input.value = "[]"; if (this.hasAttribute('data-name')){ input.name = this.getAttribute('data-name'); } this._ajaxenable=false; if (this.hasAttribute('data-source')) { this._ajaxsource = this.getAttribute('data-source'); if (this.hasAttribute('data-filter')) this._ajaxfilter = this.getAttribute('data-filter'); else this._ajaxfilter = ''; this._ajaxenable=true; }; this._allowuserinput=false; if (this.hasAttribute('data-userinput')) { this._allowuserinput=true; } this._datalist = this.querySelector('datalist'); this._allowedValues = []; for (const option of this._datalist.options) { this._allowedValues.push(option.value); if (option.hasAttribute('selected')){ const item = document.createElement('div'); item.classList.add('item'); item.textContent = option.value; this.insertBefore(item, this._input); } } this._forminput.value = this.getValues(); this._input = this.querySelector('input'); this._input.onblur = this._handleBlur.bind(this); this._input.oninput = this._handleInput.bind(this); this._input.onkeydown = (event) => { this._handleKeydown(event); }; this._input.onkeyup = (event) => { this._handleKeyup(event); }; this._allowDuplicates = this.hasAttribute('allow-duplicates'); }; _ajaxcallback(data,multiinputobject){ multiinputobject._allowedValues = []; while (multiinputobject._datalist.firstChild && !multiinputobject._datalist.firstChild.remove()); var obj = JSON.parse( data ); for( let prop in obj ){ multiinputobject._allowedValues.push(obj[prop]); const option = document.createElement('option'); option.value = obj[prop]; multiinputobject._datalist.appendChild(option); } }; //called when input is 2 or more characters and we have an ajax address configured _ajaxupdateallowedvalues() { const xmlhttp = new XMLHttpRequest(); const callback = this._ajaxcallback; const multiinputobject = this; xmlhttp.onreadystatechange = function() { if (this.readyState == 4 && this.status == 200) { callback(this.responseText,multiinputobject); } }; xmlhttp.open("POST", this._ajaxsource, true); xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); var searchstring = this._ajaxfilter; if (searchstring != '') searchstring += '=' + this._input.value xmlhttp.send(searchstring); }; // Called by _handleKeydown() when the value of the input is an allowed value. _addItem(value) { if (value =='') return; this._input.value = ''; const item = document.createElement('div'); item.classList.add('item'); item.textContent = value; this.insertBefore(item, this._input); item.onclick = () => { this._deleteItem(item); }; // Remove value from datalist options and from _allowedValues array. // Value is added back if an item is deleted (see _deleteItem()). if (!this._allowDuplicates) { for (const option of this._datalist.options) { if (option.value === value) { option.remove(); }; } this._allowedValues = this._allowedValues.filter((item) => item !== value); } this._forminput.value = this.getValues(); } // Called when the × icon is tapped/clicked or // by _handleKeydown() when Backspace is entered. _deleteItem(item) { const value = item.textContent; item.remove(); // If duplicates aren't allowed, value is removed (in _addItem()) // as a datalist option and from the _allowedValues array. // So — need to add it back here. if (!this._allowDuplicates) { const option = document.createElement('option'); option.value = value; // Insert as first option seems reasonable... this._datalist.insertBefore(option, this._datalist.firstChild); this._allowedValues.push(value); } } // Avoid stray text remaining in the input element that's not in a div.item. _handleBlur() { if (this._allowuserinput) { const option = document.createElement('option'); option.value = this._input.value; // Insert as first option seems reasonable... this._datalist.insertBefore(option, this._datalist.firstChild); this._allowedValues.push(this._input.value); this._addItem(this._input.value); } this._input.value = ''; } // Called when input text changes, // either by entering text or selecting a datalist option. _handleInput() { // Add a div.item, but only if the current value // of the input is an allowed value const value = this._input.value; if (this._allowedValues.includes(value)) { this._addItem(value); } } // Called when text is entered or keys pressed in the input element. _handleKeydown(event) { const itemToDelete = event.target.previousElementSibling; const value = this._input.value; // On Backspace, delete the div.item to the left of the input if (value ==='' && event.key === 'Backspace' && itemToDelete) { this._deleteItem(itemToDelete); // Add a div.item, but only if the current value // of the input is an allowed value } else if (this._allowedValues.includes(value)) { this._addItem(value); }; } // Called when text is entered or keys pressed in the input element. _handleKeyup(event) { const value = this._input.value; if (this._ajaxenable && value.length>1) { this._ajaxupdateallowedvalues(); } } // Public method for getting item values as an array. getValues() { const values = []; const items = this.querySelectorAll('.item'); for (const item of items) { values.push(item.textContent); } return values; } } window.customElements.define('multi-input', MultiInput);