Validate waypoint order when defining the race.
authorCMDR furrycat <elite@furrycat.net>
Tue, 19 Jan 2016 12:11:24 +0000 (07:11 -0500)
committerCMDR furrycat <elite@furrycat.net>
Tue, 19 Jan 2016 12:11:24 +0000 (07:11 -0500)
entry.js
images/locked.gif [new file with mode: 0644]
lib/entry.php
lib/race.php
race.js
style.css

index bd97c91..2106e61 100644 (file)
--- a/entry.js
+++ b/entry.js
@@ -107,6 +107,11 @@ function get_system_id(table, i) {
   return get_row_attribute_id(table, i, 'system');
 }
 
+/* Get leg order; for set-course races. */
+function get_course_order(table, i) {
+  return get_row_attribute_id(table, i, 'order');
+}
+
 /* Pad string. */
 function zero_pad(n) {
   if (n < 10) return '0' + String(n);
@@ -344,6 +349,8 @@ function reorder_column(table, j) {
   for (var i = first_row(table); valid_row(table, i, 1); i++) {
     if (start_line(table, i) || finish_line(table, i)) continue;
     if (start_line(table, i + 1) || finish_line(table, i + 1)) continue;
+    if (get_course_order(table, i)) continue;
+    if (get_course_order(table, i + 1)) continue;
     var above = get_cell_input(i, j).value || 'zzz';
     var below = get_cell_input(i + 1, j).value || 'zzz';
     if (above == below) continue;
diff --git a/images/locked.gif b/images/locked.gif
new file mode 100644 (file)
index 0000000..2168338
Binary files /dev/null and b/images/locked.gif differ
index 0dd4207..b5f1413 100644 (file)
     return true;
   }
 
+  function show_leg($station, $start_finish, $station_id, $system_id, $order, $n) {
+    $td_class = $start_finish ? " class=\"$start_finish\"" : '';
+    echo "<tr>\n";
+    $classes = array("station$station_id", "system$system_id");
+    if (is_int($order)) $classes[] = "order$order";
+    $class_list = implode(' ', $classes);
+    echo "<td$td_class><p class=\"$class_list\">$station</p></td>\n";
+    echo "<td id=\"distance$n\"><p></p></td>\n";
+    echo "</tr>\n";
+  }
+
   function sort_legs($a, $b) {
     list($a_flags, $a_station_id, $a_system_id, $a_order) = $a;
     list($b_flags, $b_station_id, $b_system_id, $b_order) = $b;
     echo "<th>Leg</th>\n";
     $n = 0;
     foreach ($legs as $station => $attributes) {
-      list($flags, $id, $system_id) = $attributes;
+      list($flags, $station_id, $system_id, $order) = $attributes;
       /* Skip finish line. */
       if ($flags & Course::FinishFlag() && ! ($flags & Course::StartFlag())) continue;
-      $n++;
-      $label = $station;
-      $class = ($flags & Course::StartFlag()) ? ' class="start_line"' : '';
-      echo "<tr>\n";
-      echo "<td$class><p class=\"station$id system$system_id\">$station</p></td>\n";
-      echo "<td id=\"distance$n\"><p></p></td>\n";
-      echo "</tr>\n";
+      if ($flags & Course::StartFlag()) {
+        $class = 'start_line';
+        $order = 0;
+      }
+      else $class = '';
+      show_leg($station, $class, $station_id, $system_id, $order, ++$n);
     }
     foreach ($legs as $station => $attributes) {
-      list($flags, $id, $system_id) = $attributes;
+      list($flags, $station_id, $system_id, $order) = $attributes;
       if (! ($flags & Course::FinishFlag())) continue;
-      $class = ' class="finish_line"';
-      echo "<tr>\n";
-      echo "<td$class><p class=\"station$id system$system_id\">$station</p></td>\n";
-      echo "<td id=\"distance$n\"><p></p></td>\n";
-      echo "</tr>\n";
+      /* Last leg. */
+      $order = 0;
+      show_leg($station, 'finish_line', $station_id, $system_id, $order, ++$n);
     }
     echo "<tr>\n";
     echo "<td>";
index 877e3e8..d02f2c6 100644 (file)
       if (do_race($name, $allotted_time, $posted)) return true;
     }
 
+    echo "<div id=\"waypoints\">\n";
     form();
     echo "<table>\n";
     foreach ($stations as $station) {
     hidden('allotted_time', $allotted_time);
     submit('add_race', 'Submit race');
     end_form();
+    echo "</div>\n";
 
     return false;
   }
diff --git a/race.js b/race.js
index 4ec19d6..4f07761 100644 (file)
--- a/race.js
+++ b/race.js
@@ -42,24 +42,49 @@ function toggle_classification(event) {
 
 /* Check start and finish lines are set. */
 function validate() {
-  var stations = 0;
   var flags = 0;
-  for (input of document.getElementsByClassName('station')) {
-    var value = parseInt(input.value);
+  var stations = [];
+  for (select of document.getElementsByClassName('station')) {
+    var value = parseInt(select.value);
     var flag = value & mandatory_flags;
-    if (value && ! flag) stations++;
+    if (value && ! flag) stations.push(select);
     flags |= flag;
   }
 
   var valid = (flags == mandatory_flags);
   if (valid) {
     /* Either all stations must be ordered or none. */
-    var ordered = 0;
+    var ordered = {};
     for (input of document.getElementsByTagName('input')) {
       if (! input.name.match(/^station\d+order$/)) continue;
-      if (input.value) ordered++;
+      if (input.value) {
+        ordered[input.value] = input;
+        /* Stations not in the course can't be ordered. */
+        var select = document.getElementById(input.name.replace(/order$/, ''));
+        if (select && ! parseInt(select.value)) input.setCustomValidity("Not part of race");
+      }
+      else input.setCustomValidity("");
+    }
+    var keys = Object.keys(ordered);
+    if (keys.length) {
+      /* If one station is ordered they all must be. */
+      for (select of stations) {
+        var input = document.getElementById(select.name + "order");
+        if (input && ! input.value) input.setCustomValidity("Missing order");
+      }
+      valid = false;
+      /* Ordering must be contiguous from 1. */
+      for (var i = 1; i <= stations.length; i++) {
+        if (ordered[i]) continue;
+        for (var j = 0; j < keys.length; j++) {
+          if (parseInt(keys[j]) <= i) continue;
+          var input = ordered[keys[j]];
+          input.setCustomValidity("Out of order");
+          valid = false;
+        }
+        break;
+      }
     }
-    if (ordered > 0 && ordered < stations) valid = false;
   }
 
   var submits = document.getElementsByName('add_race');
@@ -72,7 +97,8 @@ function changed_waypoint(event) {
     if (input == event.target) continue;
     input.value &= ~(event.target.value & mandatory_flags);
   }
-  if (event.target.value & mandatory_flags) {
+  var flag = parseInt(event.target.value);
+  if (flag & mandatory_flags || ! flag) {
     /* Clear order. */
     var input = document.getElementById(event.target.name + 'order');
     if (input) input.value = '';
@@ -85,6 +111,8 @@ function changed_waypoint(event) {
 function changed_order(event) {
   var order = parseInt(event.target.value);
   if (order) {
+    var select = document.getElementById(event.target.name.replace(/order$/, ''));
+    if (select) select.value = 1;
     for (input of document.getElementsByTagName('input')) {
       if (input == event.target) continue;
       if (! input.name.match(/^station\d+order$/)) continue;
@@ -102,7 +130,10 @@ function loaded() {
   }
   var waypoints = document.getElementsByClassName('station');
   if (waypoints.length) {
+    var div = document.getElementById('waypoints');
+    if (div && div.offsetHeight > window.innerHeight * 0.9) div.style.height = '80%';
     for (input of waypoints) {
+      input.id = input.name;
       input.addEventListener('change', changed_waypoint, true);
       input.addEventListener('focusout', changed_waypoint, true);
     }
index 22a3052..b157101 100644 (file)
--- a/style.css
+++ b/style.css
@@ -2,6 +2,7 @@
 #laps table { background-color: rgba(20, 50, 100, 0.2); }
 #laps th, #laps td { padding-left: 1em; }
 #laps label:nth-child(even) { padding-top: 0.5em; padding-bottom: 0.5em; background-color: rgba(128, 128, 128, 0.1); }
+#laps p[class*='order']:after { background-image: url(images/locked.gif); background-position: left center; position: relative; padding-left: 16px; background-repeat: no-repeat; content: ""; }
 th { text-align: left; }
 th:nth-child(n+2),td:nth-child(n+2) { text-align: center; }
 #stats td:nth-child(n+1) { text-align: left; }
@@ -10,3 +11,4 @@ table.entries th, table.entries td { text-align: center; }
 .classification .bbcode { display: none; background-color: rgba(0, 0, 0, 0.1); }
 #systems { overflow-x: auto; }
 #stations { overflow-x: auto; }
+#waypoints { overflow-x: auto; }