PageSourceSearch

https://tikfcounter.com/wp-content/uploads/javascript-uploader/client-ip-detector.js?ver=4.8.1_1791592513

js tikfcounter.com collected 2026-10-10 00:35:59 UTC 21,629 bytes, 616 lines download raw bytes

1/**
2 * Global Client IP Detector
3 * Detects real client IPv4 address (not server IP or IPv6)
4 *
5 * Usage: Include this script in your HTML file:
6 * <script src="https://api.theeztools.com/api/js/client-ip-detector.js"></script>
7 *
8 * The script automatically detects IP and makes it available globally
9 * Only our tools API requests (fetch, XHR, jQuery AJAX) get X-Client-IPv4 —
10 * never third-party hosts (e.g. Google Translate), to avoid CORS preflight failures.
11 */
12
13(function () {
14  'use strict';
15
16  // Laravel API host for /api/client-ip/detect only. Never use location.origin here:
17  // on third-party sites (e.g. freedapachecker.io) that would POST to the wrong host → 404.
18  // Optional override: <script>window.TOOLS_API_BASE_URL='https://api.theeztools.com';</script> before this file.
19  const toolsApiBase = (function () {
20    if (typeof window !== 'undefined' && window.TOOLS_API_BASE_URL) {
21      return String(window.TOOLS_API_BASE_URL).replace(/\/+$/, '');
22    }
23    return 'https://api.theeztools.com';
24  })();
25
26  function normalizeRequestUrl(url) {
27    if (typeof url === 'string') return url;
28    if (url && typeof url.url === 'string') return url.url;
29    return '';
30  }
31
32  function getAllowedApiHosts() {
33    const hosts = new Set(['api.theeztools.com', 'tools.xrespond.com']);
34    try {
35      hosts.add(new URL(toolsApiBase).hostname);
36    } catch (e) { /* ignore */ }
37    if (typeof window !== 'undefined' && window.location) {
38      const h = window.location.hostname;
39      if (h === 'localhost' || h === '127.0.0.1') {
40        hosts.add(h);
41      }
42    }
43    return hosts;
44  }
45
46  /** Free IP lookup endpoints — never wait/attach header (needed for detection). */
47  function isIpDetectionSkipUrl(url) {
48    const u = normalizeRequestUrl(url);
49    if (!u) return false;
50    if (u.includes('/api/client-ip/detect')) return true;
51    return u.includes('ipify.org') || u.includes('ifconfig.me') || u.includes('icanhazip.com') ||
52      u.includes('ident.me') || u.includes('ipecho.net') || u.includes('checkip.amazonaws.com') ||
53      u.includes('ipinfo.io') || u.includes('seeip.org') || u.includes('wtfismyip.com') ||
54      u.includes('dnsomatic.com') || u.includes('ip.42.pl') || u.includes('l2.io') ||
55      u.includes('myip.com') || u.includes('ipapi.co') || u.includes('ip-api.com') ||
56      u.includes('ip.sb') || u.includes('anysrc.net');
57  }
58
59  /**
60   * Only attach X-Client-IPv4 to our tools API host.
61   * Custom headers on cross-origin third parties (Translate, etc.) trigger CORS preflight.
62   */
63  function shouldAttachClientIpHeader(url) {
64    const u = normalizeRequestUrl(url);
65    if (!u || isIpDetectionSkipUrl(u)) return false;
66    try {
67      const base = (typeof window !== 'undefined' && window.location && window.location.href)
68        ? window.location.href
69        : toolsApiBase;
70      const resolved = new URL(u, base);
71      return getAllowedApiHosts().has(resolved.hostname);
72    } catch (e) {
73      return false;
74    }
75  }
76
77  /** Current browser page — used as site_url / Detail URL for any embed domain. */
78  function getToolPageUrl() {
79    try {
80      return String((window.location && window.location.href) || '').trim();
81    } catch (e) {
82      return '';
83    }
84  }
85
86  function attachToolPageUrlToHeadersObject(headers) {
87    const pageUrl = getToolPageUrl();
88    if (!pageUrl || !headers || typeof headers !== 'object') return headers;
89    const has = Object.keys(headers).some(function (key) {
90      return String(key).toLowerCase() === 'x-tool-page-url';
91    });
92    if (!has) {
93      headers['X-Tool-Page-Url'] = pageUrl;
94    }
95    return headers;
96  }
97
98  // Free IP APIs list - 15+ different services
99  // Each API will be checked one by one with 5-second timeout
100  const freeIpApis = [
101    // JSON APIs
102    { url: 'https://api.ipify.org?format=json', type: 'json', field: 'ip', name: 'ipify-json' },
103    { url: 'https://api64.ipify.org?format=json', type: 'json', field: 'ip', name: 'ipify64-json' },
104    { url: 'https://api.myip.com', type: 'json', field: 'ip', name: 'myip-json' },
105    { url: 'https://ipapi.co/json', type: 'json', field: 'ip', name: 'ipapi-json' },
106    { url: 'https://ip-api.com/json', type: 'json', field: 'query', name: 'ip-api-json' },
107
108    // Plain text APIs (IPv4 only) - Fastest and most reliable
109    { url: 'https://api.ipify.org', type: 'text', name: 'ipify-text' },
110    { url: 'https://ifconfig.me/ip', type: 'text', name: 'ifconfig-me' },
111    { url: 'https://icanhazip.com', type: 'text', name: 'icanhazip' },
112    { url: 'https://ident.me', type: 'text', name: 'ident-me' },
113    { url: 'https://ipecho.net/plain', type: 'text', name: 'ipecho' },
114    { url: 'https://checkip.amazonaws.com', type: 'text', name: 'amazonaws' },
115    { url: 'https://ipinfo.io/ip', type: 'text', name: 'ipinfo' },
116    { url: 'https://ip.seeip.org', type: 'text', name: 'seeip' },
117    { url: 'https://wtfismyip.com/text', type: 'text', name: 'wtfismyip' },
118    { url: 'https://myip.dnsomatic.com', type: 'text', name: 'dnsomatic' },
119    { url: 'https://ip.42.pl/raw', type: 'text', name: 'ip42' },
120    { url: 'https://l2.io/ip', type: 'text', name: 'l2io' },
121    { url: 'https://ipv4.icanhazip.com', type: 'text', name: 'icanhazip-v4' },
122    { url: 'https://ipv4.ident.me', type: 'text', name: 'ident-v4' },
123    { url: 'https://v4.ident.me', type: 'text', name: 'ident-v4-alt' },
124    { url: 'https://4.ident.me', type: 'text', name: 'ident-4' },
125    { url: 'https://api.ip.sb/ip', type: 'text', name: 'ip-sb' },
126    { url: 'https://ipv4.wtfismyip.com/text', type: 'text', name: 'wtfismyip-v4' },
127    { url: 'https://ip.anysrc.net/plain', type: 'text', name: 'anysrc' },
128  ];
129
130  // IP Detection state
131  let detectedClientIp = null;
132  let detectedIpv4 = null;
133  let ipDetectionInProgress = false;
134  let ipDetectionPromise = null;
135
136  /**
137   * Intercept jQuery AJAX globally (if jQuery is available)
138   * Also handles jQuery loading after this script
139   * Defined early so it can be called from anywhere
140   */
141  function interceptJQueryAjax() {
142    if (typeof jQuery !== 'undefined' && jQuery.ajax) {
143      // Check if already intercepted
144      if (jQuery.ajax._clientIpIntercepted) {
145        return;
146      }
147
148      // Get the current jQuery.ajax (might already be intercepted by global-rate-limit-handler.js)
149      const originalAjax = jQuery.ajax;
150
151      // Check if it's already been intercepted by us
152      if (jQuery.ajax._clientIpIntercepted) {
153        return; // Already intercepted
154      }
155
156      jQuery.ajax = async function (options) {
157        const url = options.url || '';
158
159        // Skip IP-detection URLs and any non-tools-API host (avoids CORS preflight)
160        if (!shouldAttachClientIpHeader(url)) {
161          return originalAjax.call(this, options);
162        }
163
164        // Wait for IP detection before sending request
165        const ip = await waitForIpDetection(10000);
166
167        if (!ip) {
168          console.warn('[Client IP Detector] ⚠️ IP not detected yet, jQuery AJAX request may proceed without X-Client-IPv4 header');
169        }
170
171        // Ensure headers object exists
172        if (!options.headers) {
173          options.headers = {};
174        }
175
176        // Add client-detected IPv4 (this will override any IP from global-rate-limit-handler.js)
177        if (ip) {
178          options.headers['X-Client-IPv4'] = ip;
179        }
180        attachToolPageUrlToHeadersObject(options.headers);
181
182        // Also add to beforeSend hook as backup (runs after headers are set)
183        const originalBeforeSend = options.beforeSend;
184        options.beforeSend = function (xhr, settings) {
185          // Get latest IP (might have been detected after options were set)
186          const latestIp = detectedIpv4 || window.clientDetectedIpv4 || window.detectedClientIpv4 || ip || null;
187          if (latestIp) {
188            try {
189              xhr.setRequestHeader('X-Client-IPv4', latestIp);
190            } catch (e) {
191              // Header might already be set, ignore error
192            }
193          }
194          const pageUrl = getToolPageUrl();
195          if (pageUrl) {
196            try {
197              xhr.setRequestHeader('X-Tool-Page-Url', pageUrl);
198            } catch (e) {
199              // Header might already be set, ignore error
200            }
201          }
202          // Call original beforeSend (might be from global-rate-limit-handler.js or user co
202de)
203          if (originalBeforeSend) {
204            return originalBeforeSend.call(this, xhr, settings);
205          }
206        };
207
208        // Call original ajax (which might be global-rate-limit-handler.js's interception)
209        return originalAjax.call(this, options);
210      };
211
212      // Mark as intercepted
213      jQuery.ajax._clientIpIntercepted = true;
214    }
215  }
216
217  /**
218   * Get IPv4 from free APIs (browser-side - sees user's real IP)
219   * Only returns IPv4, ignores IPv6
220   * Each API has 5-second timeout, checks one by one until IP is found
221   */
222  async function getIpv4FromFreeApis() {
223    const API_TIMEOUT = 5000; // 5 seconds per API
224
225    console.log(`[Client IP Detector] Checking ${freeIpApis.length} free IP APIs (5s timeout each)...`);
226
227    // Check each API one by one with timeout
228    for (let i = 0; i < freeIpApis.length; i++) {
229      const api = freeIpApis[i];
230      const apiName = api.name || `API-${i + 1}`;
231
232      try {
233        console.log(`[Client IP Detector] Trying ${apiName} (${i + 1}/${freeIpApis.length})...`);
234
235        // Create timeout promise
236        const timeoutPromise = new Promise((_, reject) => {
237          setTimeout(() => reject(new Error('Timeout')), API_TIMEOUT);
238        });
239
240        // Create fetch promise
241        const fetchPromise = fetch(api.url, {
242          method: 'GET',
243          headers: {
244            'Accept': api.type === 'json' ? 'application/json' : 'text/plain',
245          },
246        });
247
248        // Race between fetch and timeout
249        const response = await Promise.race([fetchPromise, timeoutPromise]);
250
251        if (response && response.ok) {
252          let ip = null;
253
254          if (api.type === 'json') {
255            const data = await response.json();
256            ip = data[api.field] || null;
257          } else {
258            ip = (await response.text()).trim();
259          }
260
261          // Validate it's a valid IPv4 (NOT IPv6)
262          if (ip && /^(\d{1,3}\.){3}\d{1,3}$/.test(ip)) {
263            // Double check it's not IPv6 mapped IPv4
264            if (!ip.includes(':') && filterIPv4(ip)) {
265              console.log(`[Client IP Detector] ✅ IP found from ${apiName}: ${ip}`);
266              return ip; // Found valid IPv4, stop immediately
267            }
268          }
269        }
270      } catch (error) {
271        // Timeout or other error - try next API
272        if (error.message === 'Timeout') {
273          console.log(`[Client IP Detector] ⏱️ ${apiName} timed out after 5s, trying next...`);
274        } else {
275          console.log(`[Client IP Detector] ❌ ${apiName} failed: ${error.message}, trying next...`);
276        }
277        continue; // Try next API
278      }
279    }
280
281    console.warn(`[Client IP Detector] ⚠️ No IPv4 found from any of ${freeIpApis.length} APIs`);
282    return null; // No IPv4 found from any API
283  }
284
285  /**
286   * Filter to ensure it's IPv4 only
287   */
288  function filterIPv4(ip) {
289    const parts = ip.split('.');
290    if (parts.length !== 4) return false;
291
292    for (const part of parts) {
293      const num = parseInt(part, 10);
294      if (isNaN(num) || num < 0 || num > 255) {
295        return false;
296      }
297    }
298    return true;
299  }
300
301  /**
302   * Detect user's real IPv4 address
303   * ONLY uses free APIs (client-side) - NO backend fallback
304   * Will retry once if first attempt fails
305   */
306  async function detectClientIp() {
307    // Prevent multiple simultaneous detections
308    if (ipDetectionInProgress && ipDetectionPromise) {
309      return ipDetectionPromise;
310    }
311
312    // Return cached IP if already detected
313    if (detectedIpv4) {
314      return detectedIpv4;
315    }
316
317    ipDetectionInProgress = true;
318    ipDetectionPromise = (async () => {
319      try {
320        // Step 1: Try free APIs first (most reliable for client IP)
321        console.log('[Client IP Detector] Detecting IPv4 from free APIs...');
322        const apiIpv4 = await getIpv4FromFreeApis();
323
324        if (apiIpv4) {
325          detectedIpv4 = apiIpv4;
326          detectedClientIp = apiIpv4;
327
328          // Make IP available globally (multiple ways for compatibility)
329          window.clientDetectedIp = detectedClientIp;
330          window.clientDetectedIpv4 = detectedIpv4;
331          window.detectedClientIpv4 = detectedIpv4; // Additional global variable
332
333          console.log('[Client IP Detector] ✅ IPv4 detected from free APIs:', detectedIpv4);
334          console.log('[Client IP Detector] ✅ All future API requests will include X-Client-IPv4 header');
335
336          // Re-intercept jQuery in case it loaded after initial interception
337          if (typeof window !== 'undefined') {
338            setTimeout(() => {
339              if (typeof jQuery !== 'undefined' && jQuery.ajax) {
340                const originalAjax = jQuery.ajax;
341                if (!jQuery.ajax._clientIpIntercepted) {
342                  interceptJQueryAjax();
343                }
344              }
345            }, 100);
346          }
347
348          // Automatically send to service after successful detection
349          sendIpToService().catch(err => {
350            console.warn('[Client IP Detector] Failed to auto-send to service:', err);
351          });
352
353          return detectedIpv4;
354        }
355
356        // No fallback - only use client-side free APIs
357        // If free APIs fail, keep retrying (don't use backend)
358        console.warn('[Client IP Detector] ⚠️ Free APIs failed, retrying...');
359
360        // Retry once more with a short delay
361        await new Promise(resolve => setTimeout(resolve, 500));
362        const retryIpv4 = await getIpv4FromFreeApis();
363
364        if (retryIpv4) {
365          detectedIpv4 = retryIpv4;
366          detectedClientIp = retryIpv4;
367
368          // Make IP available globally (multiple ways for compatibility)
369          window.clientDetectedIp = detectedClientIp;
370          window.clientDetectedIpv4 = detectedIpv4;
371          window.detectedClientIpv4 = detectedIpv4;
372
373          console.log('[Client IP Detector] ✅ IPv4 detected on retry:', detectedIpv4);
374
375          // Re-intercept jQuery in case it loaded after initial interception
376          if (typeof window !== 'undefined') {
377            setTimeout(() => {
378              if (typeof jQuery !== 'undefined' && jQuery.ajax) {
379                if (!jQuery.ajax._clientIpIntercepted) {
380                  interceptJQueryAjax();
381                }
382              }
383            }, 100);
384          }
385
386          // Automatically send to service after successful detection
387          sendIpToService().catch(err => {
388            console.warn('[Client IP Detector] Failed to auto-send to service:', err);
389          });
390
391          return detectedIpv4;
392        }
393
394        console.error('[Client IP Detector] ❌ Failed to detect IPv4 from free APIs - no fallback, will retry on next request');
395        return null;
396      } catch (error) {
397        console.error('[Client IP Detector] ❌ Error detecting IP:', error);
398        return null;
399      } finally {
400        ipDetectionInProgress = false;
401      }
402    })();
403
404    return ipDetectionPromise;
405  }
406
407  /**
408   * Get detected IP (synchronous getter)
409   * Returns cached IPv4 if available, otherwise returns null
410   */
411  function getDetectedIp() {
412    return detectedIpv4;
413  }
414
415  /**
416   * Send detected IP to backend service
417   * This is called automatically after detection
418   * Properly sends IP to service with correct headers
419   */
420  async function sendIpToService() {
421    if (!detectedIpv4) {
422      console.warn('[Client IP Detector] No IP detected yet, cannot send to service');
423      return null;
424    }
425
426    try {
427      console.log('[Client IP Detector] Sending IP to service:', detectedIpv4);
428
429      const response = await fetch(`${toolsApiBase}/api/client-ip/detect`, {
430        method: 'POST',
431        headers: {
432          'Content-Type': 'application/json',
433          'X-Client-IPv4': detectedIpv4, // Send IP in header
434        },
435        body: JSON.stringify({
436          client_ip: detectedIpv4,
437          detected_at: new Date().toISOString()
438        })
439      });
440
441      if (!response.ok) {
442        throw new Error(`HTTP error! status: ${response.status}`);
443      }
444
445      const data = await response.json();
446      console.log('[Client IP Detector] ✅ IP successfully sent to service:', data);
447      return data;
448    } catch (error) {
449      console.error('[Client IP Detector] ❌ Failed to send IP to service:', error);
450      return null;
451    }
452  }
453
454  // Auto-detect IP immediately (don't wait for DOMContentLoaded)
455  // This ensures IP is detected before any API requests are made
456  if (typeof window !== 'undefined') {
457    // Start detection immediately
458    detectClientIp().then(() => {
459      sendIpToService();
460      console.log('[Client IP Detector] ✅ IP detected and ready:', detectedIpv4);
461    }).catch(err => {
462      console.warn('[Client IP Detector] Auto-detection failed:', err);
463    });
464  }
465
466  // Expose functions globally
467  window.clientIpDetector = {
468    detect: detectClientIp,
469    getIp: getDetectedIp,
470    getIpv4: () => detectedIpv4,
471    isDetected: () => detectedIpv4 !== null,
472    sendToService: sendIpToService
473  };
474
475  /**
476   * Wait for IP detection (with timeout)
477   * Returns detected IP or null if timeout
478   */
479  async function waitForIpDetection(timeout = 10000) {
480    if (detectedIpv4) {
481      return detectedIpv4;
482    }
483
484    const startTime = Date.now();
485    while (!detectedIpv4 && (Date.now() - startTime) < timeout) {
486      await new Promise(resolve => setTimeout(resolve, 100));
487    }
488
489    return detectedIpv4;
490  }
491
492  /**
493   * Intercept Fetch API: add X-Client-IPv4 only for our tools API host
494   * WAITS for IP detection before those requests (no fallback)
495   */
496  const originalFetch = window.fetch;
497  window.fetch = async function (...args) {
498    const url = args[0];
499
500    // Skip IP-detection URLs and third-party hosts (Google Translate, etc.)
501    if (!shouldAttachClientIpHeader(url)) {
502      return originalFetch.apply(this, args);
503    }
504
505    // Wait for IP detection before sending request
506    const ip = await waitForIpDetection(10000);
507
508    if (!ip) {
509      console.warn('[Client IP Detector] ⚠️ IP not detected yet, request may proceed without X-Cl
509ient-IPv4 header');
510    }
511
512    // Add client-detected IPv4 to headers
513    const options = args[1] || {};
514    let headers = options.headers;
515
516    // Handle different header types
517    if (headers instanceof Headers) {
518      // Headers object - add to it
519      if (ip) {
520        headers.set('X-Client-IPv4', ip);
521      }
522      const pageUrl = getToolPageUrl();
523      if (pageUrl && !headers.has('X-Tool-Page-Url')) {
524        headers.set('X-Tool-Page-Url', pageUrl);
525      }
526    } else if (typeof headers === 'object' && headers !== null && !Array.isArray(headers)) {
527      // Plain object - add property
528      headers = { ...headers };
529      if (ip) {
530        headers['X-Client-IPv4'] = ip;
531      }
532      attachToolPageUrlToHeadersObject(headers);
533    } else {
534      // No headers or other type - create new object
535      headers = ip ? { 'X-Client-IPv4': ip } : {};
536      attachToolPageUrlToHeadersObject(headers);
537    }
538
539    // Update options with new headers
540    const newOptions = {
541      ...options,
542      headers: headers
543    };
544
545    return originalFetch.apply(this, [args[0], newOptions]);
546  };
547
548  /**
549   * Intercept XMLHttpRequest to add X-Client-IPv4 header
550   */
551  const originalXHROpen = XMLHttpRequest.prototype.open;
552  const originalXHRSend = XMLHttpRequest.prototype.send;
553
554  XMLHttpRequest.prototype.open = function (method, url, ...args) {
555    this._url = url;
556    // Store detected IPv4 for this request
557    this._clientIpv4 = detectedIpv4;
558    return originalXHROpen.apply(this, [method, url, ...args]);
559  };
560
561  XMLHttpRequest.prototype.send = async function (...args) {
562    const url = this._url || '';
563
564    // Skip IP-detection URLs and third-party hosts (Google Translate, etc.)
565    if (!shouldAttachClientIpHeader(url)) {
566      return originalXHRSend.apply(this, args);
567    }
568
569    // Wait for IP detection before sending request
570    const ip = await waitForIpDetection(10000);
571
572    if (!ip) {
573      console.warn('[Client IP Detector] ⚠️ IP not detected yet, XHR request may proceed without X-Client-IPv4 header');
574    }
575
576    // Add client-detected IPv4 header to XHR request
577    if (ip) {
578      try {
579        this.setRequestHeader('X-Client-IPv4', ip);
580      } catch (e) {
581        // Header might already be set, ignore error
582      }
583    }
584    const pageUrl = getToolPageUrl();
585    if (pageUrl) {
586      try {
587        this.setRequestHeader('X-Tool-Page-Url', pageUrl);
588      } catch (e) {
589        // Header might already be set, ignore error
590      }
591    }
592
593    return originalXHRSend.apply(this, args);
594  };
595
596  // Intercept immediately if jQuery is already loaded
597  interceptJQueryAjax();
598
599  // Also intercept when jQuery loads (if it loads after this script)
600  if (typeof window !== 'undefined') {
601    // Try to intercept when jQuery is available
602    const checkJQuery = setInterval(() => {
603      if (typeof jQuery !== 'undefined' && jQuery.ajax) {
604        interceptJQueryAjax();
605        clearInterval(checkJQuery);
606      }
607    }, 100);
608
609    // Stop checking after 10 seconds
610    setTimeout(() => clearInterval(checkJQuery), 10000);
611  }
612
613  console.log('✅ Client IP Detector loaded - detecting real client IPv4...');
614  console.log('✅ Tools API requests will include X-Client-IPv4 + X-Tool-Page-Url (third-party hosts skipped)');
615})();
616

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.