1/** 2 * Ask AI â sidebar helper for Payoneer developer topics. 3 * Uses a local curated KB (no third-party AI, no API keys in the page). 4 * Optional: set window.PAYO_ASK_AI_ENDPOINT to a same-origin HTTPS endpoint that 5 * accepts POST JSON { "question": "..." } and returns JSON { "answer": "...", "sources": [{ "label", "href" }] }. 6 */ 7(function () { 8 "use strict"; 9 10 /** Curated Payoneer developer knowledge (expand as needed). */ 11 var KB = [ 12 { 13 id: "overview", 14 title: "Payoneer developer platform", 15 keywords: ["platform", "developer", "api", "integrate", "overview", "start", "begin"], 16 answer: 17 "The Payoneer Platform exposes REST APIs so you can embed financial flows (balances, payouts, billing, cards, etc.) in your product. Use the official API reference for endpoints, request bodies, and auth. Sandbox base URLs let you test before production.", 18 sources: [ 19 { label: "API reference", href: "https://developer.payoneer.com/docs/payoneer-api-reference" }, 20 { label: "Platform (this site)", href: "./developers-docs.html" }, 21 ], 22 }, 23 { 24 id: "auth", 25 title: "Authentication and tokens", 26 keywords: ["oauth", "token", "bearer", "authorization", "auth", "client", "secret", "credential"], 27 answer: 28 "API calls use HTTPS and typically a Bearer access token in the Authorization header. Obtain tokens through Payoneerâs OAuth / consent flows as documented; never expose client secrets or tokens in frontend bundles or public repos â keep them on your server.", 29 sources: [{ label: "API reference â auth", href: "https://developer.payoneer.com/docs/payoneer-api-reference" }], 30 }, 31 { 32 id: "sandbox", 33 title: "Sandbox vs production", 34 keywords: ["sandbox", "production", "test", "api.sandbox", "environment"], 35 answer: 36 "Sandbox hostnames (e.g. api.sandbox.payoneer.com) are for integration testing. Use production hosts only when your integration is complete and approved. Always confirm exact base URLs in the current API reference.", 37 sources: [{ label: "API reference", href: "https://developer.payoneer.com/docs/payoneer-api-reference" }], 38 }, 39 { 40 id: "eligibility", 41 title: "Payment eligibility", 42 keywords: ["eligibility", "eligible", "minimum", "maximum", "amount", "balance", "limits"], 43 answer: 44 "Before paying another Payoneer account, call the eligibility API to confirm the user may send that payment and to read min/max amounts per balance. Do not hardcode limits â use the API response.", 45 sources: [{ label: "API reference", href: "https://developer.payoneer.com/docs/payoneer-api-reference" }], 46 }, 47 { 48 id: "charge-account", 49 title: "POST .../precalc (accountholder charge)", 50 keywords: ["charge", "precalc", "debit", "fee", "customer", "service fee", "subscription", "collect", "platform", "balance", "mfa", "commit"], 51 answer: 52 "Accountholder-initiated charge: retrieve balances, user selects balance, POST precalc on /v4/accounts/{account_id}/balances/{balance_id}/precalc, user approves fees, PUT commit. Commit may return challenge_required with a challenge URL and session for MFA. See the Pay with Payoneer (charge account) guide and Global financial services on banking portals for details.", 53 sources: [ 54 { label: "Pay with Payoneer (charge account)", href: "./ap-charge-account.html#charge-user-balance-flow" }, 55 { label: "Official â banking portals", href: "https://developer.payoneer.com/docs/global-financial-services-on-banking-portals.html#/dmgnep147rctz-accountholder-initiated-charge-debit" }, 56 ], 57 }, 58 { 59 id: "send-bank", 60 title: "POST payments/send (bank account)", 61 keywords: ["bank", "wire", "swift", "iban", "routing", "ach", "local bank", "bank account", "recipient bank"], 62 answer: 63 "For payouts to a saved recipient bank account, use the same POST /v4/accounts/{account_id}/balances/{balance_id}/payments/send path. Identify the destination with to.type = bank_account and to.id = the Payoneer bank-account identifier (per your programâs registration flow). Then consent, PUT commit with commit_id, and handle challenge_required like other send flows.", 64 sources: [ 65 { label: "Bank payout guide", href: "./ap-payments-to-recipient-bank-account.html#step-b-send" }, 66 { label: "Official docs", href: "https://developer.payoneer.com/docs/diverse-financial-services-on-digital-platforms.html#/9e161fb566531-making-payments-to-recipient-s-bank-accounts" }, 67 ], 68 }, 69 { 70 id: "send", 71 title: "POST payments/pay_to_payoneer_account",
72 keywords: ["send", "post", "payment", "pay", "balance", "email", "recipient", "payee", "pay_to_payoneer_account", "purpose_of_payment"], 73 answer: 74 "Initiate a payment to another Payoneer account with POST /v4/accounts/{account_id}/balances/{balance_id}/payments/pay_to_payoneer_account. Put the recipient in to.type = email and to.id = their email; include purpose_of_payment when required. The response includes commit_id, fees, and amounts â use them for confirmation UI and the commit step.", 75 sources: [ 76 { label: "This guide", href: "./ap-payments-between-payoneer-accounts.html#step-b-send" }, 77 { label: "Official docs", href: "https://developer.payoneer.com/docs/diverse-financial-services-on-digital-platforms.html#/73e805060ae51-making-payments-between-payoneer-accounts" }, 78 ], 79 }, 80 { 81 id: "commit", 82 title: "Commit payment (PUT)", 83 keywords: ["commit", "put", "confirm", "approve", "charge"], 84 answer: 85 "After the user approves fees and details, confirm with PUT /v4/accounts/{account_id}/payments/{commit_id} using the commit_id from the send response. Without commit, the payment is not finalized.", 86 sources: [{ label: "Commit step", href: "./ap-payments-between-payoneer-accounts.html#step-d-commit" }], 87 }, 88 { 89 id: "consent", 90 title: "User confirmation", 91 keywords: ["consent", "confirm", "approve", "fees", "user", "customer", "ui"], 92 answer: 93 "You must show the user transaction details and fees from the send response and obtain confirmation before calling commit. The user must confirm agreement to charging their Payoneer balance.", 94 sources: [{ label: "Confirmation step", href: "./ap-payments-between-payoneer-accounts.html#step-c-consent" }], 95 }, 96 { 97 id: "mfa", 98 title: "challenge_required and MFA", 99 keywords: ["403", "challenge", "challenge_reference", "mfa", "forbidden", "authentication", "auth.payoneer", "session"], 100 answer: 101 "If commit returns 403 with error challenge_required, the user must complete MFA. The JSON includes challenge.url, session_id, and challenge_reference â send the user to the URL, then retry commit after authentication. Handle expiry via challenge.expires_at.", 102 sources: [{ label: "Challenge section", href: "./ap-payments-between-payoneer-accounts.html#challenge-required" }], 103 }, 104 { 105 id: "accounts-payable", 106 title: "Accounts Payable", 107 keywords: ["payable", "supplier", "pay", "accounts payable", "ap"], 108 answer: 109 "Accounts Payable covers outbound flows such as paying other Payoneer accounts, bank payouts, and charging balances, depending on your program. Pick the product that matches your use case in the Platform overview.", 110 sources: [{ label: "Accounts Payable", href: "./accounts-payable.html" }], 111 }, 112 { 113 id: "accounts-receivable", 114 title: "Accounts Receivable", 115 keywords: ["receivable", "receive", "incoming", "withdraw", "ar", "billing"], 116 answer: 117 "Accounts Receivable focuses on receiving funds and options like withdrawing to bank. See Billing services and Withdraw to bank guides on the Accounts Receivable page, plus the API reference.", 118 sources: [{ label: "Accounts Receivable", href: "./accounts-receivable.html" }], 119 }, 120 { 121 id: "withdraw-to-bank", 122 title: "Withdraw to bank (balance â bank account)", 123 keywords: ["withdraw", "withdrawal", "eligibility", "bank_id", "bank account", "balance", "commit", "mfa", "ar", "receivable"], 124 answer: 125 "Withdraw to bank: GET eligibility/withdraw for payout methods and bank_id, GET balances for balance IDs, then POST .../payments/withdraw with to.type = bank_id and the bank id from eligibility. Show fees/FX for user confirmation, PUT commit with commit_id, and handle challenge_required on commit if needed. See the API reference for exact schemas.", 126 sources: [ 127 { label: "Withdraw to bank guide", href: "./withdraw-to-bank.html#overview" }, 128 { label: "Official â withdraw from balance", href: "https://developer.payoneer.com/docs/diverse-financial-services-on-digital-platforms.html#/2987be6d6b91b-withdraw-funds-from-user-s-account-balance" }, 129 ], 130 }, 131 { 132 id: "billing-services", 133 title: "Billing services (opt-in + payment requests)", 134 keywords: ["billing", "payment request", "opt-in", "invoice", "request payment", "billing services", "opa", "core country", "optin"], 135 answer: 136 "Use payment_request/optin to start opt-in and check eligibility; surface statuses (e.g. NOT_STARTED, IN_PROCESS, COMPLETED, NOT_ELIGIBLE) on your customer. After Payoneer completes review, use Create Payment Request for an OPA link; payer fields can be pre-filled in the API body. NonâCore countries may require $5k volumeâsee the guide.", 137 sources: [ 138 { label: "Billing services guide", href: "./ar-billing-services.html#opt-in-billing-services" }, 139 { label: "Opt-in (official)", href: "https://developer.payoneer.com/docs/diverse-financial-services-on-digital-platforms.html#/f5ol55uv2y8vn-opt-in-to-billing-services" }, 140 { label: "Payment requests (official)", href: "https://developer.payoneer.com/docs/diverse-financial-services-on-digital-platforms.html#/65p4dh2ff8wk3-create-payment-requests" }, 141 ], 142 }, 143 { 144 id: "gca", 145 title: "Receiving Accounts", 146 keywords: ["gca", "currency", "balance", "receiving", "iban", "account"], 147 answer: 148 "Global Currency Accounts let account holders hold and manage balances in multiple currencies. APIs can expose balances, transactions, and receiving accounts â see the product page and API reference.", 149 sources: [ 150 { label: "Receiving Accounts", href: "./receiving-accounts.html" }, 151 { label: "Create receiving accounts", href: "./receiving-accounts.html#create" }, 152 { label: "Get receiving accounts", href: "./receiving-accounts.html#get" }, 153 ], 154 }, 155 { 156 id: "gca-create-receiving", 157 title: "Create receiving accounts (POST receiving_account)", 158 keywords: ["receiving account", "receiving_account", "create receiving", "iban", "local bank", "currency account", "wire", "bic"], 159 answer: 160 "POST /v4/accounts/{account_id}/receiving_account with a JSON body such as {\"currency\":\"EUR\"} (see API reference for supported fields). Use the bearer token and account_id from your consent flow. The response includes result.id, currency, status, and a details array of name/value pairs for bank instructions.", 161 sources: [ 162 { label: "Create receiving accounts guide", href: "./receiving-accounts.html#create" }, 163 { label: "API reference", href: "https://developer.payoneer.com/docs/payoneer-api-reference" }, 164 ], 165 }, 166 { 167 id: "gca-get-receiving", 168 title: "Get receiving accounts (GET receiving_accounts)", 169 keywords: ["get receiving", "receiving_accounts", "list receiving", "all receiving accounts", "account_id"], 170 answer: 171 "GET /v4/accounts/{account_id}/receiving_accounts returns result.items (each with id, currency, status, details.items name/value pairs) and result.total. Use the bearer token from your consent flow. See the API reference for pagination or filters if your program supports them.", 172 sources: [ 173 { label: "Get receiving accounts guide", href: "./receiving-accounts.html#get" }, 174 { label: "API reference", href: "https://developer.payoneer.com/docs/payoneer-api-reference" }, 175 ], 176 }, 177 { 178 id: "mass-payout", 179 title: "Mass Payout", 180 keywords: ["mass", "payout", "batch", "bulk"], 181 answer: 182 "Mass Payout is for programmatic payouts at scale. Use the documented batch or payout APIs for your integration; details vary by program â confirm in the API reference.", 183 sources: [ 184 { label: "Mass Payout", href: "./mass-payout.html" }, 185 { label: "Payee onboarding", href: "./mass-payee-onboarding.html#overview" }, 186 { label: "Submit mass payout", href: "./mass-submit-payout.html#overview" }, 187 { label: "China SAFE", href: "./mass-payout-china-safe.html#overview" }, 188 ], 189 }, 190 { 191 id: "mass-china-safe", 192 title: "Mass Payout with China SAFE (API & SFTP)", 193 keywords: ["china safe", "SAFE", "夿±", "mass payout china", "sftp", "order reporting", "v4-china-safe", "orders_report", "local_ftp", "safe-data"], 194 answer: 195 "POST .../masspayouts with Payments including orders_report (merchant/store; orders.type local_ftp + path to SFTP JSON, or use POST .../payouts/{client_reference_id}/safe-data with orders.type data and items[]). Sandbox: api.sandbox.payoneer.com. Full spec: v4-china-safe guide.", 196 sources: [ 197 { label: "China SAFE guide (local)", href: "./mass-payout-china-safe.html#overview" }, 198 { label: "Official v4-china-safe", href: "https://developer.payoneer.com/docs/v4-china-safe.html#/8e768e6831f6c-overview" }, 199 { label: "API reference", href: "https://developer.payoneer.com/docs/payoneer-api-reference" }, 200 ], 201 }, 202 { 203 id: "mass-submit-payout", 204 title: "Submit mass payout (API & Admin Console)", 205 keywords: ["mass payout", "masspayouts", "submit mass", "500 payouts", "payments array", "admin console", "mass payments", "csv", "upload"], 206 answer: 207 "POST /v4/programs/{program_id}/masspayouts with a Payments array (payee_id, amount, currency, description, client_reference_id). Deposit funds first; insufficient balance means payouts won't run. Or use Admin Console Mass Payments to upload CSV/XLS/XLSX/XML. Transfers are asynchronous; dual approval may require Integration team setup.", 208 sources: [ 209 { label: "Submit mass payout guide", href: "./mass-submit-payout.html#overview" }, 210 { label: "API reference", href: "https://developer.payoneer.com/docs/payoneer-api-reference" }, 211 ], 212 }, 213 { 214 id: "mass-payee-onboarding", 215 title: "Payee onboarding (Create Registration Link)", 216 keywords: ["payee", "onboarding", "registration link", "registration-link", "payee_id", "already_have_an_account", "pre-populate", "prepopulate", "lock_type"], 217 answer: 218 "POST /v4/programs/{program_id}/payees/registration-link with your payee_id and optional payee profile to pre-fill registration. The response includes a redirect URL. Use already_have_an_account=true for existing Payoneer users (that URL must not be loaded in an iframe). For Active payees, use webhooks (Admin Console URL) and/or GET .../payees/{ClientPayeeID}/status with your app token. See the guide and API reference.", 219 sources: [ 220 { label: "Payee onboarding guide", href: "./mass-payee-onboarding.html#overview" }, 221 { label: "API reference", href: "https://developer.payoneer.com/docs/payoneer-api-reference" }, 222 ], 223 }, 224 { 225 id: "white-label", 226 title: "White Label", 227 keywords: ["white", "label", "brand", "customize"], 228 answer: 229 "White Label solutions let partners present Payoneer capabilities under their own brand. Integration patterns are described
229in the White Label product documentation and API reference.", 230 sources: [ 231 { label: "White Label", href: "./white-label.html" }, 232 { label: "Registration & Payouts", href: "./white-label-registration-payouts.html#overview" }, 233 { label: "Register payee with token", href: "./white-label-register-payee-with-token.html#overview" }, 234 { label: "White Label Payouts", href: "./white-label-payouts.html#overview" }, 235 ], 236 }, 237 { 238 id: "white-label-payouts", 239 title: "White Label Payouts", 240 keywords: ["white label payouts", "one step payments", "single step payments", "masspayouts", "500 payouts", "banking registration fields", "registration-fields", "payout_method_details", "Account Funded webhook", "webhook notifications", "cancel payment webhook", "Payment Loaded webhook", "KYC webhook", "Soft No Go", "batch payout", "wl payout v4"], 241 answer: 242 "One Step / Single Step Payments: one API call with beneficiary KYC + bank details; optional China SAFE orders report. masspayouts accepts up to 500 instructions; fund the program account first; Account Funded webhook; deferred validation; separate webhooks per client_reference_id; subscribe to cancel/failure webhooks (Callback Notifications). Query registration-fields / Banking registration fields before bank details. Local page: batch interfaces schematic, example JSON, webhook flowchart.", 243 sources: [ 244 { label: "White Label Payouts guide", href: "./white-label-payouts.html#overview" }, 245 { label: "Batch payout instruction", href: "./white-label-payouts.html#batch-payout-instruction" }, 246 { label: "Official getting started", href: "https://developer.payoneer.com/docs/white-label-payout-getting-started-v4.html" }, 247 { label: "API reference", href: "https://developer.payoneer.com/docs/payoneer-api-reference" }, 248 ], 249 }, 250 { 251 id: "white-label-registration-payouts", 252 title: "White Label Registration & Payouts", 253 keywords: ["white label registration", "register payee", "payout funding", "payout instructions", "wl v4", "getting started"], 254 answer: 255 "Flow: get register-payee format from Payoneer â collect payee bank details on your site â register payee with Payoneer â payout funding â payout instructions â Payoneer credits payees. See getting started on developer.payoneer.com (white-label-registration-payouts-v4-getting-started) and the local flowchart page.", 256 sources: [ 257 { label: "Registration & Payouts guide", href: "./white-label-registration-payouts.html#overview" }, 258 { label: "Official getting started", href: "https://developer.payoneer.com/docs/white-label-registration-payouts-v4-getting-started.html" }, 259 { label: "API reference", href: "https://developer.payoneer.com/docs/payoneer-api-reference" }, 260 ], 261 }, 262 { 263 id: "white-label-register-payee-with-token", 264 title: "Register payee with token", 265 keywords: [ 266 "register payee with token", 267 "registration with tokens", 268 "register-payee-with-token", 269 "payee_id", 270 "wl token", 271 "white label token", 272 "kyc", 273 "bank transfer registration", 274 ], 275 answer: 276 "POST /v4/programs/{program_id}/payees/register-payee-with-token: pass your payee_id and payee/payout_method payload for KYC and bank-transfer routing. Response includes a token to redirect the user to Payoneer's page to complete bank details. Full schemas and redirect handling: getting started (white-label-registration-with-tokens-v4) and API reference.", 277 sources: [ 278 { label: "Local guide", href: "./white-label-register-payee-with-token.html#overview" }, 279 { label: "Official getting started", href: "https://developer.payoneer.com/docs/white-label-registration-with-tokens-v4-getting-started.html" }, 280 { label: "API reference", href: "https://developer.payoneer.com/docs/payoneer-api-reference" }, 281 ], 282 }, 283 { 284 id: "cards", 285 title: "Cards", 286 keywords: [ 287 "card", 288 "cards", 289 "plastic", 290 "virtual", 291 "order cards", 292 "primary card", 293 "linked card", 294 "order primary card", 295 "get order primary card eligibility", 296 "order linked card", 297 "activate card",
298 "get card information", 299 "get card details", 300 "list all cards", 301 "get sensitive card data", 302 "view pin", 303 "get card transactions", 304 "update card info", 305 "update card status", 306 "lost stolen cancelled", 307 "update pin", 308 "spend control", 309 "reset spend control", 310 "card nickname", 311 ], 312 answer: 313 "Order a primary card (eligibility first: get order primary card eligibility for currency, card plans, shipping). Order linked cards only after a primary card exists in that currency: Get Card Details, Activate Card if needed, Order linked card eligibility, then Order linked card for virtual linked cards. For read APIs: Get Card Details (non-sensitive), List All Cards (filterable), Get Sensitive Card Data (full PAN/CVV), View PIN, Get Card Transactions in a time range. For updates: Update Card Status (Lost, Stolen, or Cancelled only), Update PIN, Update Card Information (nickname only), Set Spend Control, Reset Spend Control. See local pages and the API reference.", 314 sources: [ 315 { label: "Order cards", href: "./order-cards.html#overview" }, 316 { label: "Get card information", href: "./get-card-information.html#overview" }, 317 { label: "Update card info", href: "./update-card-info.html#overview" }, 318 { label: "Cards hub", href: "./cards.html" }, 319 { label: "API reference", href: "https://developer.payoneer.com/docs/payoneer-api-reference" }, 320 ], 321 }, 322 { 323 id: "capital-advance", 324 title: "Capital Advance", 325 keywords: [ 326 "capital advance", 327 "get capital advance offers", 328 "capital advance offers", 329 "early payment", 330 "working capital", 331 ], 332 answer: 333 "GET /v4/accounts/{account_id}/capital-advance/offers returns available offers (items with marketplace, store, amount, fee, settlement_plan, url, etc.). Present offers on your platform and use each itemâs url for the customer to claim. See the local Get capital advance offers page and the API reference.", 334 sources: [ 335 { label: "Capital Advance", href: "./get-capital-advance-offers.html#overview" }, 336 { label: "API reference", href: "https://developer.payoneer.com/docs/payoneer-api-reference" }, 337 ], 338 }, 339 { 340 id: "webhooks", 341 title: "Callbacks and status", 342 keywords: ["webhook", "callback", "status", "notification", "event"], 343 answer: 344 "For async operations, use the mechanisms documented for your program (webhooks, polling, or status APIs). Always verify signatures and treat payloads as untrusted until validated server-side.", 345 sources: [{ label: "API reference", href: "https://developer.payoneer.com/docs/payoneer-api-reference" }], 346 }, 347 ]; 348 349 function tokenize(q) { 350 return String(q || "") 351 .toLowerCase() 352 .replace(/[^a-z0-9\s]/g, " ") 353 .split(/\s+/) 354 .filter(function (w) { 355 return w.length > 1; 356 }); 357 } 358 359 function scoreEntry(words, entry) { 360 if (!words.length) return 0; 361 var title = (entry.title || "").toLowerCase(); 362 var ans = (entry.answer || "").toLowerCase(); 363 var kws = entry.keywords || []; 364 var s = 0; 365 for (var i = 0; i < words.length; i++) { 366 var w = words[i]; 367 if (kws.indexOf(w) !== -1) s += 5; 368 else { 369 for (var j = 0; j < kws.length; j++) { 370 if (kws[j].indexOf(w) !== -1 || w.indexOf(kws[j]) !== -1) { 371 s += 3; 372 break; 373 } 374 } 375 } 376 if (title.indexOf(w) !== -1) s += 2; 377 if (ans.indexOf(w) !== -1) s += 1; 378 } 379 return s; 380 } 381 382 function escapeHtml(s) { 383 return String(s) 384 .replace(/&/g, "&") 385 .replace(/</g, "<") 386 .replace(/>/g, ">") 387 .replace(/"/g, """); 388 } 389
390 function renderSources(sources) { 391 if (!sources || !sources.length) return ""; 392 var parts = ['<p class="ask-ai-sources-title mb-1">References</p><ul class="ask-ai-sources list-unstyled mb-0">']; 393 for (var i = 0; i < sources.length; i++) { 394 var src = sources[i]; 395 parts.push( 396 '<li><a href="' + 397 escapeHtml(src.href) + 398 '" target="_blank" rel="noopener noreferrer">' + 399 escapeHtml(src.label) + 400 "</a></li>" 401 ); 402 } 403 parts.push("</ul>"); 404 return parts.join(""); 405 } 406 407 function rankEntries(query) { 408 var words = tokenize(query); 409 var ranked = []; 410 for (var i = 0; i < KB.length; i++) { 411 var sc = scoreEntry(words, KB[i]); 412 if (sc > 0) ranked.push({ entry: KB[i], score: sc }); 413 } 414 ranked.sort(function (a, b) { 415 return b.score - a.score; 416 }); 417 return { words: words, ranked: ranked }; 418 } 419 420 function renderLocalAnswer(query) { 421 var r = rankEntries(query); 422 if (!r.words.length) { 423 return { 424 html: 425 '<p class="ask-ai-muted mb-0">Type a question about Payoneer developer APIs, authentication, sandbox, or payments flows.</p>', 426 }; 427 } 428 if (!r.ranked.length || r.ranked[0].score < 2) { 429 return { 430 html: 431 '<p class="mb-2">No strong match in this offline knowledge base. Try keywords like <strong>send</strong>, <strong>commit</strong>, <strong>eligibility</strong>, <strong>MFA</strong>, or open the full <a href="https://developer.payoneer.com/docs/payoneer-api-reference" target="_blank" rel="noopener">API reference</a>.</p>' + 432 '<p class="ask-ai-muted small mb-0">Tip: questions stay in your browser â nothing is sent to Payoneer unless you configure an optional backend endpoint.</p>', 433 }; 434 } 435 var top = r.ranked[0].entry; 436 var html = [ 437 '<p class="ask-ai-match-title mb-1">' + escapeHtml(top.title) + "</p>", 438 '<p class="mb-2">' + escapeHtml(top.answer) + "</p>", 439 renderSources(top.sources), 440 ]; 441 if (r.ranked.length > 1 && r.ranked[1].score >= 2) { 442 html.push('<p class="ask-ai-related-title mt-2 mb-1">Related</p><ul class="ask-ai-related small mb-0">'); 443 for (var k = 1; k < Math.min(4, r.ranked.length); k++) { 444 var e = r.ranked[k].entry; 445 html.push("<li>" + escapeHtml(e.title) + "</li>"); 446 } 447 html.push("</ul>"); 448 } 449 return { html: html.join("") }; 450 } 451 452 function tryRemote(question) { 453 var endpoint = typeof window !== "undefined" && window.PAYO_ASK_AI_ENDPOINT; 454 if (!endpoint || typeof fetch !== "function") return Promise.resolve(null); 455 return fetch(endpoint, { 456 method: "POST", 457 headers: { "Content-Type": "application/json" }, 458 body: JSON.stringify({ question: question }), 459 credentials: "same-origin", 460 }) 461 .then(function (res) { 462 if (!res.ok) return null; 463 return res.json(); 464 }) 465 .catch(function () { 466 return null; 467 }); 468 } 469 470 function renderRemote(data) { 471 if (!data || !data.answer) return ""; 472 var html = ['<p class="mb-2">' + escapeHtml(String(data.answer)) + "</p>"]; 473 if (data.sources && data.sources.length) { 474 html.push(renderSources(data.sources)); 475 } 476 return html.join(""); 477 } 478 479 function hideAskAiResponse(root) { 480 var out = root.querySelector(".ask-ai-response"); 481 if (!out || out.hidden) return; 482 out.hidden = true; 483 out.innerHTML = ""; 484 out.setAttribute("aria-busy", "false"); 485 } 486 487 function bindRoot(root) { 488 var form = root.querySelector(".ask-ai-form"); 489 var input = root.querySelector(".ask-ai-input"); 490 var out = root.querySelector(".ask-ai-response"); 491 if (!form || !input || !out) return; 492 493 function setLoading(on) { 494 out.hidden = false; 495 out.innerHTML = on 496 ? '<p class="ask-ai-muted mb-0">Searchingâ¦</p>' 497 : ""; 498 out.setAttribute("aria-busy", on ? "true" : "false"); 499 } 500 501 function showHtml(html) { 502 out.hidden = false; 503 out.innerHTML = html; 504 out.setAttribute("aria-busy", "false"); 505 } 506 507 form.addEventListener("submit", function (e) { 508 e.preventDefault(); 509 var q = (input.value || "").trim();
510 setLoading(true); 511 512 tryRemote(q).then(function (remote) { 513 if (remote && remote.answer) { 514 var rh = renderRemote(remote); 515 if (rh) { 516 showHtml('<div class="ask-ai-block ask-ai-block--remote">' + rh + "</div>"); 517 return; 518 } 519 } 520 var local = renderLocalAnswer(q); 521 showHtml('<div class="ask-ai-block ask-ai-block--local">' + local.html + "</div>"); 522 }); 523 }); 524 } 525 526 function init() { 527 var roots = document.querySelectorAll("[data-ask-ai-root]"); 528 if (!roots.length) return; 529 530 document.addEventListener("click", function (e) { 531 for (var i = 0; i < roots.length; i++) { 532 if (!roots[i].contains(e.target)) { 533 hideAskAiResponse(roots[i]); 534 } 535 } 536 }); 537 538 document.addEventListener("keydown", function (e) { 539 if (e.key !== "Escape") return; 540 for (var j = 0; j < roots.length; j++) { 541 hideAskAiResponse(roots[j]); 542 } 543 }); 544 545 for (var k = 0; k < roots.length; k++) { 546 bindRoot(roots[k]); 547 } 548 } 549 550 if (document.readyState === "loading") { 551 document.addEventListener("DOMContentLoaded", init); 552 } else { 553 init(); 554 } 555})();
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.