1import ApplicationController from "satis/controllers/application_controller" 2 3export default class FieldsForController extends ApplicationController { 4 static targets = ["insertionPoint", "template"] 5 6 connect() { 7 super.connect() 8 9 this.boundMonitorChanges = this.monitorChanges.bind(this) 10 this.boundMouseMove = this.mouseMove.bind(this) 11 this.addNewLine() 12 } 13 14 addAssociation(event) { 15 event.preventDefault() 16 17 // Find template and remove event listeners 18 let templateElement = this.insertionPointTarget.querySelector(".template") 19 templateElement.querySelectorAll("input,select").forEach((input) => { 20 input.removeEventListener("change", this.boundMonitorChanges) 21 }) 22 23 let tmpNode = event.target.closest(".nested-fields") 24 this.setupNewChild(tmpNode) 25 26 this.addNewLine() 27 28 window.scrollBy(0, this.element.querySelector(".nested-fields").clientHeight) 29 } 30 31 setupNewChild(tmpNode) { 32 tmpNode.classList.remove("template") 33 tmpNode.querySelectorAll(".association").forEach((item) => { 34 if (item.classList.contains("hidden")) { 35 if (!item.querySelector("a[data-action='click->satis-fields-for#cloneAssociation']")) { 36 item.classList.remove("hidden") 37 } 38 } else { 39 if (!item.querySelector("a[data-action='click->satis-fields-for#cloneAssociation']")) { 40 item.remove() 41 } else { 42 item.classList.add("hidden") 43 } 44 } 45 }) 46 47 // Simply replace every child node's attributes value, replacing TEMPLATE 48 let templateId = new Date().valueOf() 49 let templateName = tmpNode.querySelector("span.temp-name").getAttribute("temp_name") 50 tmpNode.querySelector("span.temp-id").setAttribute("temp_id", templateId) 51 tmpNode.querySelectorAll("*").forEach((node) => { 52 for (let attribute of node.attributes) { 53 attribute.value = attribute.value.replace(/TEMPLATE-NAME/g, `[${templateName}][${templateId}]`) 54 attribute.value = attribute.value.replace(/TEMPLATE-ID/g, `${templateName}_${templateId}`) 55 56 attribute.value = attribute.value.replace(/TEMPLATE/g, templateId) 57 } 58 }) 59 60 tmpNode.querySelectorAll("template").forEach((node) => { 61 node.innerHTML = node.innerHTML.replace(/TEMPLATE-NANE/g, `[${templateName}][${templateId}]`) 62 node.innerHTML = node.innerHTML.replace(/TEMPLATE-ID/g, `${templateName}_${templateId}`) 63 64 node.innerHTML = node.innerHTML.replace(/TEMPLATE/g, templateId) 65 }) 66 67 tmpNode.addEventListener("mousemove", this.boundMouseMove) 68 } 69 70 cloneAssociation(event) { 71 event.preventDefault() 72 73 let item = event.target.closest(".nested-fields") 74 let templateId = item.querySelector("span.temp-id").getAttribute("temp_id") 75 let templateName = item.querySelector("span.temp-name").getAttribute("temp_name") 76 77 let clonedItem = item.cloneNode(true) 78 79 clonedItem.querySelectorAll("*").forEach((node) => { 80 for (let attribute of node.attributes) { 81 attribute.value = attribute.value.replaceAll(`[${templateName}][${templateId}]`, "TEMPLATE-NAME") 82 attribute.value = attribute.value.replaceAll(`${templateName}_${templateId}`, "TEMPLATE-ID") 83 } 84 }) 85 86 //this.insertionPointTarget.appendChild(clonedItem) 87 this.insertionPointTarget.insertBefore( 88 clonedItem, 89 this.insertionPointTarget.childNodes[this.insertionPointTarget.childNodes.length - 1] 90 ) 91 92 clonedItem 93 .querySelector("a[data-action='click->satis-fields-for#removeAssociation']") 94 ?.parentElement.classList.remove("hidden") 95 clonedItem 96 .querySelector("a[data-action='click->satis-fields-for#cloneAssociation']") 97 ?.parentElement.classList.add("hidden") 98 this.setupNewChild(clonedItem) 99 } 100 101 addNewLine() { 102 this.insertionPointTarget.insertAdjacentHTML("beforeend", this.templateTarget.innerHTML) 103 104 // Find template and add event listeners 105 let templateElement = this.insertionPointTarget.querySelector(".template") 106 setTimeout(() => { 107 // add a delay so host can finish dom manipulation and everything is ready 108 templateElement.querySelectorAll("input,select").forEach((input) => { 109 input.addEventListener("change", this.boundMonitorChanges) 110 }) 111 }, 500) 112 } 113 114 mouseMove(event) {
115 let item = event.target.closest(".nested-fields") 116 117 if (event.altKey) { 118 item 119 .querySelector("a[data-action='click->satis-fields-for#removeAssociation']") 120 ?.parentElement.classList.add("hidden") 121 item 122 .querySelector("a[data-action='click->satis-fields-for#cloneAssociation']") 123 ?.parentElement.classList.remove("hidden") 124 } else { 125 item 126 .querySelector("a[data-action='click->satis-fields-for#removeAssociation']") 127 ?.parentElement.classList.remove("hidden") 128 item 129 .querySelector("a[data-action='click->satis-fields-for#cloneAssociation']") 130 ?.parentElement.classList.add("hidden") 131 } 132 } 133 134 monitorChanges(event) { 135 if (event?.detail?.src == "satis-dropdown") { 136 // Skip events caused by the initial load of a satis-dropdown 137 return 138 } 139 140 this.addAssociation(event) 141 } 142 143 removeAssociation(event) { 144 event.preventDefault() 145 let item = event.target.closest(".nested-fields") 146 item.querySelector("input[name*='_destroy']").value = 1 147 item.style.display = "none" 148 } 149};
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.