PageSourceSearch

https://gumpla.jp/wp-content/themes/gush/assets/js/upload1.js?15194453

js gumpla.jp collected 2026-10-03 22:14:54 UTC 14,013 bytes, 441 lines download raw bytes

1function updateThumbnailButton(el) {
2  const hidden = el.querySelector("#_thumbnail_id");
3  const submitButton = el.closest("form")?.querySelector('[type="submit"]');
4  if (!hidden || !submitButton) return;
5  submitButton.classList.toggle("no-thumbnail", !hidden.value);
6}
7document.addEventListener("DOMContentLoaded", function () {
8  document.querySelectorAll(".wpuf-el").forEach((el) => {
9    const fileInput = el.querySelector(".js-upload");
10    const btn = el.querySelector(".js-upload-btn");
11    const preview = el.querySelector(".js-preview");
12    const hidden = el.querySelector("input[type='text']");
13    if (!btn || !fileInput) {
14      return;
15    }
16    btn.addEventListener("click", function (e) {
17      e.preventDefault();
18      fileInput.click();
19    });
20    fileInput.addEventListener("change", async function (e) {
21      const files = Array.from(e.target.files);
22      if (!files.length) return;
23
24      const isThumbnail = el.classList.contains("is-thumbnail");
25
26      for (const file of files) {
27        if (isThumbnail) {
28          await handleThumbnailUpload(file, el);
29          updateThumbnailButton(el);
30        } else {
31          await handleUpload(file, el);
32        }
33      }
34      e.target.value = "";
35    });
36  });
37  document.querySelectorAll(".wpuf-el").forEach(async (el) => {
38    const hidden = el.querySelector("input[type='text']");
39    const preview = el.querySelector(".js-preview");
40    const btn = el.querySelector(".js-upload-btn");
41    if (!hidden || !hidden.value) return;
42    const formData = new FormData();
43    formData.append("action", "get_attachment_preview");
44    formData.append("attachment_id", hidden.value);
45    formData.append("nonce", simpleUploadNonce);
46    const res = await fetch("/wp-admin/admin-ajax.php", {
47      method: "POST",
48      body: formData,
49      credentials: "same-origin",
50    });
51    const json = await res.json();
52    if (!json.success) return;
53    const url = json.data.url;
54    const box = document.createElement("div");
55    box.className = "wpuf-image-wrap";
56    box.innerHTML = `
57      <img src="${url}?t=${Date.now()}">
58      <button type="button" class="attachment-delete"><i class="icon-close"></i></button>
59    `;
60    preview.appendChild(box);
61    btn.style.display = "none";
62  });
63  document.addEventListener(
64    "click",
65    function (e) {
66      const submitButton = e.target.closest(".wpuf-submit-button");
67
68      if (!submitButton) return;
69
70      if (!submitButton.classList.contains("no-thumbnail")) {
71        return;
72      }
73
74      console.log("no-thumbnail: 送信を阻止");
75
76      e.preventDefault();
77      e.stopPropagation();
78      e.stopImmediatePropagation();
79
80      const form = submitButton.closest("form");
81      if (!form) return;
82
83      // -------------------------
84      // ① メイン写真のエラー
85      // -------------------------
86
87      const input = form.querySelector("#_thumbnail_id");
88      const field = input?.closest(".wpuf-el");
89
90      if (field) {
91        field.querySelectorAll(".wpuf-error-msg").forEach((el) => el.remove());
92
93        const error = document.createElement("div");
94        error.className = "wpuf-error-msg";
95        error.textContent = "メイン写真は必須です";
96
97        const help = [...field.querySelectorAll(".wpuf-help")].find((el) =>
98          el.querySelector(".js-upload-btn"),
99        );
100
101        if (help) {
102          help.insertAdjacentElement("afterend", error);
103        } else {
104          field.appendChild(error);
105        }
106      }
107
108      // -------------------------
109      // ② WPUFの全体エラー
110      // -------------------------
111
112      const wpufErrors = form.querySelector(".wpuf-errors");
113
114      if (wpufErrors) {
115        wpufErrors.style.display = "block";
116      } else {
117        // なければ作る
118        const submitArea = submitButton.closest(".wpuf-submit");
119
120        if (submitArea) {
121          const error = document.createElement("div");
122          error.className = "wpuf-errors";
123          error.textContent =
124            "未入力のエラー等があります。ご確認の上ご投稿ください";
125
126          submitArea.appendChild(error);
127        }
128      }
129    },
130    true,
131  );
132  document.querySelectorAll(".wpuf-el.is-thumbnail").forEach((el) => {
133    updateThumbnailButton(el);
134  });
135});
136async function handleUpload(file, el) {
137  const MAX_SIZE = 20 * 1024 * 1024;
138  if (file.size > MAX_SIZE) {
139    alert("ファイルサイズが20MBを超えています");
140    return;
141  }
142  const preview = el.querySelector(".js-preview");
143  const btn = el.querySelector(".js-upload-btn");
144  const hidden = el.querySelector("input[type='text']");
145  const isGif = file.type === "image/gif";
146  if (isGif) {
147    cleanFile = file; // ★そのまま
148  } else {
149    cleanFile = await compress(file);
150  }
151  const box = document.createElement("div");
152  box.className = "wpuf-image-wrap";
153  box.innerHTML = `<div class="upload-item"><div class="progress progress-striped active"><div class="bar" style="width: 80%;"></div></div><div class="filename original"><b><img src="/wp-content/themes/gush/img/icon_loading.gif"></b>
153アップロード中(しばらくお待ちください)</div></div>`;
154  preview.appendChild(box);
155  try {
156    const json = await uploadToServer(cleanFile);
157    if (!json.success) throw new Error("upload failed");
158    const url = json.sizes?.large?.url || json.url;
159    hidden.value = json.attachment_id;
160    btn.style.display = "none";
161    box.innerHTML = `
162      <img src="${url}?t=${Date.now()}">
163      <button type="button" class="attachment-delete"><i class="icon-close"></i></button>
164    `;
165  } catch (e) {
166    console.error(e);
167    box.innerHTML = `<span>upload failed</span>`;
168  }
169}
170async function uploadToServer(file) {
171  const formData = new FormData();
172  formData.append("action", "simple_upload");
173  formData.append("file", file, file.name);
174  formData.append("nonce", simpleUploadNonce);
175  const res = await fetch("/wp-admin/admin-ajax.php", {
176    method: "POST",
177    body: formData,
178    credentials: "same-origin",
179  });
180  return await res.json();
181}
182async function compress(file) {
183  const isGif = file.type === "image/gif";
184  if (isGif) return file;
185  const isJpeg = file.type === "image/jpeg";
186  const compressedBlob = await imageCompression(file, {
187    maxSizeMB: 1,
188    maxWidthOrHeight: 2048,
189    useWebWorker: true,
190    fileType: file.type,
191    initialQuality: 0.9,
192    exifOrientation: -1,
193  });
194  const originalName = file.name.replace(/\.[^/.]+$/, "");
195  const ext = file.name.split(".").pop();
196  return new File([compressedBlob], `${originalName}.${ext}`, {
197    type: file.type,
198    lastModified: Date.now(),
199  });
200}
201async function handleThumbnailUpload(file, el) {
202  const preview = el.querySelector(".js-preview");
203  const btn = el.querySelector(".js-upload-btn");
204  const hidden = el.querySelector("input[type='text']");
205  const originalInput = el.querySelector(".original-id");
206
207  preview.innerHTML = `
208    <div class="upload-item">
209      <div class="progress progress-striped active">
210        <div class="bar" style="width: 80%;"></div>
211      </div>
212      <div class="filename original">
213        <b><img src="/wp-content/themes/gush/img/icon_loading.gif"></b>
214        アップロード中(しばらくお待ちください)
215      </div>
216    </div>
217  `;
218
219  const cleanFile = await compress(file);
220  const originalJson = await uploadToServer(cleanFile);
221  if (!originalJson.success) {
222    alert("アップロード失敗");
223    return;
224  }
225  const originalId = originalJson.attachment_id;
226  const imageUrl = originalJson.url;
227  const filename = imageUrl.split("/").pop();
228  const originalName = filename.replace(/\.[^/.]+$/, "");
229  openCropModal(imageUrl, {
230    el,
231    hidden,
232    btn,
233    preview,
234    originalId,
235    originalInput,
236    originalName,
237    mimeType: file?.type,
238  });
239}
240let cropperInstance = null;
241let cropState = null;
242function createCropModal() {
243  if (document.getElementById("cropModal")) return;
244  const modal = document.createElement("div");
245  modal.id = "cropModal";
246  modal.innerHTML = `
247    <div class="crop-overlay"></div>
248    <div class="c-daalog__modal">
249      <div class="c-dialog__header">
250        <h3 class="c-dialog__header-title">
250画像の切り抜き</h3>
251      </div>
252      <div class="crop-body">
253        <div class="crop-container">
254          <img id="cropModalTarget">
255        </div>
256      </div>
257      <ul class="inner_margin crop-bottom-list">
258        <li class="crop-bottom-item">
259          <button id="cropConfirmBtn" class="c-button__regist">保存</button>
260        </li>
261        <li class="crop-bottom-item">
262          <button id="cropCancelBtn" class="c-button__login">キャンセル</button>
263        </li>
264      </ul>
265    </div>
266  `;
267  modal.style.cssText = `
268    position:fixed;
269    inset:0;
270    z-index:99999;
271    display:none;
272  `;
273  document.body.appendChild(modal);
274  bindCropModalEvents(modal);
275}
276function openCropModal(imageUrl, state) {
277  createCropModal();
278  cropState = state;
279  const modal = document.getElementById("cropModal");
280  const img = document.getElementById("cropModalTarget");
281  modal.style.display = "block";
282  document.body.style.overflow = "hidden";
283  img.onload = () => {
284    const ratio = img.naturalWidth / img.naturalHeight;
285    const modal = document.getElementById("cropModal");
286    const body = modal.querySelector(".crop-body");
287    const maxWidth = Math.min(window.innerWidth * 0.9, 600);
288    const maxHeight = window.innerHeight * 0.8;
289    let width = maxWidth;
290    let height = width / ratio;
291    if (height > maxHeight) {
292      height = maxHeight;
293      width = height * ratio;
294    }
295    body.style.width = width + "px";
296    body.style.height = height + "px";
297    if (cropperInstance) {
298      cropperInstance.destroy();
299    }
300    cropperInstance = new Cropper(img, {
301      aspectRatio: 1,
302      viewMode: 1,
303      autoCropArea: 1,
304      movable: false,
305      zoomable: false,
306      background: false,
307    });
308  };
309  img.src = imageUrl;
310}
311function bindCropModalEvents(modal) {
312  const confirmBtn = modal.querySelector("#cropConfirmBtn");
313  const cancelBtn = modal.querySelector("#cropCancelBtn");
314  confirmBtn.addEventListener("click", async () => {
315    if (!cropperInstance) return;
316    const canvas = cropperInstance.getCroppedCanvas({
317      width: 750,
318      height: 750,
319    });
320    const baseName = cropState.originalName || "image";
321    const isGif = cropState.mimeType === "image/gif";
322    let file;
323    if (isGif) {
324      const gif = new GIF({
325        workers: 2,
326        quality: 10,
327        width: canvas.width,
328        height: canvas.height,
329        workerScript: "/wp-content/themes/gush/assets/js/gif.worker.js",
330      });
331      gif.addFrame(canvas, { delay: 200 });
332      file = await new Promise((resolve) => {
333        gif.on("finished", function (blob) {
334          const f = new File([blob], `${baseName}-aspect-ratio-750-750.gif`, {
335            type: "image/gif",
336          });
337          resolve(f);
338        });
339        gif.render();
340      });
341    } else {
342      let mime = "image/jpeg";
343      let ext = "jpg";
344      if (cropState.mimeType === "image/png") {
345        mime = "image/png";
346        ext = "png";
347      }
348      const blob = await new Promise((resolve) => {
349        canvas.toBlob(resolve, mime, 0.9);
350      });
351      file = new File([blob], `${baseName}-aspect-ratio-750-750.${ext}`, {
352        type: mime,
353      });
354    }
355    const json = await uploadToServer(file);
356    const { hidden, btn, preview, originalId, originalInput } = cropState;
357    hidden.value = json.attachment_id;
358    updateThumbnailButton(cropState.el);
359    if (originalInput) {
360      originalInput.value = originalId;
361    }
362    preview.innerHTML = `
363      <div class="wpuf-image-wrap">
364        <img src="${json.url}?t=${Date.now()}">
365        <button type="button" class="attachment-delete">
366          <i class="icon-close"></i>
367        </button>
368      </div>
369    `;
370    btn.style.display = "none";
371    closeCropModal();
372  });
373  cancelBtn.addEventListener("click", async () => {
374    const { preview, hidden, btn, originalId } = cropState;
375    closeCropModal();
376    preview.innerHTML = "";
377    hidden.value = "";
378    btn.style.display = "";
379    if (originalId) {
380      const formData = new FormData();
381      formData.append("action", "delete_image");
382      formData.append("attachment_id", originalId);
383      formData.append("nonce", simpleUploadNonce);
384      await fetch("/wp-admin/admin-ajax.php", {
385        method: "POST",
386        body: formData,
387        credentials: "same-origin",
388      });
389    }
390  });
391}
392function closeCropModal() {
393  const modal = document.getElementById("cropModal");
394  if (cropperInstance) {
395    cropperInstance.destroy();
396    cropperInstance = null;
397  }
398  if (modal) {
399    modal.style.display = "none";
400  }
401  document.body.style.overflow = "";
402  cropState = null;
403}
404document.addEventListener("click", async (e) => {
405  const btn = e.target.closest(".attachment-delete");
406  if (!btn) return;
407  const wrap = btn.closest(".wpuf-image-wrap");
408  const el = btn.closest(".wpuf-el");
409  if (!wrap || !el) return;
410  const hidden = el.querySelector("input[type='text']");
411  const mainBtn = el.querySelector(".js-upload-btn");
412  const attachmentId = hidden.value;
413  if (!attachmentId) return;
414  const ok = confirm(
415    "この画像を削除します。元に戻せません。本当によろしいですか?",
416  );
417  if (!ok) return;
418  const isThumbnail = el.classList.contains("is-thumbnail");
419  if (isThumbnail) {
420    wrap.remove();
421    hidden.value = "";
422    mainBtn.style.display = "";
423    updateThumbnailButton(el);
424    return;
425  }
426  const formData = new FormData();
427  formData.append("action", "delete_image");
428  formData.append("attachment_id", attachmentId);
429  formData.append("nonce", simpleUploadNonce);
430  const res = await fetch("/wp-admin/admin-ajax.php", {
431    method: "POST",
432    body: formData,
433    credentials: "same-origin",
434  });
435  const result = await res.json();
436  if (result.success) {
437    wrap.remove();
438    hidden.value = "";
439    mainBtn.style.display = "";
440  }
441});

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.