PageSourceSearch

https://id.priyadi.net/assets/controllers/timezone-map_controller-l8_gCLL.js

js priyadi.net collected 2026-10-02 06:20:25 UTC 27,872 bytes, 633 lines download raw bytes

vendor: 4,392 bytes, lines 1-122
1function _array_like_to_array(arr, len) {
2    if (len == null || len > arr.length) len = arr.length;
3    for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i];
4    return arr2;
5}
6function _array_with_holes(arr) {
7    if (Array.isArray(arr)) return arr;
8}
9function _assert_this_initialized(self) {
10    if (self === void 0) {
11        throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
12    }
13    return self;
14}
15function _class_call_check(instance, Constructor) {
16    if (!(instance instanceof Constructor)) {
17        throw new TypeError("Cannot call a class as a function");
18    }
19}
20function _defineProperties(target, props) {
21    for(var i = 0; i < props.length; i++){
22        var descriptor = props[i];
23        descriptor.enumerable = descriptor.enumerable || false;
24        descriptor.configurable = true;
25        if ("value" in descriptor) descriptor.writable = true;
26        Object.defineProperty(target, descriptor.key, descriptor);
27    }
28}
29function _create_class(Constructor, protoProps, staticProps) {
30    if (protoProps) _defineProperties(Constructor.prototype, protoProps);
31    if (staticProps) _defineProperties(Constructor, staticProps);
32    return Constructor;
33}
34function _define_property(obj, key, value) {
35    if (key in obj) {
36        Object.defineProperty(obj, key, {
37            value: value,
38            enumerable: true,
39            configurable: true,
40            writable: true
41        });
42    } else {
43        obj[key] = value;
44    }
45    return obj;
46}
47function _get_prototype_of(o) {
48    _get_prototype_of = Object.setPrototypeOf ? Object.getPrototypeOf : function getPrototypeOf(o) {
49        return o.__proto__ || Object.getPrototypeOf(o);
50    };
51    return _get_prototype_of(o);
52}
53function _inherits(subClass, superClass) {
54    if (typeof superClass !== "function" && superClass !== null) {
55        throw new TypeError("Super expression must either be null or a function");
56    }
57    subClass.prototype = Object.create(superClass && superClass.prototype, {
58        constructor: {
59            value: subClass,
60            writable: true,
61            configurable: true
62        }
63    });
64    if (superClass) _set_prototype_of(subClass, superClass);
65}
66function _iterable_to_array_limit(arr, i) {
67    var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"];
68    if (_i == null) return;
69    var _arr = [];
70    var _n = true;
71    var _d = false;
72    var _s, _e;
73    try {
74        for(_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true){
75            _arr.push(_s.value);
76            if (i && _arr.length === i) break;
77        }
78    } catch (err) {
79        _d = true;
80        _e = err;
81    } finally{
82        try {
83            if (!_n && _i["return"] != null) _i["return"]();
84        } finally{
85            if (_d) throw _e;
86        }
87    }
88    return _arr;
89}
90function _non_iterable_rest() {
91    throw new TypeError("Invalid attempt to destructure non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
92}
93function _possible_constructor_return(self, call) {
94    if (call && (_type_of(call) === "object" || typeof call === "function")) {
95        return call;
96    }
97    return _assert_this_initialized(self);
98}
99function _set_prototype_of(o, p) {
100    _set_prototype_of = Object.setPrototypeOf || function setPrototypeOf(o, p) {
101        o.__proto__ = p;
102        return o;
103    };
104    return _set_prototype_of(o, p);
105}
106function _sliced_to_array(arr, i) {
107    return _array_with_holes(arr) || _iterable_to_array_limit(arr, i) || _unsupported_iterable_to_array(arr, i) || _non_iterable_rest();
108}
109function _type_of(obj) {
110    "@swc/helpers - typeof";
111    return obj && typeof Symbol !== "undefined" && obj.constructor === Symbol ? "symbol" : typeof obj;
112}
113function _unsupported_iterable_to_array(o, minLen) {
114    if (!o) return;
115    if (typeof o === "string") return _array_like_to_array(o, minLen);
116    var n = Object.prototype.toString.call(o).slice(8, -1);
117    if (n === "Object" && o.constructor) n = o.constructor.name;
118    if (n === "Map" || n === "Set") return Array.from(n);
119    if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array(o, minLen);
120}
121function _is_native_reflect_construct() {
122    if (typeof Reflect === "undefined" || !Reflect.construct) return false;
123    if (Reflect.construct.sham) return false;
124    if (typeof Proxy === "function") return true;
125    try {
126        Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function() {}));
127        return true;
128    } catch (e) {
129        return false;
130    }
131}
132function _create_super(Derived) {
133    var hasNativeReflectConstruct = _is_native_reflect_construct();
134    return function _createSuperInternal() {
135        var Super = _get_prototype_of(Derived), result;
136        if (hasNativeReflectConstruct) {
137            var NewTarget = _get_prototype_of(this).constructor;
138            result = Reflect.construct(Super, arguments, NewTarget);
139        } else {
140            result = Super.apply(this, arguments);
141        }
142        return _possible_constructor_return(this, result);
143    };
144}
145import { Controller } from "@hotwired/stimulus";
146import { geoGraticule10, geoNaturalEarth1, geoPath } from "d3-geo";
147import { pointer, select } from "d3-selection";
148import "d3-transition";
149import { zoom, zoomIdentity } from "d3-zoom";
150import { feature, mesh } from "topojson-client";
151var WIDTH = 960;
152var HEIGHT = 500;
153/** How far from a dot, in CSS pixels, a pointer still counts as on it. */ var PICK_RADIUS = 28;
154var MAX_ZOOM = 16;
155var atlas = null;
156function loadAtlas(url) {
157    atlas !== null && atlas !== void 0 ? atlas : atlas = fetch(url).then(function(response) {
158        if (!response.ok) {
159            throw new Error("Cannot load the world map: HTTP ".concat(response.status));
160        }
161        return response.json();
162    });
163    return atlas.catch(function(error) {
164        atlas = null;
165        throw error;
166    });
167}
168var _class = /*#__PURE__*/ function(Controller) {
169    "use strict";
170    _inherits(_class, Controller);
171    var _super = _create_super(_class);
172    function _class() {
173        _class_call_check(this, _class);
174        var _this;
175        _this = _super.apply(this, arguments);
176        _define_property(_assert_this_initialized(_this), "field", null);
177        _define_property(_assert_this_initialized(_this), "svg", null);
178        _define_property(_assert_this_initialized(_this), "zoomBehavior", void 0);
179        _define_property(_assert_this_initialized(_this), "transform", zoomIdentity);
180        _define_property(_assert_this_initialized(_this), "cities", []);
181        _define_property(_assert_this_initialized(_this), "cityById", new Map());
182        _define_property(_assert_this_initialized(_this), "offsets", new Map());
183        _define_property(_assert_this_initialized(_this), "selectedId", null);
184        _define_property(_assert_this_initialized(_this), "hoveredId", null);
185        _define_property(_assert_this_initialized(_this), "label", void 0);
186        _define_property(_assert_this_initialized(_this), "resizeObserver", null);
187        _define_property(_assert_this_initialized(_this), "locale", "en");
188        _define_property(_assert_this_initialized(_this), "onFieldChange", function() {
189            return _this.showSelected(true);
190        });
191        return _this;
192    }
193    _create_class(_class, [
194        {
195            key: "connect",
196            value: function connect() {
197                this.field = document.getElementById(this.forValue);
198                if (!this.field) {
199                    return;
200                }
201                this.locale = (document.documentElement.lang || "en").replace("_", "-");
202                this.field.addEventListener("change", this.onFieldChange);
203            }
204        },
205        {
206            key: "disconnect",
207            value: function disconnect() {
208                var _this_field, _this_resizeObserver, _this_svg, _this_svg1, _this_label;
209                (_this_field = this.field) === null || _this_field === void 0 ? void 0 : _this_field.removeEventListener("change", this.onFieldChange);
210                (_this_resizeObserver = this.resizeObserver) === null || _this_resizeObserver === void 0 ? void 0 : _this_resizeObserver.disconnect();
211                (_this_svg = this.svg) === null || _this_svg === void 0 ? void 0 : _this_svg.interrupt();
212                (_this_svg1 = this.svg) === null || _this_svg1 === void 0 ? void 0 : _this_svg1.on(".zoom", null).on(".map", null);
213                this.svg = null;
214                (_this_label = this.label) === null || _this_label === void 0 ? void 0 : _this_label.remove();
215                this.svgTarget.replaceChildren();
216                this.cities = [];
217                this.cityById.clear();
218                this.panelTarget.hidden = true;
219                this.toggleTarget.setAttribute("aria-expanded", "false");
220            }
221        },
222        {
223            key: "toggle",
224            value: function toggle() {
225                var open = this.panelTarget.hidden;
226                this.panelTarget.hidden = !open;
227                this.toggleTarget.setAttribute("aria-expanded", String(open));
228                if (open && this.field && !this.svg) {
229                    this.setUp();
230                }
231            }
232        },
233        {
234            key: "setUp",
235            value: function setUp() {
236                var _this = this;
237                var projection = geoNaturalEarth1().fitExtent([
238                    [
239                        2,
240                        2
241                    ],
242                    [
243                        WIDTH - 2,
244                        HEIGHT - 2
245                    ]
246                ], {
247                    type: "Sphere"
248                });
249                this.buildCities(projection);
250                this.draw(projection);
251                this.setUpZoom();
252                this.resizeObserver = new ResizeObserver(function() {
253                    return _this.render();
254                });
255                this.resizeObserver.observe(this.svgTarget);
256                this.showSelected(false);
257            }
258        },
259        {
260            key: "zoomIn",
261            value: function zoomIn() {
262                var _this_svg;
263                (_this_svg = this.svg) === null || _this_svg === void 0 ? void 0 : _this_svg.transition().duration(250).call(this.zoomBehavior.scaleBy, 2);
264            }
265        },
266        {
267            key: "zoomOut",
268            value: function zoomOut() {
269                var _this_svg;
270                (_this_svg = this.svg) === null || _this_svg === void 0 ? void 0 : _this_svg.transition().duration(250).call(this.zoomBehavior.scaleBy, 0.5);
271            }
272        },
273        {
274            key: "reset",
275            value: function reset() {
276                var _this_svg;
277                (_this_svg = this.svg) === null || _this_svg === void 0 ? void 0 : _this_svg.transition().duration(350).call(this.zoomBehavior.transform, zoomIdentity);
278            }
279        },
280        {
281            key: "buildCities",
282            value: /**
283     * One city per <select> option the tz database has a location for, so
284     * the map offers exactly what the field accepts.
285     */ function buildCities(projection) {
286                var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
287                try {
288                    for(var _iterator = Array.from(this.field.options)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
289                        var option = _step.value;
290                        var location = this.locationsValue[option.value];
291                        if (!location) {
292                            continue;
293                        }
294                        var _location = _sliced_to_array(location, 4), latitude = _location[0], longitude = _location[1], country = _location[2], comment = _location[3];
295                        var point = projection([
296                            longitude,
297                            latitude
298                        ]);
299                        if (!point) {
300                            continue;
301                        }
302                        var _option_value_split_pop;
303                        var city = {
304                            id: option.value,
305                            label: option.text.trim() || option.value,
306                            name: ((_option_value_split_pop = option.value.split("/").pop()) !== null && _option_value_split_pop !== void 0 ? _option_value_split_pop : option.value).replaceAll("_", " "),
307                            country: country,
308                            comment: comment,
309                            x: point[0],
310                            y: point[1]
311                        };
312                        this.cities.push(city);
313                        this.cityById.set(city.id, city);
314                    }
315                } catch (err) {
316                    _didIteratorError = true;
317                    _iteratorError = err;
318                } finally{
319                    try {
320                        if (!_iteratorNormalCompletion && _iterator.return != null) {
321                            _iterator.return();
322                        }
323                    } finally{
324                        if (_didIteratorError) {
325                            throw _iteratorError;
326                        }
327                    }
328                }
329            }
330        },
331        {
332            key: "draw",
333            value: function draw(projection) {
334                var _this = this;
335                var path = geoPath(projection);
336                this.svgTarget.replaceChildren();
337                var svg = select(this.svgTarget);
338                this.svg = svg;
339                var viewport = svg.append("g").attr("class", "el-tzmap-viewport");
340                viewport.append("path").attr("class", "el-tzmap-sphere").attr("d", path({
341                    type: "Sphere"
342                }));
343                viewport.append("path").attr("class", "el-tzmap-graticule").attr("d", path(geoGraticule10()));
344                var land = viewport.append("g");
345                svg.append("g").attr("class", "el-tzmap-cities").selectAll("circle").data(this.cities).join("circle").attr("class", "el-tzmap-city");
346                svg.append("circle").attr("class", "el-tzmap-hover");
347                svg.append("circle").attr("class", "el-tzmap-marker");
348                this.label = document.createElement("div");
349                this.label.className = "el-tzmap-label";
350                this.label.hidden = true;
351                this.svgTarget.after(this.label);
352                svg.on("pointermove.map", function(event) {
353                    return _this.hover(_this.nearest(event));
354                }).on("pointerleave.map", function() {
355                    return _this.hover(null);
356                }).on("click.map", function(event) {
357                    return _this.pick(_this.nearest(event));
358                });
359                loadAtlas(this.atlasValue).then(function(topology) {
360                    var _topology_objects = topology.objects, landObject = _topology_objects.land, countries = _topology_objects.countries;
361                    land.append("path").attr("class", "el-tzmap-land").attr("d", path(feature(topology, landObject)));
362                    land.append("path").attr("class", "el-tzmap-border").attr("d", path(mesh(topology, countries, function(a, b) {
363                        return a !== b;
364                    })));
365                }).catch(function(error) {
366                    return console.warn(error);
367                });
368            }
369        },
370        {
371            key: "setUpZoom",
372            value: function setUpZoom() {
373                var _this = this;
374                var _this_svg;
375                this.zoomBehavior = zoom().extent([
376                    [
377                        0,
378                        0
379                    ],
380                    [
381                        WIDTH,
382                        HEIGHT
383                    ]
384                ]).scaleExtent([
385                    1,
386                    MAX_ZOOM
387                ]).translateExtent([
388                    [
389                        0,
390                        0
391                    ],
392                    [
393                        WIDTH,
394                        HEIGHT
395                    ]
396                ])// A plain wheel scrolls the page, as it does everywhere else on
397                // it; Ctrl + wheel — also what a trackpad pinch sends — zooms.
398                .filter(function(event) {
399                    var e = event;
400                    if (event.type === "wheel") {
401                        return e.ctrlKey || e.metaKey;
402                    }
403                    return !e.ctrlKey && !e.button;
404                })// d3's default multiplies Ctrl + wheel by ten, which is right for
405                // a pinch's small deltas and a jump of 4× for one mouse notch.
406                .wheelDelta(function(event) {
407                    var delta = -event.deltaY * (event.deltaMode === 1 ? 0.05 : event.deltaMode ? 1 : 0.002) * 10;
408                    return Math.max(-0.5, Math.min(0.5, delta));
409                }).on("zoom", function(event) {
410                    var _this_svg;
411                    _this.transform = event.transform;
412                    (_this_svg = _this.svg) === null || _this_svg === void 0 ? void 0 : _this_svg.select(".el-tzmap-viewport").attr("transform", event.transform.toString());
413                    _this.render();
414                });
415                (_this_svg = this.svg) === null || _this_svg === void 0 ? void 0 : _this_svg.call(this.zoomBehavior);
416            }
417        },
418        {
419            key: "showSelected",
420            value: function showSelected(follow) {
421                var _this_field;
422                if (!this.svg) {
423                    return;
424                }
425                var _this_field_value;
426                var id = (_this_field_value = (_this_field = this.field) === null || _this_field === void 0 ? void 0 : _this_field.value) !== null && _this_field_value !== void 0 ? _this_field_value : "";
427                this.selectedId = this.cityById.has(id) ? id : null;
428                var city = this.selectedId ? this.cityById.get(this.selectedId) : null;
429                if (follow && city && !this.isInView(city)) {
430                    this.svg.transition().duration(500).call(this.zoomBehavior.translateTo, city.x, city.y);
431                }
432                this.render();
433            }
434        },
435        {
436            key: "pick",
437            value: function pick(city) {
438                if (!city || !this.field || this.field.disabled || city.id === this.field.value) {
439                    return;
440                }
441                var tomSelect = this.field.tomselect;
442                if (tomSelect) {
443                    // Tom Select fires `change` on the <select> itself.
444                    tomSelect.setValue(city.id);
445                } else {
446                    this.field.value = city.id;
447                    this.field.dispatchEvent(new Event("change", {
448                        bubbles: true
449                    }));
450                }
451            }
452        },
453        {
454            key: "hover",
455            value: function hover(city) {
456                var _city_id;
457                var id = (_city_id = city === null || city === void 0 ? void 0 : city.id) !== null && _city_id !== void 0 ? _city_id : null;
458                if (id === this.hoveredId) {
459                    return;
460                }
461                this.hoveredId = id;
462                this.render();
463            }
464        },
465        {
466            key: "nearest",
467            value: /** The city nearest the pointer, if it is within PICK_RADIUS of it. */ function nearest(event) {
468                var _pointer = _sliced_to_array(pointer(event, this.svgTarget), 2), px = _pointer[0], py = _pointer[1];
469                var limit = PICK_RADIUS * this.unitsPerPixel();
470                var best = null;
471                var bestDistance = limit * limit;
472                var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
473                try {
474                    for(var _iterator = this.cities[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
475                        var city = _step.value;
476                        var dx = this.transform.applyX(city.x) - px;
477                        var dy = this.transform.applyY(city.y) - py;
478                        var distance = dx * dx + dy * dy;
479                        if (distance < bestDistance) {
480                            best = city;
481                            bestDistance = distance;
482                        }
483                    }
484                } catch (err) {
485                    _didIteratorError = true;
486                    _iteratorError = err;
487                } finally{
488                    try {
489                        if (!_iteratorNormalCompletion && _iterator.return != null) {
490                            _iterator.return();
491                        }
492                    } finally{
493                        if (_didIteratorError) {
494                            throw _iteratorError;
495                        }
496                    }
497                }
498                return best;
499            }
500        },
501        {
502            key: "render",
503            value: function render() {
504                var _this = this;
505                var svg = this.svg;
506                if (!svg) {
507                    return;
508                }
509                var unit = this.unitsPerPixel();
510                var t = this.transform;
511                var _this_cityById_get;
512                var selected = this.selectedId ? (_this_cityById_get = this.cityById.get(this.selectedId)) !== null && _this_cityById_get !== void 0 ? _this_cityById_get : null : null;
513                var _this_cityById_get1;
514                var hovered = this.hoveredId ? (_this_cityById_get1 = this.cityById.get(this.hoveredId)) !== null && _this_cityById_get1 !== void 0 ? _this_cityById_get1 : null : null;
515                var peerOffset = selected ? this.offsetOf(selected.id) : null;
516                svg.selectAll(".el-tzmap-city").attr("cx", function(c) {
517                    return t.applyX(c.x);
518                }).attr("cy", function(c) {
519                    return t.applyY(c.y);
520                }).attr("r", 2.5 * unit).classed("is-peer", function(c) {
521                    return peerOffset !== null && c.id !== (selected === null || selected === void 0 ? void 0 : selected.id) && _this.offsetOf(c.id) === peerOffset;
522                });
523                this.placeCircle(".el-tzmap-marker", selected, 6 * unit);
524                this.placeCircle(".el-tzmap-hover", hovered && hovered !== selected ? hovered : null, 7 * unit);
525                var labelled = hovered !== null && hovered !== void 0 ? hovered : selected;
526                this.label.hidden = labelled === null;
527                if (labelled) {
528                    this.label.textContent = labelled.name;
529                    this.label.style.left = "".concat(t.applyX(labelled.x) / WIDTH * 100, "%");
530                    this.label.style.top = "".concat(t.applyY(labelled.y) / HEIGHT * 100, "%");
531                }
532                this.captionTarget.textContent = labelled ? this.describe(labelled) : this.hintValue;
533                this.svgTarget.classList.toggle("is-over-city", hovered !== null);
534            }
535        },
536        {
537            key: "placeCircle",
538            value: function placeCircle(selector, city, radius) {
539                var _this_svg;
540                (_this_svg = this.svg) === null || _this_svg === void 0 ? void 0 : _this_svg.select(selector).attr("hidden", city ? null : "").attr("cx", city ? this.transform.applyX(city.x) : 0).attr("cy", city ? this.transform.applyY(city.y) : 0).attr("r", radius);
541            }
542        },
543        {
544            key: "describe",
545            value: /** "Asia / Jakarta — Indonesia (Java, Sumatra) · 14:32 GMT+7" */ function describe(city) {
546                var country = city.country;
547                try {
548                    var _of;
549                    country = (_of = new Intl.DisplayNames([
550                        this.locale,
551                        "en"
552                    ], {
553                        type: "region"
554                    }).of(city.country)) !== null && _of !== void 0 ? _of : city.country;
555                } catch (e) {
556                // An unknown locale tag: the code is still an answer.
557                }
558                var now = "";
559                try {
560                    now = new Intl.DateTimeFormat([
561                        this.locale,
562                        "en"
563                    ], {
564                        timeZone: city.id,
565                        hour: "numeric",
566                        minute: "2-digit",
567                        timeZoneName: "shortOffset"
568                    }).format(new Date());
569                } catch (e) {
570                // A zone the browser's ICU does not know yet.
571                }
572                // tzdata comments carry their own parentheses ("Buenos Aires (BA, CF)").
573                var where = !city.comment ? country : city.comment.includes("(") ? "".concat(country, ": ").concat(city.c
573omment) : "".concat(country, " (").concat(city.comment, ")");
574                return [
575                    "".concat(city.label, " — ").concat(where),
576                    now
577                ].filter(Boolean).join(" \xb7 ");
578            }
579        },
580        {
581            key: "offsetOf",
582            value: /** The zone's UTC offset right now, "GMT+07:00"; cached per page. */ function offsetOf(id) {
583                var offset = this.offsets.get(id);
584                if (offset === undefined) {
585                    try {
586                        var _formatToParts_find;
587                        var _formatToParts_find_value;
588                        offset = (_formatToParts_find_value = (_formatToParts_find = new Intl.DateTimeFormat("en", {
589                            timeZone: id,
590                            timeZoneName: "longOffset"
591                        }).formatToParts(new Date()).find(function(part) {
592                            return part.type === "timeZoneName";
593                        })) === null || _formatToParts_find === void 0 ? void 0 : _formatToParts_find.value) !== null && _formatToParts_find_value !== void 0 ? _formatToParts_find_value : "";
594                    } catch (e) {
595                        offset = "";
596                    }
597                    this.offsets.set(id, offset);
598                }
599                return offset;
600            }
601        },
602        {
603            key: "isInView",
604            value: function isInView(city) {
605                var x = this.transform.applyX(city.x);
606                var y = this.transform.applyY(city.y);
607                var margin = 40;
608                return x >= margin && x <= WIDTH - margin && y >= margin && y <= HEIGHT - margin;
609            }
610        },
611        {
612            key: "unitsPerPixel",
613            value: /** viewBox units per CSS pixel, so marks keep one size however wide the map is drawn. */ function unitsPerPixel() {
614                var width = this.svgTarget.clientWidth;
615                return width > 0 ? WIDTH / width : 1;
616            }
617        }
618    ]);
619    return _class;
620}(Controller);
621_define_property(_class, "targets", [
622    "svg",
623    "caption",
624    "panel",
625    "toggle"
626]);
627_define_property(_class, "values", {
628    for: String,
629    atlas: String,
630    locations: Object,
631    hint: String
632});
633export { _class as default };

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.