Reorder form entries

Change-Id: I47b2ed843cf3cfef2e1ebf766c576d661da0579a
diff --git a/cmd/koralmapper/main_test.go b/cmd/koralmapper/main_test.go
index 0d1d2cf..067dd4c 100644
--- a/cmd/koralmapper/main_test.go
+++ b/cmd/koralmapper/main_test.go
@@ -1937,16 +1937,20 @@
 	assert.Contains(t, htmlContent, `/static/style.css`)
 	assert.Contains(t, htmlContent, `/static/config.js`)
 
-	// Annotation mapping section
-	assert.Contains(t, htmlContent, "Query")
+	// Request/response sections
+	assert.Contains(t, htmlContent, "<h2>Request</h2>")
+	assert.Contains(t, htmlContent, "<h2>Response</h2>")
+
+	// Annotation mapping entries
+	assert.Contains(t, htmlContent, "(query) anno-mapper")
 	assert.Contains(t, htmlContent, `data-id="anno-mapper"`)
 	assert.Contains(t, htmlContent, `data-type="annotation"`)
 	assert.Contains(t, htmlContent, `value="opennlp"`)
 	assert.Contains(t, htmlContent, `value="upos"`)
 	assert.Contains(t, htmlContent, "Annotation mapping")
 
-	// Corpus mapping section
-	assert.Contains(t, htmlContent, "Corpus")
+	// Corpus mapping entries
+	assert.Contains(t, htmlContent, "(corpus) corpus-mapper")
 	assert.Contains(t, htmlContent, `data-id="corpus-mapper"`)
 	assert.Contains(t, htmlContent, `data-type="corpus"`)
 	assert.Contains(t, htmlContent, "Corpus mapping")
diff --git a/cmd/koralmapper/static/config.html b/cmd/koralmapper/static/config.html
index 6e71be1..3eda7ee 100644
--- a/cmd/koralmapper/static/config.html
+++ b/cmd/koralmapper/static/config.html
@@ -15,25 +15,39 @@
             <p>{{.Description}}</p>
         </section>
 
-        {{if .AnnotationMappings}}
         <section class="mapping-section">
-            <h2>Query</h2>
+            <h2>Request</h2>
             {{range .AnnotationMappings}}
-            <div class="mapping" data-id="{{.ID}}" data-type="annotation"
+            <div class="mapping" data-id="{{.ID}}" data-type="annotation" data-mode="request"
                  data-default-foundry-a="{{.FoundryA}}" data-default-layer-a="{{.LayerA}}"
                  data-default-foundry-b="{{.FoundryB}}" data-default-layer-b="{{.LayerB}}">
-                <h3>{{.ID}}</h3>
-                {{if .Description}}<p class="desc">{{.Description}}</p>{{end}}
                 <div class="mapping-row">
-                    <label><input type="checkbox" class="request-cb" name="request"> Request</label>
+                    <label><input type="checkbox" class="request-cb" name="request"> (query) {{.ID}}</label>
                     <div class="mapping-fields request-fields">
                         <input type="text" class="request-foundryA" value="{{.FoundryA}}" placeholder="{{.FoundryA}}" size="8">/<input type="text" class="request-layerA" value="{{.LayerA}}" placeholder="{{.LayerA}}" size="4">
                         <button type="button" class="request-dir-arrow" data-dir="atob">&rarr;</button>
                         <input type="text" class="request-foundryB" value="{{.FoundryB}}" placeholder="{{.FoundryB}}" size="8">/<input type="text" class="request-layerB" value="{{.LayerB}}" placeholder="{{.LayerB}}" size="4">
                     </div>
                 </div>
+            </div>
+            {{end}}
+            {{range .CorpusMappings}}
+            <div class="mapping" data-id="{{.ID}}" data-type="corpus" data-mode="request">
                 <div class="mapping-row">
