PageSourceSearch

http://mostafavisaffron.com/wp-content/themes/theme_mostafavi/js/product-reviews.js?ver=1.0.0

js mostafavisaffron.com collected 2026-10-03 00:33:21 UTC 27,331 bytes, 830 lines download raw bytes

1(function ($) {
2  "use strict";
3
4  $(document).ready(function () {
5    $(".Reviews.click-tempalte")
6      .off("click")
7      .on("click", function () {
8        $(".item-tempalte-content").addClass("hidden");
9        $("#Reviews-tempalte").removeClass("hidden");
10
11        $(".click-tempalte").removeClass("active-menu-dashboard");
12        $(this).addClass("active-menu-dashboard");
13
14        $("#title-name").text("نظرات من");
15
16        if ($("#change-image").length) {
17          $("#change-image").attr(
18            "src",
19            $("#change-image").data("reviews-icon") || ""
20          );
21        }
22      });
23
24    if ($(".comment-form").length) {
25      $(".comment-form").addClass("product-review-form");
26
27      $("#review_image")
28        .off("change")
29        .on("change", function () {
30          // Only proceed if this is within a review form
31          if (
32            !$(this).closest(
33              ".product-review-form, #review-form, .comment-form"
34            ).length
35          ) {
36            return;
37          }
38
39          var files = this.files;
40          var previewContainer = $(".image-preview-container");
41
42          // Limit to 3 images
43          if (files.length > 3) {
44            alert("لطفاً حداکثر سه تصویر انتخاب کنید.");
45            this.value = "";
46            return;
47          }
48
49          // Clear existing previews
50          previewContainer.empty();
51
52          // Mostrar el contenedor
53          previewContainer.show();
54
55          // Procesar cada archivo seleccionado
56          for (var i = 0; i < files.length; i++) {
57            var file = files[i];
58            if (file) {
59              var reader = new FileReader();
60              reader.onload = function (e) {
61                // Crear un contenedor para cada imagen
62                var previewItem = $(
63                  '<div class="image-preview-item relative m-1"></div>'
64                );
65
66                // Añadir la imagen
67                var img = $(
68                  '<img src="' +
69                    e.target.result +
70                    '" class="max-w-xs max-h-40 rounded-lg" />'
71                );
72                previewItem.append(img);
73
74                // Añadir botón para eliminar esta previsualización
75                var removeBtn = $(
76                  '<button type="button" class="remove-single-preview absolute top-1 right-1 bg-red-500 text-white px-2 py-1 rounded-full text-xs">×</button>'
77                );
78                previewItem.append(removeBtn);
79
80                // Añadir al contenedor principal
81                previewContainer.append(previewItem);
82              };
83              reader.readAsDataURL(file);
84            }
85          }
86        });
87
88      // Manejar la eliminación de una sola previsualización
89      $(document).on("click", ".remove-single-preview", function () {
90        // Only proceed if this is within a review form
91        if (
92          !$(this).closest(".product-review-form, #review-form, .comment-form")
93            .length
94        ) {
95          return;
96        }
97
98        $(this).closest(".image-preview-item").remove();
99
100        // Si no quedan imágenes, ocultar el contenedor
101        if ($(".image-preview-item").length === 0) {
102          $(".image-preview-container").hide();
103        }
104      });
105
106      // Manejar la eliminación de todas las previsualizaciones
107      $(document).on("click", ".remove-preview", function () {
108        // Only proceed if this is within a review form
109        if (
110          !$(this).closest(".product-review-form, #review-form, .comment-form")
111            .length
112        ) {
113          return;
114        }
115
116        $(".image-preview-container").empty().hide();
117        $("#review_image").val("");
118      });
119
120      $(".comment-form").on("submit", function (e) {
121        var isValid = true;
122        var errorMessages = [];
123
124        if ($("#review_first_name").val() === "") {
125          isValid = false;
126          errorMessages.push("لطفاً نام خود را وارد کنید.");
127        }
128
129        if ($("#review_last_name").val() === "") {
130          isValid = false;
131          errorMessages.push("لطفاً نام خانوادگی خود را وارد کنید.");
132        }
133
134        if ($("#email").val() === "") {
135          isValid = false;
136          errorMessages.push("لطفاً ایمیل خود را وارد کنید.");
137        }
138
139        if ($("#comment").val() === "") {
140          isValid = false;
141          errorMessages.push("لطفاً متن نظر خود را وارد کنید.");
142        }
143
144        if (!isValid) {
145          e.preventDefault();
146
147          $(".review-error-messages").remove();
148
149          var errorBox = '<div class="review-error-messages"><ul>';
150          $.each(errorMessages, function (index, message) {
151            errorBox += "<li>" + message + "</li>";
152          });
153          errorBox += "</ul></div>";
154
155          $(errorBox).insertBefore(".form-submit");
156        }
157      });
158    }
159
160    $(document).on("click", ".review-image", function () {
161      var imgSrc = $(this).attr("src");
162      var overlay = $('<div class="review-image-overlay"></div>');
163      var imgContainer = $('<div class="review-image-large-container"></div>');
164      var img = $('<img src="' + imgSrc + '" class="review-image-large" />');
165      var closeBtn = $('<span class="review-image-close">&times;</span>');
166
167      imgContainer.append(img);
168      imgContainer.append(closeBtn);
169      overlay.append(imgContainer);
170      $("body").append(overlay);
171
172      overlay.css({
173        position: "fixed",
174        top: 0,
175        left: 0,
176        width: "100%",
177        height: "100%",
178        "background-color": "rgba(0,0,0,0.8)",
179        "z-index": 9999,
180        display: "flex",
181        "justify-content": "center",
182        "align-items": "center",
183      });
184
185      imgContainer.css({
186        position: "relative",
187        "max-width": "90%",
188        "max-height": "90%",
189      });
190
191      img.css({
192        "max-width": "100%",
193        "max-height": "90vh",
194        "border-radius": "5px",
195      });
196
197      closeBtn.css({
198        position: "absolute",
199        top: "-20px",
200        right: "-20px",
201        color: "white",
202        "font-size": "30px",
203        cursor: "pointer",
204      });
205
206      closeBtn.on("click", function () {
207        overlay.remove();
208      });
209
210      overlay.on("click", function (e) {
211        if (e.target === this) {
212          overlay.remove();
213        }
214      });
215    });
216
217    if ($("#reviews").length) {
218      setupSingleProductReviews();
219    }
220
221    $(".product-review-form").on("submit", function (e) {
222      if (!$(".review-error-messages").length) {
223        $(this).prepend(
224          '<div class="review-error-messages" style="display:none;"></div>'
225        );
226      }
227
228      const $errorContainer = $(".review-error-messages");
229      $errorContainer.empty().hide();
230
231      let errors = [];
232
233      if (!$("#comment").val().trim()) {
234        errors.push("لطفا متن دیدگاه خود را وارد کنید.");
235      }
236
237      if (
238        $("#review_first_name").length &&
239        !$("#review_first_name").val().trim()
240      ) {
241        errors.push("لطفا نام خود را وارد کنید.");
242      }
243
244      if (
245        $("#review_last_name").length &&
246        !$("#review_last_name").val().trim()
247      ) {
248        errors.push("لطفا نام خانوادگی خود را وارد کنید.");
249      }
250
251      if ($("#email").length) {
252        const email = $("#email").val().trim();
253        if (!email) {
254          errors.push("لطفا ایمیل خود را وارد کنید.");
255        } else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
256          errors.push("لطفا یک ایمیل معتبر وارد کنید.");
257        }
258      }
259
260      if (
261        $("#rating").length &&
262        (!$("#rating").val() || $("#rating").val() === "")
263      ) {
264        errors.push("لطفا امتیاز خود را به محصول اضافه کنید.");
265      }
266
267      if (errors.length > 0) {
268        e.preventDefault();
269        $errorContainer.html(
270          "<ul>" +
271            errors.map((error) => "<li>" + error + "</li>").join("") +
272            "</ul>"
273        );
274        $errorContainer.show();
275        $("html, body").animate(
276          {
277            scrollTop: $errorContainer.offset().top - 100,
278          },
279          200
280        );
281      }
282    });
283
284    // Handle customer dashboard reviews tab
285    $(document).ready(function () {
286      // When the user clicks on the Reviews tab in the dashboard
287      $('.tab-link[data-tab="Reviews-tempalte"]').on("click", function () {
288        // Change the active tab
289        $(".tab-link").removeClass("active");
290        $(this).addClass("active");
291
292        // Change the page title
293        $(".title-dashboard h3").text("نظرات من");
294
295        // Change the icon
296        $(".title-dashboard img").attr(
297          "src",
298          $(".title-dashboard").data("comment-icon")
299        );
300
301        // Show the content of the Reviews tab
302        $(".dashboard-template").hide();
303        $("#Reviews-tempalte").show();
304      });
305
306      // Single product reviews functionality
307      if ($("#reviews").length) {
308        setupSingleProductReviews();
309      }
310
311      // Comment form handling
312      $(".comment-form").addClass("product-review-form");
313
314      // File input preview for multiple files
315      $("#file-upload")
316        .off("change")
317        .on("change", function () {
318          // Only proceed if this is within a review form
319          if (
320            !$(this).closest(
321              ".product-review-form, #review-form, .comment-form, #Reviews-tempalte"
322            ).length
323          ) {
324            return;
325          }
326
327          const files = this.files;
328          const previewContainer = $("#image-preview-container");
329          previewContainer.empty();
330
331          // Limit to 3 images
332          if (files.length > 3) {
333            alert("لطفاً حداکثر سه تصویر انتخاب کنید.");
334            this.value = "";
335            return;
336          }
337
338          if (files.length > 0) {
339            for (let i = 0; i < files.length; i++) {
340              const file = files[i];
341              if (file) {
342                const reader = new FileReader();
343                reader.onload = function (event) {
344                  const previewWrapper = $(
345                    '<div class="preview-wrapper relative"></div>'
346                  );
347                  const preview = $(
348                    '<img class="w-24 h-24 object-cover rounded border border-gray-300" />'
349                  );
350                  preview.attr("src", event.target.result);
351
352                  const removeBtn = $(
353                    '<button type="button" class="remove-preview absolute -top-2 -right-2 bg-red-500 text-white rounded-full w-5 h-5 flex items-center justify-center">×</button>'
354                  );
355                  removeBtn.on("click", function () {
356                    previewWrapper.remove();
357                    if ($(".preview-wrapper").length === 0) {
358                      $("#file-upload").val("");
359                    } else {
360                      $(
361                        '<div class="text-sm text-red-500 mt-2 mb-2">برای حذف کامل تصاویر، دوباره انتخاب کنید</div>'
362                      )
363                        .insertAfter(previewContainer)
364                        .delay(3000)
365                        .fadeOut(function () {
366                          $(this).remove();
367                        });
368                    }
369                  });
370
371                  previewWrapper.append(preview);
372                  previewWrapper.append(removeBtn);
373                  previewContainer.append(previewWrapper);
374                };
375                reader.readAsDataURL(file);
376              }
377            }
378          }
379        });
380
381      // Función para verificar si se han marcado todos los previews para eliminación
382      function checkRemovedPreviews() {
383        const totalPreviews = $(".preview-wrapper").length;
384        const removedPreviews = $(".preview-wrapper.marked-for-removal").length;
385
386        // Si todos los previews están marcados como eliminados, limpiar el input file
387        if (totalPreviews > 0 && totalPreviews === removedPreviews) {
388          $("#file-upload").val("");
389          $("#image-preview-container").empty();
390        }
391      }
392
393      // Form validation
394      $(".product-review-form, #review-form").on("submit", function (e) {
395        // Create error message container if it doesn't exist
396        if (!$(".review-error-messages").length) {
397          $(this).prepend(
398            '<div class="review-error-messages" style="display:none;"></div>'
399          );
400        }
401
402        const $errorContainer = $(".review-error-messages");
403        $errorContainer.empty().hide();
404
405        // Validate all required fields
406        let errors = [];
407
408        // Check comment text
409        if (!$("#comment").val().trim()) {
410          errors.push("لطفا متن دیدگاه خود را وارد کنید.");
411        }
412
413        // Check first and last name if present and required
414        if (
415          $("#review_first_name").length &&
416          $("#review_first_name").prop("required") &&
417          !$("#review_first_name").val().trim()
418        ) {
419          errors.push("لطفا نام خود را وارد کنید.");
420        }
421
422        if (
423          $("#review_last_name").length &&
424          $("#review_last_name").prop("required") &&
425          !$("#review_last_name").val().trim()
426        ) {
427          errors.push("لطفا نام خانوادگی خود را وارد کنید.");
428        }
429
430        // Check email
431        if ($("#email").length && $("#email").prop("required")) {
432          const email = $("#email").val().trim();
433          if (!email) {
434            errors.push("لطفا ایمیل خود را وارد کنید.");
435          } else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
436            errors.push("لطفا یک ایمیل معتبر وارد کنید.");
437          }
438        }
439
440        // Check rating if present
441        if (
442          $("#rating").length &&
443          $("#rating").prop("required") &&
444          (!$("#rating").val() || $("#rating").val() === "")
445        ) {
446          errors.push("لطفا امتیاز خود را به محصول اضافه کنید.");
447        }
448
449        // If there are errors, prevent form submission and show errors
450        if (errors.length > 0) {
451          e.preventDefault();
452          $errorContainer.html(
453            "<ul>" +
454              errors.map((error) => "<li>" + error + "</li>").join("") +
455              "</ul>"
456          );
457          $errorContainer.show();
458          // Scroll to the error messages
459          $("html, body").animate(
460            {
461              scrollTop: $errorContainer.offset().top - 100,
462            },
463            200
464          );
465        }
466      });
467
468      // Handle clicks on review images
469      $(document).on("click", ".review-image-link", function (e) {
470        e.preventDefault();
471        const imgSrc = $(this).attr("href");
472        showImageOverlay(imgSrc);
473      });
474    });
475
476    // Cambiar el título y el icono al hacer clic en la pestaña de reseñas
477    $("#Reviews").on("click", function () {
478      setTimeout(function () {
479        $("#title-name").text("نظرات من");
480        $("#dash-icon").attr(
481          "src",
482          "/wp-content/themes/mostafavishop-saberi-translate/media/dashboard/icon-comments.svg"
483        );
484      }, 100);
485    });
486
487    // Manejar la vista previa de las imágenes
488    $("#file-upload")
489      .off("change")
490      .on("change", function () {
491        // Only proceed if this is within a review form
492        if (
493          !$(this).closest(
494            ".product-review-form, #review-form, .comment-form, #Reviews-tempalte"
495          ).length
496        ) {
497          return;
498        }
499
500        const files = this.files;
501        const previewContainer = $("#image-preview-container");
502
503        // Limpiar el contenedor para evitar duplicaciones
504        previewContainer.empty();
505
506        // Limit to 3 images
507        if (files.length > 3) {
508          alert("لطفاً حداکثر سه تصویر انتخاب کنید.");
509          this.value = "";
510          return;
511        }
512
513        if (files.length > 0) {
514          for (let i = 0; i < files.length; i++) {
515            const file = files[i];
516            if (file) {
517              const reader = new FileReader();
518              reader.onload = function (event) {
519                const previewWrapper = $(
520                  '<div class="preview-wrapper relative"></div>'
521                );
522                const preview = $(
523                  '<img class="w-24 h-24 object-cover rounded border border-gray-300" />'
524                );
525                preview.attr("src", event.target.result);
526
527                const removeBtn = $(
528                  '<button type="button" class="remove-preview absolute -top-2 -right-2 bg-red-500 text-white rounded-full w-5 h-5 flex items-center justify-center">×</button>'
529                );
530                removeBtn.on("click", function () {
531                  previewWrapper.remove();
532                  if ($(".preview-wrapper").length === 0) {
533                    $("#file-upload").val("");
534                  } else {
535                    $(
536                      '<div class="text-sm text-red-500 mt-2 mb-2">برای حذف کامل تصاویر، دوباره انتخاب کنید</div>'
537                    )
538                      .insertAfter(previewContainer)
539                      .delay(3000)
540                      .fadeOut(function () {
541                        $(this).remove();
542                      });
543                  }
544                });
545
546                previewWrapper.append(preview);
547                previewWrapper.append(removeBtn);
548                previewContainer.append(previewWrapper);
549              };
550              reader.readAsDataURL(file);
551            }
552          }
553        }
554      });
555
556    // Validación del formulario
557    $("#review-form").on("submit", function (e) {
558      let isValid = true;
559      let errorMessage = "";
560
561      // Verificar comentario
562      if (!$("#comment").val().trim()) {
563        isValid = false;
564        errorMessage += "لطفا متن دیدگاه خود را وارد کنید.\n";
565      }
566
567      // Si no está logueado, verificar campos adicionales
568      if (
569        typeof productReviewsData !== "undefined" &&
570        productReviewsData.isLoggedIn === "false"
571      ) {
572        if (!$("#review_first_name").val().trim()) {
573          isValid = false;
574          errorMessage += "لطفا نام خود را وارد کنید.\n";
575        }
576
577        if (!$("#review_last_name").val().trim()) {
578          isValid = false;
579          errorMessage += "لطفا نام خانوادگی خود را وارد کنید.\n";
580        }
581
582        if (!$("#author").val().trim()) {
583          isValid = false;
584          errorMessage += "لطفا نام کاربری خود را وارد کنید.\n";
585        }
586
587        if (!$("#email").val().trim()) {
588          isValid = false;
589          errorMessage += "لطفا ایمیل خود را وارد کنید.\n";
590        }
591      }
592
593      // Validar formato del archivo
594      if ($("#file-upload")[0].files.length > 0) {
595        const allowedTypes = ["image/jpeg", "image/jpg", "image/png"];
596        const maxSize = 5 * 1024 * 1024; // 5MB
597
598        for (let i = 0; i < $("#file-upload")[0].files.length; i++) {
599          const file = $("#file-upload")[0].files[i];
600
601          if (!allowedTypes.includes(file.type)) {
602            isValid = false;
603            errorMessage +=
604              "لطفا فقط فایل‌های تصویری (JPG, JPEG, PNG) آپلود کنید.\n";
605            break;
606          }
607
608          if (file.size > maxSize) {
609            isValid = false;
610            errorMessage += "حداکثر اندازه هر تصویر 5 مگابایت است.\n";
611            break;
612          }
613        }
614      }
615
616      if (!isValid) {
617        e.preventDefault();
618        alert(errorMessage);
619      } else {
620        // Verificar que el campo rating esté presente y tenga un valor
621        if (!$("#rating").val()) {
622          $("#rating").val(5); // Valor por defecto si de alguna manera no se ha establecido
623        }
624      }
625    });
626
627    // Manejo del sistema de estrellas - versión mejorada
628    $(".rating-stars .star")
629      .off("click")
630      .on("click", function () {
631        const rating = $(this).data("rating");
632
633        // Asegurarse de que el campo oculto existe y actualizarlo
634        if ($("#rating").length) {
635          $("#rating").val(rating);
636        } else {
637          // Crear el campo si no existe
638          $('<input type="hidden" name="rating" id="rating" />')
639            .val(rating)
640            .appendTo("#review-form");
641        }
642
643        // Actualizar la visualización de las estrellas
644        $(".rating-stars .star")
645          .removeClass("star-filled")
646          .addClass("star-empty");
647        for (let i = 1; i <= 5; i++) {
648          if (i <= rating) {
649            $(`.rating-stars .star[data-rating="${i}"]`)
650              .removeClass("star-empty")
651              .addClass("star-filled");
652          }
653        }
654      });
655
656    // Mostrar el overlay de imagen cuando se hace clic en una imagen en la lista de reseñas
657    $(document).on("click", ".grid img, .review-image", function () {
658      // Only proceed if this is within a review section
659      if (
660        !$(this).closest(
661          ".product-review-form, #review-form, .comment-form, #Reviews-tempalte, #reviews"
662        ).length
663      ) {
664        return;
665      }
666
667      const imgSrc = $(this).attr("src");
668      const highResSrc = $(this).closest("a").attr("href") || imgSrc;
669
670      // Crear el overlay
671      const overlay = $('<div class="review-image-overlay"></div>');
672      const imgContainer = $(
673        '<div class="review-image-large-container"></div>'
674      );
675      const img = $(
676        '<img src="' + highResSrc + '" class="review-image-large" />'
677      );
678      const closeBtn = $('<span class="review-image-close">&times;</span>');
679
680      imgContainer.append(img);
681      imgContainer.append(closeBtn);
682      overlay.append(imgContainer);
683      $("body").append(overlay);
684
685      // Agregar clases para hacerlo visible
686      setTimeout(function () {
687        overlay.addClass("active");
688      }, 10);
689
690      // Cerrar al hacer clic
691      overlay.on("click", function (e) {
692        if (e.target === this) {
693          overlay.removeClass("active");
694          setTimeout(function () {
695            overlay.remove();
696          }, 300);
697        }
698      });
699
700      closeBtn.on("click", function () {
701        overlay.removeClass("active");
702        setTimeout(function () {
703          overlay.remove();
704        }, 300);
705      });
706    });
707  });
708
709  function setupSingleProductReviews() {
710    // Setup image overlay for review images
711    setupImageOverlay();
712
713    // Setup star rating functionality if exists
714    if ($(".comment-form-rating").length) {
715      setupStarRating();
716    }
717
718    // Enable clicking on review images to open overlay
719    $(".review-image").on("click", function () {
720      const imgSrc = $(this).attr("src");
721      showImageOverlay(imgSrc);
722    });
723  }
724
725  function setupImageOverlay() {
726    // Add overlay HTML if it doesn't exist
727    if (!$(".review-image-overlay").length) {
728      $("body").append(`
729            <div class="review-image-overlay">
730                <div class="review-image-large-container">
731                    <img src="" class="review-image-large" alt="تصویر بزرگ">
732                    <button class="review-image-close">×</button>
733                </div>
734            </div>
735        `);
736
737      // Close overlay on click
738      $(".review-image-overlay, .review-image-close").on("click", function (e) {
739        if (e.target === this || $(this).hasClass("review-image-close")) {
740          $(".review-image-overlay").removeClass("active");
741        }
742      });
743    }
744  }
745
746  function showImageOverlay(imgSrc) {
747    $(".review-image-large").attr("src", imgSrc);
748    $(".review-image-overlay").addClass("active");
749  }
750
751  function setupStarRating() {
752    // Handle star rating selection
753    $(".comment-form-rating .stars a").on("click", function (e) {
754      e.preventDefault();
755
756      const $star = $(this);
757      const rating = $star.text(); // This is the star's value (1-5)
758
759      // Update the hidden select field
760      $("#rating").val(rating);
761
762      // Highlight the selected star and all stars to its right
763      $star.addClass("active").siblings().removeClass("active");
764
765      return false;
766    });
767  }
768})(jQuery);
769
770/**
771 * JavaScript para gestionar el sistema de calificación por estrellas de productos
772 */
773(function ($) {
774  "use strict";
775
776  $(document).ready(function () {
777    // Inicializar el estado de las estrellas (5 estrellas por defecto)
778    initRatingStars();
779
780    // Manejar los clics en las estrellas
781    $(document).on("click", ".rating-stars .star", function () {
782      const rating = parseInt($(this).data("rating"), 10);
783
784      // Actualizar el campo oculto
785      if ($("#rating").length) {
786        $("#rating").val(rating);
787      } else {
788        // Crear el campo si no existe
789        $(
790          '<input type="hidden" name="rating" id="rating" value="' +
791            rating +
792            '" />'
793        ).appendTo("#review-form");
794      }
795
796      // Actualizar la visualización de las estrellas
797      updateStarsDisplay(rating);
798    });
799
800    // Validar el formulario antes de enviarlo
801    $("#review-form").on("submit", function (e) {
802      // Asegurarse de que la calificación está configurada
803      if (!$("#rating").val() || $("#rating").val() === "0") {
804        $("#rating").val(5);
805      }
806    });
807
808    // Funciones auxiliares
809    function initRatingStars() {
810      // La calificación inicial es 5 por defecto
811      const initialRating = $("#rating").val() || 5;
812      updateStarsDisplay(initialRating);
813    }
814
815    function updateStarsDisplay(rating) {
816      // Primero vaciar todas las estrellas
817      $(".rating-stars .star")
818        .removeClass("star-filled")
819        .addClass("star-empty");
820
821      // Luego llenar las estrellas hasta la calificación seleccionada
822      $(".rating-stars .star").each(function () {
823        const starRating = parseInt($(this).data("rating"), 10);
824        if (starRating <= rating) {
825          $(this).removeClass("star-empty").addClass("star-filled");
826        }
827      });
828    }
829  });
830})(jQuery);

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.