https://tategu-oonami.jp/wp-ont/wp-content/plugins/form-nest/assets/js/shared/utilities.js?ver=1.0.9
1/** 2 * WP Advanced Form - Shared Utility Functions 3 * Common helper functions used across the plugin 4 * 5 * @package FN_Plugin 6 * @since 1.0.0 7 */ 8 9(function($, FN) { 10 'use strict'; 11 12 // Initialize FN namespace 13 if (!window.FormNest) { 14 window.FormNest = {}; 15 } 16 17 if (!window.FormNest.Utils) { 18 window.FormNest.Utils = {}; 19 } 20 21 /** 22 * Parse JSON string safely with fallback 23 * Handles both string and already-parsed objects 24 * 25 * @param {*} data - Data to parse (string, object, or other) 26 * @param {*} defaultValue - Default value if parsing fails (default: null) 27 * @returns {*} Parsed data or default value 28 * 29 * @example 30 * // Parse array 31 * var options = FN.Utils.parseJSON('["a","b"]', []); 32 * // Returns: ["a", "b"] 33 * 34 * @example 35 * // Parse object 36 * var settings = FN.Utils.parseJSON('{"key":"value"}', {}); 37 * // Returns: {key: "value"} 38 * 39 * @example 40 * // Already parsed 41 * var options = FN.Utils.parseJSON(["a","b"], []); 42 * // Returns: ["a", "b"] (no parsing needed) 43 * 44 * @example 45 * // Invalid JSON 46 * var options = FN.Utils.parseJSON('invalid', []); 47 * // Returns: [] (default value) 48 */ 49 window.FormNest.Utils.parseJSON = function(data, defaultValue) { 50 // Set default value if not provided 51 if (defaultValue === undefined) { 52 defaultValue = null; 53 } 54 55 // Already parsed object or array 56 if (typeof data === 'object' && data !== null) { 57 return data; 58 } 59 60 // Empty or invalid string 61 if (!data || (typeof data === 'string' && !data.trim())) { 62 return defaultValue; 63 } 64 65 // Parse string 66 if (typeof data === 'string') { 67 try { 68 var parsed = JSON.parse(data); 69 return parsed !== null ? parsed : defaultValue; 70 } catch (e) { 71 return defaultValue; 72 } 73 } 74 75 // Unknown type (number, boolean, etc.) 76 return defaultValue; 77 }; 78 79 /** 80 * Parse field options safely 81 * Convenience wrapper for parseJSON with array default 82 * Used for select/radio/checkbox options 83 * 84 * @param {*} data - Options data (string or array) 85 * @returns {Array} Parsed options array 86 * 87 * @example 88 * var options = FN.Utils.parseFieldOptions('["Option 1","Option 2"]'); 89 * // Returns: ["Option 1", "Option 2"] 90 */ 91 window.FormNest.Utils.parseFieldOptions = function(data) { 92 var result = window.FormNest.Utils.parseJSON(data, []); 93 94 // Ensure array result 95 if (!Array.isArray(result)) { 96 return []; 97 } 98 99 return result; 100 }; 101 102 /** 103 * Parse field validation safely 104 * Convenience wrapper for parseJSON with object default 105 * Used for field validation rules 106 * 107 * @param {*} data - Validation data (string or object) 108 * @returns {Object} Parsed validation object 109 * 110 * @example 111 * var validation = FN.Utils.parseFieldValidation('{"required":true}'); 112 * // Returns: {required: true} 113 */ 114 window.FormNest.Utils.parseFieldValidation = function(data) { 115 var result = window.FormNest.Utils.parseJSON(data, {}); 116 117 // Ensure object result (silently convert array to object) 118 if (typeof result !== 'object' || Array.isArray(result)) { 119 // Array [] is treated as empty validation object {} 120 if (Array.isArray(result) && result.length === 0) { 121 return {}; // Silent conversion for empty array 122 } 123 124 // Non-empty arrays or invalid types use default 125 return {}; 126 } 127 128 return result; 129 }; 130 131 /** 132 * Escape HTML special characters 133 * Prevents XSS attacks 134 * 135 * @param {string} text - Text to escape 136 * @returns {string} Escaped text 137 * 138 * @example 139 * var safe = FN.Utils.escapeHtml('<script>alert("xss")</script>'); 140 * // Returns: "<script>alert("xss")</script>" 141 */ 142 window.FormNest.Utils.escapeHtml = function(text) { 143 if (!text) return ''; 144 145 var map = { 146 '&': '&', 147 '<': '<', 148 '>': '>', 149 '"': '"', 150 "'": ''' 151 }; 152 153 return String(text).replace(/[&<>
153"']/g, function(char) { 154 return map[char]; 155 }); 156 }; 157 158 /** 159 * Deep clone object 160 * Creates independent copy of object/array 161 * 162 * @param {*} obj - Object to clone 163 * @returns {*} Cloned object 164 * 165 * @example 166 * var original = {a: 1, b: {c: 2}}; 167 * var clone = FN.Utils.deepClone(original); 168 * clone.b.c = 3; 169 * // original.b.c is still 2 (independent copy) 170 */ 171 window.FormNest.Utils.deepClone = function(obj) { 172 // Primitives 173 if (obj === null || typeof obj !== 'object') { 174 return obj; 175 } 176 177 // Date 178 if (obj instanceof Date) { 179 return new Date(obj.getTime()); 180 } 181 182 // Array 183 if (obj instanceof Array) { 184 return obj.map(function(item) { 185 return window.FormNest.Utils.deepClone(item); 186 }); 187 } 188 189 // Object 190 if (obj instanceof Object) { 191 var cloned = {}; 192 for (var key in obj) { 193 if (obj.hasOwnProperty(key)) { 194 cloned[key] = window.FormNest.Utils.deepClone(obj[key]); 195 } 196 } 197 return cloned; 198 } 199 200 return obj; 201 }; 202 203 /** 204 * Check if value is empty 205 * Considers null, undefined, empty string, empty array, empty object as empty 206 * 207 * @param {*} value - Value to check 208 * @returns {boolean} True if empty 209 * 210 * @example 211 * FN.Utils.isEmpty(null) // true 212 * FN.Utils.isEmpty('') // true 213 * FN.Utils.isEmpty([]) // true 214 * FN.Utils.isEmpty({}) // true 215 * FN.Utils.isEmpty('text') // false 216 * FN.Utils.isEmpty([1, 2]) // false 217 */ 218 window.FormNest.Utils.isEmpty = function(value) { 219 if (value === null || value === undefined) { 220 return true; 221 } 222 223 if (typeof value === 'string' && value.trim() === '') { 224 return true; 225 } 226 227 if (Array.isArray(value) && value.length === 0) { 228 return true; 229 } 230 231 if (typeof value === 'object' && Object.keys(value).length === 0) { 232 return true; 233 } 234 235 return false; 236 }; 237 238 /** 239 * Debounce function 240 * Delays function execution until after wait time 241 * 242 * @param {Function} func - Function to debounce 243 * @param {number} wait - Wait time in milliseconds 244 * @param {boolean} immediate - Whether to execute immediately on first call (optional) 245 * @returns {Function} Debounced function 246 * 247 * @example 248 * var debouncedSearch = FN.Utils.debounce(function() { 249 * performSearch(); 250 * }, 300); 251 * 252 * $input.on('keyup', debouncedSearch); 253 * 254 * @example 255 * // Immediate execution 256 * var immediateDebounce = FN.Utils.debounce(function() { 257 * performAction(); 258 * }, 300, true); 259 */ 260 window.FormNest.Utils.debounce = function(func, wait, immediate) { 261 var timeout; 262 return function() { 263 var context = this; 264 var args = arguments; 265 var later = function() { 266 timeout = null; 267 if (!immediate) func.apply(context, args); 268 }; 269 var callNow = immediate && !timeout; 270 clearTimeout(timeout); 271 timeout = setTimeout(later, wait); 272 if (callNow) func.apply(context, args); 273 }; 274 }; 275 276 /** 277 * Generate unique ID 278 * Creates timestamp-based unique identifier 279 * 280 * @param {string} prefix - Optional prefix (default: 'formnest') 281 * @returns {string} Unique ID 282 * 283 * @example 284 * var id = FN.Utils.generateId('field'); 285 * // Returns: "field_1234567890123" 286 */ 287 window.FormNest.Utils.generateId = function(prefix) { 288 prefix = prefix || 'formnest'; 289 return prefix + '_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9); 290 }; 291 292 /** 293 * Escape HTML attribute values 294 * Sanitize string for HTML attribute values (prevents XSS) 295 * 296 * @param {string} str - String to escape 297 * @returns {string} Escaped string 298 * 299 * @example 300 * var safe = FN.Utils.escapeAttr('<script>alert("xss")</script>'); 301 * // Returns: "<script>alert("xss")</script>" 302 */ 303 window.FormNest.Utils.escapeAttr = function(str) { 304 if (typeof str !== 'string') { 305 return ''; 306 } 307 308 return str 309 .replace(/&/g, '&') 310 .replace(/"/g, '"') 311 .replace(/'/g, ''') 312 .replace(/</g, '<') 313 .replace(/>/g, '>'); 314 }; 315 316 /** 317 * Validate email address 318 * 319 * @param {string} email - Email address to validate 320 * @returns {boolean} True if valid email format 321 * 322 * @example 323 * FN.Utils.isValidEmail('[email protected]') // true 324 * FN.Utils.isValidEmail('invalid') // false 325 */ 326 window.FormNest.Utils.isValidEmail = function(email) { 327 if (!email || typeof email !== 'string') { 328 return false; 329 } 330 // CRITICAL: Trim and remove all spaces before validation 331 // Emails cannot contain spaces (RFC 5322) 332 var cleaned = email.trim().replace(/\s+/g, ''); 333 if (!cleaned) { 334 return false; 335 } 336 var pattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; 337 return pattern.test(cleaned); 338 }; 339 340 /** 341 * Validate URL 342 * 343 * @param {string} url - URL to validate 344 * @returns {boolean} True if valid URL format 345 * 346 * @example 347 * FN.Utils.isValidUrl('https://example.com') // true 348 * FN.Utils.isValidUrl('invalid') // false 349 */ 350 window.FormNest.Utils.isValidUrl = function(url) { 351 if (!url || typeof url !== 'string') { 352 return false; 353 } 354 try { 355 new URL(url); 356 return true; 357 } catch (e) { 358 return false; 359 } 360 }; 361 362 /** 363 * Validate phone number (basic validation) 364 * 365 * @param {string} phone - Phone number to validate 366 * @returns {boolean} True if valid phone format 367 * 368 * @example 369 * FN.Utils.isValidPhone('09012345678') // true 370 * FN.Utils.isValidPhone('invalid') // false 371 */ 372 window.FormNest.Utils.isValidPhone = function(phone) { 373 if (!phone || typeof phone !== 'string') { 374 return false; 375 } 376
377 // Allow: +81, -, spaces, parentheses 378 // Remove allowed characters for validation, then check remaining digits 379 var cleaned = phone.replace(/[\s\-\(\)]/g, ''); 380 381 // Extract digits only (remove + sign for length check) 382 var digitsOnly = cleaned.replace(/\D/g, ''); 383 384 // Check length 385 if (digitsOnly.length < 6 || digitsOnly.length > 15) { 386 return false; 387 } 388 389 // Check if starts with +81 or 81 (international format) 390 var hasPlusPrefix = cleaned.charAt(0) === '+'; 391 var startsWith81 = cleaned.indexOf('81') === 0 || (hasPlusPrefix && cleaned.indexOf('+81') === 0); 392 393 if (startsWith81) { 394 // International format: +81 or 81 prefix 395 // Remove country code (81) and check remaining digits 396 var withoutCountryCode = digitsOnly.substring(2); // Remove "81" 397 398 // Japanese phone without leading 0: should be 9-10 digits 399 if (withoutCountryCode.length >= 9 && withoutCountryCode.length <= 10) { 400 // Add leading 0 and validate as Japanese phone 401 var withLeadingZero = '0' + withoutCountryCode; 402 return isValidJapanesePhoneFormat(withLeadingZero); 403 } 404 return false; 405 } 406 407 // Check Japanese format (starts with 0) 408 if (digitsOnly.charAt(0) === '0') { 409 // Must be 10-11 digits for Japanese phone 410 if (digitsOnly.length >= 10 && digitsOnly.length <= 11) { 411 return isValidJapanesePhoneFormat(digitsOnly); 412 } 413 return false; 414 } 415 416 // For other international formats (not +81), use basic validation 417 // 7-15 digits for international numbers 418 if (digitsOnly.length >= 7 && digitsOnly.length <= 15) { 419 return true; 420 } 421 422 return false; 423 }; 424 425 /** 426 * Validate Japanese phone number format (digits only, starts with 0) 427 * @param {string} digits - Phone number digits only (e.g., "09012341234") 428 * @returns {boolean} Is valid Japanese phone format 429 */ 430 function isValidJapanesePhoneFormat(digits) { 431 if (!digits || typeof digits !== 'string') { 432 return false; 433 } 434 435 // Mobile: 090, 080, 070, 050 (10-11 digits) 436 // Accept both 10 and 11 digits for mobile: 0901234567 (10) or 09012345678 (11) 437 // Pattern: 0 + [789] + 0 + 7-8 digits = 10-11 digits total 438 if (/^0[789]0\d{7,8}$/.test(digits) || /^050\d{7,8}$/.test(digits)) { 439 return true; 440 } 441 442 // Japanese landline: 03, 06, 052, etc. (10-11 digits) 443 // Pattern: 0 + [1-9] + 8-9 digits = 10-11 digits total 444 // Note: This also matches mobile numbers like 090, but mobile is checked first above 445 if (/^0[1-9]\d{8,9}$/.test(digits)) { 446 return true; 447 } 448 449 // Japanese toll-free: 0120, 0800, 0570 (10-11 digits) 450 // Pattern: 0 + [1-9] + 2 digits + 7-8 digits = 10-11 digits total 451 // This is redundant with landline pattern above, but kept for clarity 452 if (/^0[1-9]\d{2}\d{7,8}$/.test(digits)) { 453 return true; 454 } 455 456 return false; 457 } 458 459 /** 460 * Type checking utilities 461 */ 462 window.FormNest.Utils.isFunction = function(obj) { 463 return typeof obj === 'function'; 464 }; 465 466 window.FormNest.Utils.isObject = function(obj) { 467 return typeof obj === 'object' && obj !== null && !Array.isArray(obj); 468 }; 469 470 window.FormNest.Utils.isArray = function(obj) { 471 return Array.isArray(obj); 472 }; 473 474 /** 475 * Simple object extend (shallow merge) 476 * 477 * @param {Object} target - Target object 478 * @param {Object} source - Source object 479 * @returns {Object} Extended target object 480 * 481 * @example 482 * var result = FN.Utils.extend({a: 1}, {b: 2}); 483 * // Returns: {a: 1, b: 2} 484 */ 485 window.FormNest.Utils.extend = function(target, source) { 486 if (!this.isObject(target) || !this.isObject(source)) { 487 return target; 488 } 489 490 for (var key in source) { 491 if (source.hasOwnProperty(key)) { 492 target[key] = source[key]; 493 } 494 } 495 return target; 496 }; 497 498 /** 499 * Throttle function execution 500 * Limits function execution to once per time period 501 * 502 * @param {Function} func - Function to throttle 503 * @param {number} limit - Time limit in milliseconds 504 * @returns {Function} Throttled function 505 * 506 * @example 507 * var throttledScroll = FN.Utils.throttle(function() { 508 * handleScroll(); 509 * }, 300); 510 * 511 * $(window).on('scroll', throttledScroll); 512 */ 513 window.FormNest.Utils.throttle = function(func, limit) { 514 var inThrottle; 515 return function() { 516 var args = arguments; 517 var context = this; 518 if (!inThrottle) {
519 func.apply(context, args); 520 inThrottle = true; 521 setTimeout(function() { 522 inThrottle = false; 523 }, limit); 524 } 525 }; 526 }; 527 528 /** 529 * Show button loading state 530 * Disables button and shows spinner animation 531 * 532 * @param {jQuery} $button - Button element 533 * @param {string} loadingText - Loading text to display (optional) 534 * 535 * @example 536 * FN.Utils.showButtonLoading($('#save-btn'), 'Saving...'); 537 * 538 * @example 539 * // Just spinner, no text 540 * FN.Utils.showButtonLoading($('#save-btn')); 541 */ 542 window.FormNest.Utils.showButtonLoading = function($button, loadingText) { 543 if (!$button || $button.length === 0) { 544 return; 545 } 546 547 // Store original content for restoration 548 $button.data('formnest-original-text', $button.text()); 549 $button.data('formnest-original-html', $button.html()); 550 $button.data('formnest-was-disabled', $button.prop('disabled')); 551 552 // Add loading class and disable button 553 $button.addClass('formnest-loading'); 554 $button.prop('disabled', true); 555 556 // Build spinner HTML 557 var spinnerHtml = '<span class="formnest-spinner"></span>'; 558 if (loadingText) { 559 spinnerHtml += ' ' + loadingText; 560 } 561 562 $button.html(spinnerHtml); 563 }; 564 565 /** 566 * Hide button loading state 567 * Re-enables button and restores original content 568 * 569 * @param {jQuery} $button - Button element 570 * 571 * @example 572 * FN.Utils.hideButtonLoading($('#save-btn')); 573 */ 574 window.FormNest.Utils.hideButtonLoading = function($button) { 575 if (!$button || $button.length === 0) { 576 return; 577 } 578 579 // Remove loading class 580 $button.removeClass('formnest-loading'); 581 582 // Restore disabled state (only if it wasn't disabled before) 583 var wasDisabled = $button.data('formnest-was-disabled'); 584 $button.prop('disabled', wasDisabled === true); 585 586 // Restore original content 587 var originalHtml = $button.data('formnest-original-html'); 588 if (originalHtml) { 589 $button.html(originalHtml); 590 } else { 591 var originalText = $button.data('formnest-original-text'); 592 $button.text(originalText || ''); 593 } 594 595 // Clean up data 596 $button.removeData('formnest-original-text'); 597 $button.removeData('formnest-original-html'); 598 $button.removeData('formnest-was-disabled'); 599 }; 600 601})(jQuery, window.FormNest || {}); 602
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.