-                    <label><input type="checkbox" class="response-cb" name="response"> Response</label>
+                    <label><input type="checkbox" class="request-cb" name="request"> (corpus) {{.ID}}</label>
+                </div>
+            </div>
+            {{end}}
+        </section>
+
+        <section class="mapping-section">
+            <h2>Response</h2>
+            {{range .AnnotationMappings}}
+            <div class="mapping" data-id="{{.ID}}" data-type="annotation" data-mode="response"
+                 data-default-foundry-a="{{.FoundryA}}" data-default-layer-a="{{.LayerA}}"
+                 data-default-foundry-b="{{.FoundryB}}" data-default-layer-b="{{.LayerB}}">
+                <div class="mapping-row">
+                    <label><input type="checkbox" class="response-cb" name="response"> (query) {{.ID}}</label>
                     <div class="mapping-fields response-fields">
                         <input type="text" class="response-foundryA" value="{{.FoundryA}}" placeholder="{{.FoundryA}}" size="8">/<input type="text" class="response-layerA" value="{{.LayerA}}" placeholder="{{.LayerA}}" size="4">
                         <button type="button" class="response-dir-arrow" data-dir="btoa">&larr;</button>
@@ -42,32 +56,33 @@
                 </div>
             </div>
             {{end}}
-        </section>
-        {{end}}
-
-        {{if .CorpusMappings}}
-        <section class="mapping-section">
-            <h2>Corpus</h2>
             {{range .CorpusMappings}}
-            <div class="mapping" data-id="{{.ID}}" data-type="corpus">
-                <h3>{{.ID}}</h3>
-                {{if .Description}}<p class="desc">{{.Description}}</p>{{end}}
+            <div class="mapping" data-id="{{.ID}}" data-type="corpus" data-mode="response">
                 <div class="mapping-row">
-                    <label><input type="checkbox" class="request-cb" name="request"> Request</label>
-                </div>
-                <div class="mapping-row">
-                    <label><input type="checkbox" class="response-cb" name="response"> Response</label>
+                    <label><input type="checkbox" class="response-cb" name="response"> (corpus) {{.ID}}</label>
                 </div>
             </div>
             {{end}}
         </section>
-        {{end}}
+
+        <section>
+          <h2>Available Mappings</h2>
+          <dl>
+            {{range .AnnotationMappings}}
+            <dt>{{.ID}}</dt>
+            {{if .Description}}<dd>{{.Description}}</dd>{{end}}
+            {{end}}
+            {{range .CorpusMappings}}
+            <dt>{{.ID}}</dt>
+            {{if .Description}}<dd>{{.Description}}</dd>{{end}}
+            {{end}}
+          </dl>
+        </section>
 
         <section class="mapping-section">
-            <h2>Pipe Configuration</h2>
-            <label class="cfg-line-label" for="request-cfg-preview">Request cfg</label>
+            <label class="cfg-line-label" for="request-cfg-preview">Request:</label>
             <input type="text" id="request-cfg-preview" class="cfg-preview request-cfg-preview" readonly value="">
-            <label class="cfg-line-label" for="response-cfg-preview">Response cfg</label>
+            <label class="cfg-line-label" for="response-cfg-preview">Response:</label>
             <input type="text" id="response-cfg-preview" class="cfg-preview response-cfg-preview" readonly value="">
         </section>
 
diff --git a/cmd/koralmapper/static/config.js b/cmd/koralmapper/static/config.js
index b3cd42b..3bf9224 100644
--- a/cmd/koralmapper/static/config.js
+++ b/cmd/koralmapper/static/config.js
@@ -6,10 +6,16 @@
 
   var serviceURL = container.dataset.serviceUrl;
   var cookieName = container.dataset.cookieName || "km-config";
-  var mappingDivs = container.querySelectorAll(".mapping");
+  var requestMappingDivs = container.querySelectorAll('.mapping[data-mode="request"]');
+  var responseMappingDivMap = {};
   var requestCfgPreview = container.querySelector(".request-cfg-preview");
   var responseCfgPreview = container.querySelector(".response-cfg-preview");
 
