(function (window, document) { 'use strict'; var nativeFetch = window.fetch.bind(window); function toast(text) { var old = document.querySelector('.ajax-error-toast'); if (old) old.remove(); var element = document.createElement('div'); element.className = 'ajax-error-toast'; element.setAttribute('role', 'alert'); element.textContent = text; document.body.appendChild(element); window.setTimeout(function () { element.remove(); }, 7000); } function addCsrf(formData) { var token = window.APP_CSRF; if (token && token.name && !formData.has(token.name)) { formData.append(token.name, token.value); } return formData; } function secureFetch(url, options) { options = Object.assign({}, options || {}); var method = String(options.method || 'GET').toUpperCase(); var token = window.APP_CSRF; var target; try { target = new URL(url, window.location.href); } catch (_error) { target = null; } if (target && target.origin === window.location.origin && !/^(GET|HEAD|OPTIONS)$/.test(method) && token && token.name) { if (options.body instanceof FormData) { addCsrf(options.body); } else if (options.body instanceof URLSearchParams) { options.body = new URLSearchParams(options.body); if (!options.body.has(token.name)) options.body.append(token.name, token.value); } } return nativeFetch(url, options); } window.fetch = secureFetch; async function request(url, options) { options = options || {}; options.headers = Object.assign({ Accept: 'application/json', 'X-Requested-With': 'XMLHttpRequest' }, options.headers || {}); if (options.body instanceof FormData) addCsrf(options.body); var response; try { response = await window.fetch(url, options); } catch (error) { throw new Error('Tidak dapat terhubung ke server. Periksa jaringan atau Apache.'); } var contentType = response.headers.get('content-type') || ''; if (contentType.indexOf('application/json') === -1) { var raw = await response.text(); if (response.redirected || /<(?:html|!doctype)/i.test(raw)) { throw new Error('Sesi berakhir atau server mengirim halaman error. Silakan login ulang.'); } throw new Error(raw.replace(/<[^>]+>/g, ' ').replace(/\s+/g, ' ').trim().slice(0, 240) || 'Respons server tidak valid.'); } var payload = await response.json(); if (!response.ok) throw new Error(payload.message || ('Permintaan gagal (HTTP ' + response.status + ').')); return payload; } window.App = Object.assign(window.App || {}, { request: request, addCsrf: addCsrf, showError: toast }); if (window.jQuery) { window.jQuery.ajaxSetup({headers: {'X-Requested-With': 'XMLHttpRequest'}}); window.jQuery.ajaxPrefilter(function (options, originalOptions) { var method = String(options.type || options.method || 'GET').toUpperCase(); var token = window.APP_CSRF; if (/^(GET|HEAD|OPTIONS)$/.test(method) || !token || !token.name) return; if (options.data instanceof FormData) { if (!options.data.has(token.name)) options.data.append(token.name, token.value); return; } if (typeof options.data === 'string') { var encodedName = encodeURIComponent(token.name); if (options.data.indexOf(encodedName + '=') === -1) { options.data += (options.data ? '&' : '') + encodedName + '=' + encodeURIComponent(token.value); } return; } options.data = window.jQuery.extend({}, originalOptions.data || options.data || {}); if (typeof options.data[token.name] === 'undefined') options.data[token.name] = token.value; }); window.jQuery(document).ajaxError(function (_event, xhr, _settings, error) { var message = 'Pemanggilan data gagal'; if (xhr.status === 401 || xhr.status === 403) message = 'Sesi atau hak akses tidak valid. Silakan login ulang.'; else if (xhr.status) message += ' (HTTP ' + xhr.status + ').'; else message += '. Periksa koneksi atau Apache.'; toast(error && error !== 'error' ? error : message); }); } }(window, document));