@magicfeedback/popup-sdk 1.15.0 → 1.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -253,7 +253,8 @@ var init_labels = __esm({
253
253
  closeAria: "Close popup",
254
254
  loadingAria: "Loading survey",
255
255
  errorRequired: "Please answer the required question to continue.",
256
- errorSubmit: "An error occurred while submitting. Please try again or close the popup."
256
+ errorSubmit: "An error occurred while submitting. Please try again or close the popup.",
257
+ errorLoad: "We couldn't load this survey."
257
258
  };
258
259
  ES = {
259
260
  accept: "Enviar",
@@ -267,7 +268,8 @@ var init_labels = __esm({
267
268
  closeAria: "Cerrar ventana emergente",
268
269
  loadingAria: "Cargando encuesta",
269
270
  errorRequired: "Responde la pregunta obligatoria para continuar.",
270
- errorSubmit: "Se produjo un error al enviar. Int\xE9ntalo de nuevo o cierra la ventana."
271
+ errorSubmit: "Se produjo un error al enviar. Int\xE9ntalo de nuevo o cierra la ventana.",
272
+ errorLoad: "No se ha podido cargar la encuesta."
271
273
  };
272
274
  DA = {
273
275
  accept: "Send",
@@ -281,7 +283,8 @@ var init_labels = __esm({
281
283
  closeAria: "Luk pop op",
282
284
  loadingAria: "Indl\xE6ser unders\xF8gelse",
283
285
  errorRequired: "Besvar venligst det obligatoriske sp\xF8rgsm\xE5l for at forts\xE6tte.",
284
- errorSubmit: "Der opstod en fejl under afsendelsen. Pr\xF8v igen, eller luk pop op-vinduet."
286
+ errorSubmit: "Der opstod en fejl under afsendelsen. Pr\xF8v igen, eller luk pop op-vinduet.",
287
+ errorLoad: "Unders\xF8gelsen kunne ikke indl\xE6ses."
285
288
  };
286
289
  NO = {
287
290
  accept: "Send",
@@ -295,7 +298,8 @@ var init_labels = __esm({
295
298
  closeAria: "Lukk popup",
296
299
  loadingAria: "Laster unders\xF8kelse",
297
300
  errorRequired: "Svar p\xE5 det obligatoriske sp\xF8rsm\xE5let for \xE5 fortsette.",
298
- errorSubmit: "Det oppstod en feil under innsendingen. Pr\xF8v igjen, eller lukk popupen."
301
+ errorSubmit: "Det oppstod en feil under innsendingen. Pr\xF8v igjen, eller lukk popupen.",
302
+ errorLoad: "Unders\xF8kelsen kunne ikke lastes inn."
299
303
  };
300
304
  SV = {
301
305
  accept: "Skicka",
@@ -309,7 +313,8 @@ var init_labels = __esm({
309
313
  closeAria: "St\xE4ng popup",
310
314
  loadingAria: "Laddar unders\xF6kning",
311
315
  errorRequired: "Svara p\xE5 den obligatoriska fr\xE5gan f\xF6r att forts\xE4tta.",
312
- errorSubmit: "Ett fel uppstod vid inskickningen. F\xF6rs\xF6k igen eller st\xE4ng popupen."
316
+ errorSubmit: "Ett fel uppstod vid inskickningen. F\xF6rs\xF6k igen eller st\xE4ng popupen.",
317
+ errorLoad: "Enk\xE4ten kunde inte laddas."
313
318
  };
314
319
  FI = {
315
320
  accept: "L\xE4het\xE4",
@@ -323,7 +328,8 @@ var init_labels = __esm({
323
328
  closeAria: "Sulje ponnahdusikkuna",
324
329
  loadingAria: "Ladataan kysely\xE4",
325
330
  errorRequired: "Vastaa pakolliseen kysymykseen jatkaaksesi.",
326
- errorSubmit: "L\xE4hetyksess\xE4 tapahtui virhe. Yrit\xE4 uudelleen tai sulje ponnahdusikkuna."
331
+ errorSubmit: "L\xE4hetyksess\xE4 tapahtui virhe. Yrit\xE4 uudelleen tai sulje ponnahdusikkuna.",
332
+ errorLoad: "Kysely\xE4 ei voitu ladata."
327
333
  };
328
334
  DE = {
329
335
  accept: "Senden",
@@ -337,7 +343,8 @@ var init_labels = __esm({
337
343
  closeAria: "Popup schlie\xDFen",
338
344
  loadingAria: "Umfrage wird geladen",
339
345
  errorRequired: "Bitte beantworte die Pflichtfrage, um fortzufahren.",
340
- errorSubmit: "Beim Senden ist ein Fehler aufgetreten. Versuche es erneut oder schlie\xDFe das Popup."
346
+ errorSubmit: "Beim Senden ist ein Fehler aufgetreten. Versuche es erneut oder schlie\xDFe das Popup.",
347
+ errorLoad: "Die Umfrage konnte nicht geladen werden."
341
348
  };
342
349
  FR = {
343
350
  accept: "Envoyer",
@@ -351,7 +358,8 @@ var init_labels = __esm({
351
358
  closeAria: "Fermer la fen\xEAtre",
352
359
  loadingAria: "Chargement de l'enqu\xEAte",
353
360
  errorRequired: "Veuillez r\xE9pondre \xE0 la question obligatoire pour continuer.",
354
- errorSubmit: "Une erreur s'est produite lors de l'envoi. R\xE9essayez ou fermez la fen\xEAtre."
361
+ errorSubmit: "Une erreur s'est produite lors de l'envoi. R\xE9essayez ou fermez la fen\xEAtre.",
362
+ errorLoad: "Impossible de charger l'enqu\xEAte."
355
363
  };
356
364
  PT = {
357
365
  accept: "Enviar",
@@ -365,7 +373,8 @@ var init_labels = __esm({
365
373
  closeAria: "Fechar janela",
366
374
  loadingAria: "A carregar question\xE1rio",
367
375
  errorRequired: "Responda \xE0 pergunta obrigat\xF3ria para continuar.",
368
- errorSubmit: "Ocorreu um erro ao enviar. Tente novamente ou feche a janela."
376
+ errorSubmit: "Ocorreu um erro ao enviar. Tente novamente ou feche a janela.",
377
+ errorLoad: "N\xE3o foi poss\xEDvel carregar o inqu\xE9rito."
369
378
  };
370
379
  AR = {
371
380
  accept: "\u0625\u0631\u0633\u0627\u0644",
@@ -379,7 +388,8 @@ var init_labels = __esm({
379
388
  closeAria: "\u0625\u063A\u0644\u0627\u0642 \u0627\u0644\u0646\u0627\u0641\u0630\u0629 \u0627\u0644\u0645\u0646\u0628\u062B\u0642\u0629",
380
389
  loadingAria: "\u062C\u0627\u0631\u064D \u062A\u062D\u0645\u064A\u0644 \u0627\u0644\u0627\u0633\u062A\u0628\u064A\u0627\u0646",
381
390
  errorRequired: "\u064A\u0631\u062C\u0649 \u0627\u0644\u0625\u062C\u0627\u0628\u0629 \u0639\u0644\u0649 \u0627\u0644\u0633\u0624\u0627\u0644 \u0627\u0644\u0645\u0637\u0644\u0648\u0628 \u0644\u0644\u0645\u062A\u0627\u0628\u0639\u0629.",
382
- errorSubmit: "\u062D\u062F\u062B \u062E\u0637\u0623 \u0623\u062B\u0646\u0627\u0621 \u0627\u0644\u0625\u0631\u0633\u0627\u0644. \u064A\u0631\u062C\u0649 \u0627\u0644\u0645\u062D\u0627\u0648\u0644\u0629 \u0645\u0631\u0629 \u0623\u062E\u0631\u0649 \u0623\u0648 \u0625\u063A\u0644\u0627\u0642 \u0627\u0644\u0646\u0627\u0641\u0630\u0629."
391
+ errorSubmit: "\u062D\u062F\u062B \u062E\u0637\u0623 \u0623\u062B\u0646\u0627\u0621 \u0627\u0644\u0625\u0631\u0633\u0627\u0644. \u064A\u0631\u062C\u0649 \u0627\u0644\u0645\u062D\u0627\u0648\u0644\u0629 \u0645\u0631\u0629 \u0623\u062E\u0631\u0649 \u0623\u0648 \u0625\u063A\u0644\u0627\u0642 \u0627\u0644\u0646\u0627\u0641\u0630\u0629.",
392
+ errorLoad: "\u062A\u0639\u0630\u0651\u0631 \u062A\u062D\u0645\u064A\u0644 \u0627\u0644\u0627\u0633\u062A\u0628\u064A\u0627\u0646."
383
393
  };
384
394
  BN = {
385
395
  accept: "\u09AA\u09BE\u09A0\u09BE\u09A8",
@@ -393,7 +403,8 @@ var init_labels = __esm({
393
403
  closeAria: "\u09AA\u09AA\u0986\u09AA \u09AC\u09A8\u09CD\u09A7 \u0995\u09B0\u09C1\u09A8",
394
404
  loadingAria: "\u099C\u09B0\u09BF\u09AA \u09B2\u09CB\u09A1 \u09B9\u099A\u09CD\u099B\u09C7",
395
405
  errorRequired: "\u099A\u09BE\u09B2\u09BF\u09AF\u09BC\u09C7 \u09AF\u09C7\u09A4\u09C7 \u0986\u09AC\u09B6\u09CD\u09AF\u0995 \u09AA\u09CD\u09B0\u09B6\u09CD\u09A8\u09C7\u09B0 \u0989\u09A4\u09CD\u09A4\u09B0 \u09A6\u09BF\u09A8\u0964",
396
- errorSubmit: "\u099C\u09AE\u09BE \u09A6\u09C7\u0993\u09AF\u09BC\u09BE\u09B0 \u09B8\u09AE\u09AF\u09BC \u098F\u0995\u099F\u09BF \u09A4\u09CD\u09B0\u09C1\u099F\u09BF \u0998\u099F\u09C7\u099B\u09C7\u0964 \u0986\u09AC\u09BE\u09B0 \u099A\u09C7\u09B7\u09CD\u099F\u09BE \u0995\u09B0\u09C1\u09A8 \u09AC\u09BE \u09AA\u09AA\u0986\u09AA \u09AC\u09A8\u09CD\u09A7 \u0995\u09B0\u09C1\u09A8\u0964"
406
+ errorSubmit: "\u099C\u09AE\u09BE \u09A6\u09C7\u0993\u09AF\u09BC\u09BE\u09B0 \u09B8\u09AE\u09AF\u09BC \u098F\u0995\u099F\u09BF \u09A4\u09CD\u09B0\u09C1\u099F\u09BF \u0998\u099F\u09C7\u099B\u09C7\u0964 \u0986\u09AC\u09BE\u09B0 \u099A\u09C7\u09B7\u09CD\u099F\u09BE \u0995\u09B0\u09C1\u09A8 \u09AC\u09BE \u09AA\u09AA\u0986\u09AA \u09AC\u09A8\u09CD\u09A7 \u0995\u09B0\u09C1\u09A8\u0964",
407
+ errorLoad: "\u09B8\u09AE\u09C0\u0995\u09CD\u09B7\u09BE\u099F\u09BF \u09B2\u09CB\u09A1 \u0995\u09B0\u09BE \u09AF\u09BE\u09AF\u09BC\u09A8\u09BF\u0964"
397
408
  };
398
409
  ZH_CN = {
399
410
  accept: "\u53D1\u9001",
@@ -407,7 +418,8 @@ var init_labels = __esm({
407
418
  closeAria: "\u5173\u95ED\u5F39\u7A97",
408
419
  loadingAria: "\u6B63\u5728\u52A0\u8F7D\u95EE\u5377",
409
420
  errorRequired: "\u8BF7\u56DE\u7B54\u5FC5\u7B54\u9898\u540E\u7EE7\u7EED\u3002",
410
- errorSubmit: "\u63D0\u4EA4\u65F6\u51FA\u9519\u3002\u8BF7\u91CD\u8BD5\u6216\u5173\u95ED\u5F39\u7A97\u3002"
421
+ errorSubmit: "\u63D0\u4EA4\u65F6\u51FA\u9519\u3002\u8BF7\u91CD\u8BD5\u6216\u5173\u95ED\u5F39\u7A97\u3002",
422
+ errorLoad: "\u65E0\u6CD5\u52A0\u8F7D\u95EE\u5377\u3002"
411
423
  };
412
424
  LABELS = {
413
425
  en: EN,
@@ -2992,10 +3004,11 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
2992
3004
  closeBtn.style.color = theme.textPrimary;
2993
3005
  closeBtn.style.transform = "scale(1)";
2994
3006
  };
2995
- closeBtn.onclick = () => {
3007
+ const closeFromChrome = () => {
2996
3008
  emit("popup_clicked", surveyId, { action: "close_icon" });
2997
3009
  onClose();
2998
3010
  };
3011
+ closeBtn.onclick = closeFromChrome;
2999
3012
  closeBtn.style.flex = "0 0 auto";
3000
3013
  closeBtn.style.marginLeft = "auto";
3001
3014
  header.appendChild(closeBtn);
@@ -3103,6 +3116,12 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
3103
3116
  formWrapper.appendChild(spinnerEl);
3104
3117
  formWrapper.appendChild(formHost);
3105
3118
  main.appendChild(formWrapper);
3119
+ const loadErrorEl = document.createElement("div");
3120
+ loadErrorEl.id = "dd-load-error";
3121
+ loadErrorEl.className = "deepdots-load-error";
3122
+ loadErrorEl.setAttribute("role", "alert");
3123
+ loadErrorEl.style.cssText = `display:none; width:100%; text-align:center; padding:24px 0; font-size:15px; color:${theme.textPrimary};`;
3124
+ main.appendChild(loadErrorEl);
3106
3125
  main.appendChild(errorHint);
3107
3126
  const footer = document.createElement("div");
3108
3127
  footer.id = "dd-footer";
@@ -3218,6 +3237,13 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
3218
3237
  justify-content: center;
3219
3238
  text-align: center;
3220
3239
  `;
3240
+ const cancelButton = document.createElement("button");
3241
+ cancelButton.id = "dd-cancel";
3242
+ cancelButton.type = "button";
3243
+ cancelButton.className = "dd-nav-btn";
3244
+ cancelButton.textContent = actionLabels.decline;
3245
+ cancelButton.style.cssText = submitButton.style.cssText;
3246
+ cancelButton.onclick = closeFromChrome;
3221
3247
  submitButton.onclick = () => {
3222
3248
  if (!surveyCompletedEmitted) {
3223
3249
  emit("popup_clicked", surveyId, { action: "manual_send" });
@@ -3231,6 +3257,8 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
3231
3257
  startButton.textContent = actionLabels.start;
3232
3258
  closeButton.textContent = actionLabels.complete;
3233
3259
  submitButton.textContent = actionLabels.accept;
3260
+ cancelButton.textContent = actionLabels.decline;
3261
+ loadErrorEl.textContent = labels.errorLoad;
3234
3262
  closeBtn.setAttribute("aria-label", labels.closeAria);
3235
3263
  spinnerEl.setAttribute("aria-label", labels.loadingAria);
3236
3264
  progressFollowUp.textContent = labels.followUp;
@@ -3241,10 +3269,12 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
3241
3269
  startButton.style.display = "none";
3242
3270
  submitButton.style.display = "none";
3243
3271
  closeButton.style.display = "none";
3272
+ cancelButton.style.display = "none";
3244
3273
  footer.appendChild(submitButton);
3245
3274
  footer.appendChild(backButton);
3246
3275
  footer.appendChild(closeButton);
3247
3276
  footer.appendChild(startButton);
3277
+ footer.appendChild(cancelButton);
3248
3278
  containerContent.appendChild(main);
3249
3279
  footer.style.flex = "0 0 auto";
3250
3280
  containerContent.appendChild(footer);
@@ -3253,6 +3283,7 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
3253
3283
  startButton.style.display = "none";
3254
3284
  submitButton.style.display = "none";
3255
3285
  closeButton.style.display = "none";
3286
+ cancelButton.style.display = "none";
3256
3287
  backButton.style.width = "";
3257
3288
  startButton.style.width = "";
3258
3289
  submitButton.style.width = "";
@@ -3282,6 +3313,9 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
3282
3313
  case "error":
3283
3314
  setLoading(false);
3284
3315
  break;
3316
+ case "load_error":
3317
+ cancelButton.style.display = "inline-flex";
3318
+ break;
3285
3319
  }
3286
3320
  }
3287
3321
  function updateNavButtons() {
@@ -3303,6 +3337,20 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
3303
3337
  img.style.cssText = "max-width:100%; height:auto; margin:0 auto 16px auto; display:block;";
3304
3338
  });
3305
3339
  }
3340
+ let loadFailed = false;
3341
+ function showLoadError(reason) {
3342
+ sdkWarn("[Deepdots] survey failed to load:", reason);
3343
+ if (loadFailed) return;
3344
+ loadFailed = true;
3345
+ progressEl.style.display = "none";
3346
+ formWrapper.style.display = "none";
3347
+ errorHint.style.display = "none";
3348
+ loadErrorEl.textContent = labels.errorLoad;
3349
+ loadErrorEl.style.display = "block";
3350
+ setLoading(false);
3351
+ updateButtons("load_error");
3352
+ reveal();
3353
+ }
3306
3354
  popup.appendChild(header);
3307
3355
  popup.appendChild(progressEl);
3308
3356
  popup.appendChild(containerContent);
@@ -3367,7 +3415,7 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
3367
3415
  try {
3368
3416
  if (!import_native.default || typeof import_native.default.form !== "function") {
3369
3417
  sdkWarn("[MagicFeedback] form() no disponible. Fallback manual.");
3370
- setLoading(false);
3418
+ showLoadError("form() not available");
3371
3419
  return;
3372
3420
  }
3373
3421
  import_native.default.init(
@@ -3393,7 +3441,11 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
3393
3441
  // quitado el `inert`. La apertura va aparte (ver autofocusOnOpen).
3394
3442
  autofocus: "navigation"
3395
3443
  };
3396
- generateOptions.onLoadedEvent = ({ formData, lang }) => {
3444
+ generateOptions.onLoadedEvent = ({ formData, lang, error }) => {
3445
+ if (error) {
3446
+ showLoadError(error);
3447
+ return;
3448
+ }
3397
3449
  const shownLang = typeof lang === "string" && lang.trim().length > 0 ? lang : void 0;
3398
3450
  const surveyLang = shownLang ?? (Array.isArray(formData?.lang) ? formData.lang.find((l) => typeof l === "string" && l.trim().length > 0) : void 0);
3399
3451
  if (surveyLang) applyLabels(surveyLang);
@@ -3492,11 +3544,11 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
3492
3544
  };
3493
3545
  formInstance.generate(formDivId, generateOptions).catch((err) => {
3494
3546
  sdkError("[MagicFeedback] Error generating form:", err);
3495
- setLoading(false);
3547
+ showLoadError(err);
3496
3548
  });
3497
3549
  } catch (e) {
3498
3550
  sdkError("[MagicFeedback] Exception initializing form:", e);
3499
- setLoading(false);
3551
+ showLoadError(e);
3500
3552
  }
3501
3553
  }
3502
3554
  var import_native, SURVEY_ALIGNMENT_STYLE_ID;
@@ -3809,12 +3861,14 @@ function buildSurveyHtml(opts) {
3809
3861
  const startLabel = jsonForScript(initActionLabels.start);
3810
3862
  const completeLabel = jsonForScript(initActionLabels.complete);
3811
3863
  const submitLabel = jsonForScript(initActionLabels.accept);
3864
+ const cancelLabel = jsonForScript(initActionLabels.decline);
3812
3865
  const labelsTableJson = jsonForScript(LABELS);
3813
3866
  const actionOverridesJson = jsonForScript({
3814
3867
  accept: trimmedOrNull(opts.actions?.accept?.label),
3815
3868
  start: trimmedOrNull(opts.actions?.start?.label),
3816
3869
  complete: trimmedOrNull(opts.actions?.complete?.label),
3817
- back: trimmedOrNull(opts.actions?.back?.label)
3870
+ back: trimmedOrNull(opts.actions?.back?.label),
3871
+ decline: trimmedOrNull(opts.actions?.decline?.label)
3818
3872
  });
3819
3873
  const initLangJson = jsonForScript(opts.language ?? "");
3820
3874
  const initDirection = directionFor(opts.language);
@@ -3876,6 +3930,7 @@ body.dd-ready #dd-popup{visibility:visible}
3876
3930
  #dd-logo{max-height:40px;max-width:100%;object-fit:contain;display:block;margin:12px 0 0 0}
3877
3931
  .deepdots-error-hint{display:none;margin:12px 0 0 0;padding:10px 12px;border-radius:6px;background:#FEF3C7;color:#92400E;border:1px solid #FCD34D;font-size:13px}
3878
3932
  /* Pantalla final: el HTML del editor de la plataforma (imagen + texto), centrado. */
3933
+ .deepdots-load-error{display:none;width:100%;text-align:center;padding:24px 0;font-size:15px;color:${textPrimary}}
3879
3934
  .deepdots-success{display:none;width:100%;text-align:center;padding:24px 0;color:${textPrimary}}
3880
3935
  .deepdots-success img{max-width:100%;height:auto;margin:0 auto 16px auto;display:block}
3881
3936
  .deepdots-success p{margin:0;font-size:16px;font-weight:600;line-height:1.4}
@@ -3883,7 +3938,7 @@ body.dd-ready #dd-popup{visibility:visible}
3883
3938
  .dd-nav-btn{display:none;border:none;min-height:44px;padding:12px 24px;border-radius:999px;cursor:pointer;font-size:15px;font-weight:600;align-items:center;justify-content:center;text-align:center}
3884
3939
  /* Secundario como bot\xF3n de texto: sin borde ni fondo, para que el primario sea la \xFAnica CTA. */
3885
3940
  #dd-back{background:transparent;color:${textMuted};border:none}
3886
- #dd-start,#dd-complete,#dd-submit{background:#1E293B;color:#fff;border:none}
3941
+ #dd-start,#dd-complete,#dd-submit,#dd-cancel{background:#1E293B;color:#fff;border:none}
3887
3942
  #dd-submit:disabled,#dd-start:disabled,#dd-back:disabled,#dd-complete:disabled{opacity:0.6;cursor:not-allowed}
3888
3943
  .mf-spinner{display:none;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)}
3889
3944
  .mf-spinner-circle{width:28px;height:28px;border:3px solid #e0e6ed;border-top-color:#1E293B;border-radius:50%;animation:ddspin 0.9s linear infinite}
@@ -3924,6 +3979,7 @@ ${customCss}
3924
3979
  <div class="mf-spinner" id="dd-spinner" role="status" aria-label="${initLabels.loadingAria}"><div class="mf-spinner-circle"></div></div>
3925
3980
  <div id="mf"></div>
3926
3981
  </div>
3982
+ <div class="deepdots-load-error" id="dd-load-error" role="alert"></div>
3927
3983
  <div class="deepdots-error-hint" id="dd-error" role="alert" aria-live="polite"></div>
3928
3984
  </div>
3929
3985
  <div class="deepdots-popup-footer" id="dd-footer">
@@ -3931,6 +3987,7 @@ ${customCss}
3931
3987
  <button type="button" class="dd-nav-btn" id="dd-back"></button>
3932
3988
  <button type="button" class="dd-nav-btn" id="dd-complete"></button>
3933
3989
  <button type="button" class="dd-nav-btn" id="dd-start"></button>
3990
+ <button type="button" class="dd-nav-btn" id="dd-cancel"></button>
3934
3991
  </div>
3935
3992
  </div>
3936
3993
  </div>
@@ -3953,6 +4010,8 @@ ${customCss}
3953
4010
  var startBtn=document.getElementById('dd-start');
3954
4011
  var completeBtn=document.getElementById('dd-complete');
3955
4012
  var submitBtn=document.getElementById('dd-submit');
4013
+ var cancelBtn=document.getElementById('dd-cancel');
4014
+ var loadErrorEl=document.getElementById('dd-load-error');
3956
4015
  var titleEl=document.getElementById('dd-title');
3957
4016
  var progressEl=document.getElementById('dd-progress');
3958
4017
  var progressLabel=document.getElementById('dd-progress-label');
@@ -3982,6 +4041,8 @@ ${customCss}
3982
4041
  startBtn.textContent=DD_ACTION_OVERRIDES.start||next.start;
3983
4042
  completeBtn.textContent=DD_ACTION_OVERRIDES.complete||next.complete;
3984
4043
  submitBtn.textContent=DD_ACTION_OVERRIDES.accept||next.accept;
4044
+ cancelBtn.textContent=DD_ACTION_OVERRIDES.decline||next.decline;
4045
+ loadErrorEl.textContent=next.errorLoad;
3985
4046
  if(closeEl){ closeEl.setAttribute('aria-label',next.closeAria); }
3986
4047
  if(spinner){ spinner.setAttribute('aria-label',next.loadingAria); }
3987
4048
  progressFollowUp.textContent=next.followUp;
@@ -4036,6 +4097,7 @@ ${customCss}
4036
4097
  startBtn.textContent=${startLabel};
4037
4098
  completeBtn.textContent=${completeLabel};
4038
4099
  submitBtn.textContent=${submitLabel};
4100
+ cancelBtn.textContent=${cancelLabel};
4039
4101
 
4040
4102
  closeEl.onclick=function(){ emitJSON('popup_close'); };
4041
4103
  backBtn.onclick=function(){ if(window.DeepdotsForm && window.DeepdotsForm.back){ window.DeepdotsForm.back(); } };
@@ -4047,6 +4109,8 @@ ${customCss}
4047
4109
  }
4048
4110
  };
4049
4111
  completeBtn.onclick=function(){ emitJSON('popup_close'); };
4112
+ // \xDAnica acci\xF3n del survey que no carga: cierra igual que la X.
4113
+ cancelBtn.onclick=function(){ emitJSON('popup_close'); };
4050
4114
  var surveyCompletedEmitted=false;
4051
4115
  submitBtn.onclick=function(){
4052
4116
  if(surveyCompletedEmitted) return;
@@ -4088,6 +4152,7 @@ ${customCss}
4088
4152
  startBtn.style.display='none';
4089
4153
  submitBtn.style.display='none';
4090
4154
  completeBtn.style.display='none';
4155
+ cancelBtn.style.display='none';
4091
4156
  backBtn.style.width='';
4092
4157
  startBtn.style.width='';
4093
4158
  submitBtn.style.width='';
@@ -4103,6 +4168,8 @@ ${customCss}
4103
4168
  } else if(state==='completed'){
4104
4169
  completeBtn.style.display='inline-flex';
4105
4170
  completeBtn.style.width='100%';
4171
+ } else if(state==='load_error'){
4172
+ cancelBtn.style.display='inline-flex';
4106
4173
  }
4107
4174
  }
4108
4175
 
@@ -4149,6 +4216,26 @@ ${REVEAL_JS}
4149
4216
  }
4150
4217
  setLoading(true);
4151
4218
 
4219
+ // Survey que no ha cargado (406 de /info, sin red, bundle del CDN ca\xEDdo). Antes se emit\xEDa
4220
+ // \`loaded\` y se pintaba un Send sobre un survey vac\xEDo. Espejo de KMP: aviso traducido, la X
4221
+ // sigue, un \xFAnico Cancel, y se revela ya (manda \`ready\`): no hay nada m\xE1s que esperar.
4222
+ // No es interacci\xF3n del usuario: el renderer no lo convierte en popup_clicked.
4223
+ var ddLoadFailed=false;
4224
+ function ddShowLoadError(name, reason){
4225
+ if(ddLoadFailed){ return; }
4226
+ ddLoadFailed=true;
4227
+ var why=(reason && reason.message) ? reason.message : String(reason);
4228
+ emitJSON(name, { message: why });
4229
+ progressEl.style.display='none';
4230
+ formWrapper.style.display='none';
4231
+ errorHint.style.display='none';
4232
+ loadErrorEl.textContent=DD_LABELS_ACTIVE.errorLoad;
4233
+ loadErrorEl.style.display='block';
4234
+ setLoading(false);
4235
+ updateButtons('load_error');
4236
+ ddReveal.reveal();
4237
+ }
4238
+
4152
4239
  var initialized=false;
4153
4240
  function initMF(){
4154
4241
  if(initialized || !window.magicfeedback) return initialized;
@@ -4172,6 +4259,7 @@ ${REVEAL_JS}
4172
4259
  // de native sin esta opci\xF3n la ignoran.
4173
4260
  autofocus:'navigation',
4174
4261
  onLoadedEvent:function(args){
4262
+ if(args && args.error){ ddShowLoadError('error:load', args.error); return; }
4175
4263
  // Idioma en el que native muestra el survey: manda sobre el del init para que el
4176
4264
  // chrome no se quede en ingl\xE9s delante de un survey traducido. Native < 2.3 no lo
4177
4265
  // env\xEDa: entonces el primero de formData.lang (el idioma por defecto).
@@ -4275,13 +4363,13 @@ ${REVEAL_JS}
4275
4363
  updateProgress({ progress:p&&p.progress, total:form.total, completed:false, followup:p&&p.followup });
4276
4364
  emitJSON('back', p);
4277
4365
  }
4278
- }).catch(function(e){ setLoading(false); emit('error:init'); });
4279
- }catch(e){ setLoading(false); emit('error:exception'); }
4366
+ }).catch(function(e){ ddShowLoadError('error:init', e); });
4367
+ }catch(e){ ddShowLoadError('error:exception', e); }
4280
4368
  return true;
4281
4369
  }
4282
4370
  var s=document.createElement('script'); s.src=${JSON.stringify(cdn)}; s.async=true;
4283
4371
  s.onload=function(){ initMF(); };
4284
- s.onerror=function(){ setLoading(false); emit('error:load'); };
4372
+ s.onerror=function(){ ddShowLoadError('error:load', 'survey script failed to load'); };
4285
4373
  document.head.appendChild(s);
4286
4374
  var tries=0, t=setInterval(function(){ if(initMF()||++tries>40){ clearInterval(t); } },150);
4287
4375
  })();
@@ -4400,6 +4488,7 @@ var ReactNativePopupRenderer = class {
4400
4488
  this.onCloseFn?.();
4401
4489
  break;
4402
4490
  default:
4491
+ if (typeof name === "string" && name.startsWith("error:")) this.emitReady(surveyId);
4403
4492
  break;
4404
4493
  }
4405
4494
  }