diff --git a/themes/intotheeast/templates/macros/cycling.html.twig b/themes/intotheeast/templates/macros/cycling.html.twig
new file mode 100644
index 0000000..22e6155
--- /dev/null
+++ b/themes/intotheeast/templates/macros/cycling.html.twig
@@ -0,0 +1,41 @@
+{% macro cycling_panel() %}
+
+
+
+
+
+ โ
+ km distance
+
+
+ โ
+ m โ gain
+
+
+ โ
+ m โ loss
+
+
+ โ
+ m highest
+
+
+ โ
+ m lowest
+
+
+ โ
+ moving time
+
+
+ โ
+ km/h avg speed
+
+
+
+
+
+{% endmacro %}
diff --git a/themes/intotheeast/templates/macros/stats.html.twig b/themes/intotheeast/templates/macros/stats.html.twig
new file mode 100644
index 0000000..3f757d5
--- /dev/null
+++ b/themes/intotheeast/templates/macros/stats.html.twig
@@ -0,0 +1,93 @@
+{% macro stats_panel(journal_entries, page, journal_count, has_gpx) %}
+{% set days_on_road = 0 %}
+{% if page.header.date_end is not empty %}
+ {% set start_ts = page.header.date_start|date('U') %}
+ {% set end_ts = page.header.date_end|date('U') %}
+ {% set days_on_road = ((end_ts - start_ts) / 86400)|round(0, 'ceil') %}
+{% else %}
+ {% set first_ts = null %}
+ {% for entry in journal_entries %}
+ {% set ts = entry.date|date('U') %}
+ {% if first_ts is null or ts < first_ts %}{% set first_ts = ts %}{% endif %}
+ {% endfor %}
+ {% if first_ts is not null %}
+ {% set diff_seconds = "now"|date('U') - first_ts %}
+ {% set days_raw = (diff_seconds / 86400)|round(0, 'floor') %}
+ {% set days_on_road = days_raw < 1 ? 1 : days_raw %}
+ {% endif %}
+{% endif %}
+
+{% set seen_lower = [] %}
+{% set country_display = [] %}
+{% for entry in journal_entries %}
+ {% if entry.header.location_country is not empty %}
+ {% set lower = entry.header.location_country|trim|lower %}
+ {% if lower not in seen_lower %}
+ {% set seen_lower = seen_lower|merge([lower]) %}
+ {% set country_display = country_display|merge([entry.header.location_country|trim]) %}
+ {% endif %}
+ {% endif %}
+{% endfor %}
+
+{% set seen_city_lower = [] %}
+{% set city_display = [] %}
+{% for entry in journal_entries %}
+ {% if entry.header.location_city is not empty %}
+ {% set lower = entry.header.location_city|trim|lower %}
+ {% if lower not in seen_city_lower %}
+ {% set seen_city_lower = seen_city_lower|merge([lower]) %}
+ {% set city_display = city_display|merge([entry.header.location_city|trim]) %}
+ {% endif %}
+ {% endif %}
+{% endfor %}
+
+{% set temp_min = null %}
+{% set temp_max = null %}
+{% for entry in journal_entries %}
+ {% if entry.header.weather_temp_c is defined and entry.header.weather_temp_c is not empty %}
+ {% set t = entry.header.weather_temp_c %}
+ {% if temp_min is null or t < temp_min %}{% set temp_min = t %}{% endif %}
+ {% if temp_max is null or t > temp_max %}{% set temp_max = t %}{% endif %}
+ {% endif %}
+{% endfor %}
+
+
+
+
+
+ {{ days_on_road }}
+ {{ days_on_road == 1 ? 'day' : 'days' }} on the road
+
+
+ {{ journal_count }}
+ {{ journal_count == 1 ? 'entry' : 'entries' }} posted
+
+
+ {{ country_display|length }}
+ {{ country_display|length == 1 ? 'country' : 'countries' }} visited
+
+
+ {{ city_display|length }}
+ {{ city_display|length == 1 ? 'city' : 'cities' }} visited
+
+
+ โ
+ {{ has_gpx ? '๐ด km cycled' : '๐งญ km roamed' }}
+
+
+ {% if temp_min is not null %}
+ {{ temp_min == temp_max ? temp_min : temp_min ~ ' โ ' ~ temp_max }}
+ {% else %}
+ โ
+ {% endif %}
+ ยฐC range
+
+
+ {% if country_display|length > 0 %}
+
{{ country_display|join(' ยท ') }}
+ {% endif %}
+
{{ has_gpx ? 'Distance based on GPS track data.' : 'Distance is approximate โ straight lines between entry locations.' }}
+
+
+
+{% endmacro %}
diff --git a/themes/intotheeast/templates/trip.html.twig b/themes/intotheeast/templates/trip.html.twig
index b8013a0..eec47c2 100644
--- a/themes/intotheeast/templates/trip.html.twig
+++ b/themes/intotheeast/templates/trip.html.twig
@@ -1,6 +1,8 @@
{% extends 'partials/base.html.twig' %}
{% block content %}
+{% import 'macros/stats.html.twig' as stats_m %}
+{% import 'macros/cycling.html.twig' as cycling_m %}
{% block map_assets %}
{% do assets.addCss('theme://css-compiled/map.css') %}
{% do assets.addJs('theme://js/map.js', {group: 'bottom'}) %}
@@ -22,59 +24,6 @@
{% set journal_count = journal_entries|length %}
{% set story_count = story_entries|length %}
-{# Stats computation #}
-{% set days_on_road = 0 %}
-{% if page.header.date_end is not empty %}
- {% set start_ts = page.header.date_start|date('U') %}
- {% set end_ts = page.header.date_end|date('U') %}
- {% set days_on_road = ((end_ts - start_ts) / 86400)|round(0, 'ceil') %}
-{% else %}
- {% set first_ts = null %}
- {% for entry in journal_entries %}
- {% set ts = entry.date|date('U') %}
- {% if first_ts is null or ts < first_ts %}{% set first_ts = ts %}{% endif %}
- {% endfor %}
- {% if first_ts is not null %}
- {% set diff_seconds = "now"|date('U') - first_ts %}
- {% set days_raw = (diff_seconds / 86400)|round(0, 'floor') %}
- {% set days_on_road = days_raw < 1 ? 1 : days_raw %}
- {% endif %}
-{% endif %}
-
-{% set seen_lower = [] %}
-{% set country_display = [] %}
-{% for entry in journal_entries %}
- {% if entry.header.location_country is not empty %}
- {% set lower = entry.header.location_country|trim|lower %}
- {% if lower not in seen_lower %}
- {% set seen_lower = seen_lower|merge([lower]) %}
- {% set country_display = country_display|merge([entry.header.location_country|trim]) %}
- {% endif %}
- {% endif %}
-{% endfor %}
-
-{% set seen_city_lower = [] %}
-{% set city_display = [] %}
-{% for entry in journal_entries %}
- {% if entry.header.location_city is not empty %}
- {% set lower = entry.header.location_city|trim|lower %}
- {% if lower not in seen_city_lower %}
- {% set seen_city_lower = seen_city_lower|merge([lower]) %}
- {% set city_display = city_display|merge([entry.header.location_city|trim]) %}
- {% endif %}
- {% endif %}
-{% endfor %}
-
-{% set temp_min = null %}
-{% set temp_max = null %}
-{% for entry in journal_entries %}
- {% if entry.header.weather_temp_c is defined and entry.header.weather_temp_c is not empty %}
- {% set t = entry.header.weather_temp_c %}
- {% if temp_min is null or t < temp_min %}{% set temp_min = t %}{% endif %}
- {% if temp_max is null or t > temp_max %}{% set temp_max = t %}{% endif %}
- {% endif %}
-{% endfor %}
-
{% set gps_points = [] %}
{% for entry in journal_entries %}
{% if entry.header.lat is not empty and entry.header.lng is not empty %}
@@ -147,86 +96,10 @@
-
-
-
-
- {{ days_on_road }}
- {{ days_on_road == 1 ? 'day' : 'days' }} on the road
-
-
- {{ journal_count }}
- {{ journal_count == 1 ? 'entry' : 'entries' }} posted
-
-
- {{ country_display|length }}
- {{ country_display|length == 1 ? 'country' : 'countries' }} visited
-
-
- {{ city_display|length }}
- {{ city_display|length == 1 ? 'city' : 'cities' }} visited
-
-
- โ
- {{ has_gpx ? '๐ด km cycled' : '๐งญ km roamed' }}
-
-
- {% if temp_min is not null %}
- {{ temp_min == temp_max ? temp_min : temp_min ~ ' โ ' ~ temp_max }}
- {% else %}
- โ
- {% endif %}
- ยฐC range
-
-
- {% if country_display|length > 0 %}
-
{{ country_display|join(' ยท ') }}
- {% endif %}
-
{{ has_gpx ? 'Distance based on GPS track data.' : 'Distance is approximate โ straight lines between entry locations.' }}
-
-
-
+ {{ stats_m.stats_panel(journal_entries, page, journal_count, has_gpx) }}
{% if has_gpx %}
-
-
-
-
-
- โ
- km distance
-
-
- โ
- m โ gain
-
-
- โ
- m โ loss
-
-
- โ
- m highest
-
-
- โ
- m lowest
-
-
- โ
- moving time
-
-
- โ
- km/h avg speed
-
-
-
-
-
+ {{ cycling_m.cycling_panel() }}
{% endif %}
@@ -345,11 +218,9 @@ var HAS_GPX = {{ has_gpx ? 'true' : 'false' }};
if (HAS_GPX) {
MapUtils.parseGpxFiles(GPX_URLS, function(result) {
- // Mode A: update distance stat
if (distEl) {
distEl.textContent = result.distance > 0 ? Math.round(result.distance).toLocaleString() : 'โ';
}
- // Populate cycling panel
function setText(id, val) {
var el = document.getElementById(id);
if (el) el.textContent = val;
@@ -363,7 +234,6 @@ var HAS_GPX = {{ has_gpx ? 'true' : 'false' }};
setText('cyc-avg-speed', result.avgSpeed > 0 ? result.avgSpeed.toFixed(1) : 'โ');
});
} else {
- // Mode B: haversine between entry points
var total = 0;
for (var i = 1; i < STATS_GPS.length; i++) {
total += MapUtils.haversineKm(