+  var responseMappingDivs = container.querySelectorAll('.mapping[data-mode="response"]');
+  for (var i = 0; i < responseMappingDivs.length; i++) {
+    responseMappingDivMap[responseMappingDivs[i].dataset.id] = responseMappingDivs[i];
+  }
+
   // Cookie helpers
 
   function readCookie() {
@@ -69,18 +75,19 @@
   function getFormState() {
     var state = { mappings: [] };
 
-    for (var i = 0; i < mappingDivs.length; i++) {
-      var div = mappingDivs[i];
+    for (var i = 0; i < requestMappingDivs.length; i++) {
+      var requestDiv = requestMappingDivs[i];
+      var responseDiv = responseMappingDivMap[requestDiv.dataset.id];
       var entry = {
-        id: div.dataset.id
+        id: requestDiv.dataset.id
       };
 
-      if (div.dataset.type !== "corpus") {
-        entry.request = getModeState(div, "request");
-        entry.response = getModeState(div, "response");
+      if (requestDiv.dataset.type !== "corpus") {
+        entry.request = getModeState(requestDiv, "request");
+        entry.response = responseDiv ? getModeState(responseDiv, "response") : { enabled: false };
       } else {
-        entry.request = { enabled: div.querySelector(".request-cb").checked };
-        entry.response = { enabled: div.querySelector(".response-cb").checked };
+        entry.request = { enabled: requestDiv.querySelector(".request-cb").checked };
+        entry.response = { enabled: responseDiv && responseDiv.querySelector(".response-cb").checked };
       }
 
       state.mappings.push(entry);
@@ -124,16 +131,19 @@
       byId[saved.mappings[i].id] = saved.mappings[i];
     }
 
-    for (var i = 0; i < mappingDivs.length; i++) {
-      var div = mappingDivs[i];
-      var entry = byId[div.dataset.id];
+    for (var i = 0; i < requestMappingDivs.length; i++) {
+      var requestDiv = requestMappingDivs[i];
+      var responseDiv = responseMappingDivMap[requestDiv.dataset.id];
+      var entry = byId[requestDiv.dataset.id];
       if (!entry) continue;
 
-      if (div.dataset.type !== "corpus") {
+      if (requestDiv.dataset.type !== "corpus") {
         // Backward compatibility with old cookie schema.
         if (entry.request && typeof entry.request === "object") {
-          restoreModeState(div, "request", entry.request);
-          restoreModeState(div, "response", entry.response);
+          restoreModeState(requestDiv, "request", entry.request);
+          if (responseDiv) {
+            restoreModeState(responseDiv, "response", entry.response);
+          }
         } else {
           var requestLegacy = {
             enabled: !!entry.request,
@@ -151,12 +161,14 @@
             foundryB: entry.foundryB,
             layerB: entry.layerB
           };
-          restoreModeState(div, "request", requestLegacy);
-          restoreModeState(div, "response", responseLegacy);
+          restoreModeState(requestDiv, "request", requestLegacy);
+          if (responseDiv) {
+            restoreModeState(responseDiv, "response", responseLegacy);
+          }
         }
       } else {
-        var requestCb = div.querySelector(".request-cb");
-        var responseCb = div.querySelector(".response-cb");
+        var requestCb = requestDiv.querySelector(".request-cb");
+        var responseCb = responseDiv ? responseDiv.querySelector(".response-cb") : null;
         if (requestCb) {
           requestCb.checked = !!(entry.request && entry.request.enabled);
         }
@@ -181,6 +193,7 @@
   function buildCfgParam(mode) {
     var parts = [];
     var classes = rowFieldClasses(mode);
+    var mappingDivs = mode === "request" ? requestMappingDivs : responseMappingDivs;
 
     for (var i = 0; i < mappingDivs.length; i++) {
       var div = mappingDivs[i];
diff --git a/cmd/koralmapper/static/style.css b/cmd/koralmapper/static/style.css
index 1ed368e..8aa173d 100644
--- a/cmd/koralmapper/static/style.css
+++ b/cmd/koralmapper/static/style.css
@@ -1,5 +1,3 @@
-.mapping-section { padding-top: 0.75rem; margin-top: 1rem; }
-.mapping { margin-bottom: 1rem; }
 .mapping-row { display: flex; align-items: center; gap: 0.75rem; margin: 0.35rem 0; }
 .mapping-fields { display: inline-flex; align-items: center; gap: 0.35rem; }
 .mapping-fields input[type="text"] { font-family: monospace; }
@@ -7,5 +5,4 @@
 .request-dir-arrow, .response-dir-arrow { cursor: pointer; border: 1px solid #bbb; background: #f8f8f8; border-radius: 0.25rem; min-width: 2rem; }
 .cfg-line-label { display: block; margin: 0.35rem 0 0.1rem; }
 .cfg-preview { width: 100%; box-sizing: border-box; font-family: monospace; }
-.desc { color: #555; margin-top: 0; }
 footer { background-color: #aaa; font-size:50%; text-align: right; padding: 0.25rem; }
\ No newline at end of file