// 正则表达式数据库 const regexDatabase = { // 验证类 'email': { title: '邮箱验证', category: '验证类', description: '验证电子邮箱地址的合法性,支持@前的各种字符组合,@后必须是域名格式', patterns: { javascript: '/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$/', python: 'r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$"', php: '/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$/', java: '^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$' } }, 'phone': { title: '手机号验证', category: '验证类', description: '验证中国大陆手机号码,支持最新号段', patterns: { javascript: '/^1[3-9]\\d{9}$/', python: 'r"^1[3-9]\\d{9}$"', php: '/^1[3-9]\\d{9}$/', java: '^1[3-9]\\d{9}$' } }, 'tel': { title: '固定电话验证', category: '验证类', description: '验证固定电话号码,支持区号+号码的格式', patterns: { javascript: '/^(0\\d{2,3}-?)?\\d{7,8}$/', python: 'r"^(0\\d{2,3}-?)?\\d{7,8}$"', php: '/^(0\\d{2,3}-?)?\\d{7,8}$/', java: '^(0\\d{2,3}-?)?\\d{7,8}$' } }, 'password': { title: '密码强度验证', category: '验证类', description: '密码必须包含大小写字母、数字和特殊字符,长度8-16位', patterns: { javascript: '/^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[@$!%*?&])[A-Za-z\\d@$!%*?&]{8,16}$/', python: 'r"^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[@$!%*?&])[A-Za-z\\d@$!%*?&]{8,16}$"', php: '/^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[@$!%*?&])[A-Za-z\\d@$!%*?&]{8,16}$/', java: '^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[@$!%*?&])[A-Za-z\\d@$!%*?&]{8,16}$' } }, 'qq': { title: 'QQ号验证', category: '验证类', description: '验证QQ号,必须是5-11位数字,首位不能为0', patterns: { javascript: '/^[1-9][0-9]{4,10}$/', python: 'r"^[1-9][0-9]{4,10}$"', php: '/^[1-9][0-9]{4,10}$/', java: '^[1-9][0-9]{4,10}$' } }, 'postal': { title: '邮政编码验证', category: '验证类', description: '验证中国邮政编码,6位数字', patterns: { javascript: '/^\\d{6}$/', python: 'r"^\\d{6}$"', php: '/^\\d{6}$/', java: '^\\d{6}$' } }, 'account': { title: '账号验证', category: '验证类', description: '验证账号,字母开头,允许5-16位,字母数字下划线组合', patterns: { javascript: '/^[a-zA-Z]\\w{4,15}$/', python: 'r"^[a-zA-Z]\\w{4,15}$"', php: '/^[a-zA-Z]\\w{4,15}$/', java: '^[a-zA-Z]\\w{4,15}$' } }, 'url': { title: 'URL验证', category: '验证类', description: '验证URL地址的合法性,支持http、https协议,可选端口、路径、参数、锚点,支持localhost和IP地址', patterns: { javascript: '/^(https?:\\/\\/)?((([\\w-]+\\.)+[\\w-]+|localhost|\\d{1,3}(?:\\.\\d{1,3}){3}))(\\:\\d{1,5})?(\\/[^\\s?#]*)?(\\?[^\\s#]*)?(#[^\\s]*)?$/i', python: 'r"(https?:\/\/)?((([\w-]+\.)+[\w-]+|localhost|\d{1,3}(?:\.\d{1,3}){3}))(\:\d{1,5})?(\/[^s?#]*)?(\?[^s#]*)?(#[^s]*)?$"', php: '/^(https?:\\/\\/)?((([\\w-]+\\.)+[\\w-]+|localhost|\\d{1,3}(?:\\.\\d{1,3}){3}))(\\:\\d{1,5})?(\\/[^\\s?#]*)?(\\?[^\\s#]*)?(#[^\\s]*)?$/i', java: '^(https?:\\/\\/)?((([\\w-]+\\.)+[\\w-]+|localhost|\\d{1,3}(?:\\.\\d{1,3}){3}))(\\:\\d{1,5})?(\\/[^\\s?#]*)?(\\?[^\\s#]*)?(#[^\\s]*)?$' } }, 'idcard': { title: '身份证验证', category: '验证类', description: '验证中国大陆居民身份证号码,支持18位', patterns: { javascript: '/^[1-9]\\d{5}(19|20)\\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\\d|3[01])\\d{3}[0-9X]$/', python: 'r"^[1-9]\\d{5}(19|20)\\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\\d|3[01])\\d{3}[0-9X]$"', php: '/^[1-9]\\d{5}(19|20)\\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\\d|3[01])\\d{3}[0-9X]$/', java: '^[1-9]\\d{5}(19|20)\\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\\d|3[01])\\d{3}[0-9X]$' } }, 'ipv4': { title: 'IPv4地址验证', category: '验证类', description: '验证IPv4地址格式', patterns: { javascript: '/^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/', python: 'r"^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$"', php: '/^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/', java: '^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$' } }, 'date': { title: '日期验证', category: '验证类', description: '验证日期格式(YYYY-MM-DD)', patterns: { javascript: '/^\\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\\d|3[01])$/', python: 'r"^\\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\\d|3[01])$"', php: '/^\\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\\d|3[01])$/', java: '^\\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\\d|3[01])$' } }, // 提取类 'html-tag': { title: 'HTML标签提取', category: '提取类', description: '提取HTML标签及其内容', patterns: { javascript: '/<([a-z][a-z0-9]*)[^>]*>.*?<\\/\\1>/gi', python: 'r"<([a-z][a-z0-9]*)[^>]*>.*?"', php: '/<([a-z][a-z0-9]*)[^>]*>.*?<\\/\\1>/i', java: '<([a-z][a-z0-9]*)[^>]*>.*?' } }, 'img-url': { title: '图片URL提取', category: '提取类', description: '提取HTML中的图片URL', patterns: { javascript: '/]+src="([^">]+)"/gi', python: 'r"]+src=\\"([^\\">]+)\\""', php: '/]+src="([^">]+)"/i', java: ']+src="([^">]+)"' } }, 'chinese': { title: '中文字符提取', category: '提取类', description: '提取中文字符', patterns: { javascript: '/[\\u4e00-\\u9fa5]/g', python: 'r"[\\u4e00-\\u9fa5]"', php: '/[\\x{4e00}-\\x{9fa5}]/u', java: '[\\u4e00-\\u9fa5]' } }, 'numbers': { title: '数字提取', category: '提取类', description: '提取字符串中的数字', patterns: { javascript: '/\\d+(\\.\\d+)?/g', python: 'r"\\d+(\\.\\d+)?"', php: '/\\d+(\\.\\d+)?/', java: '\\d+(\\.\\d+)?' } }, 'email-extract': { title: '邮箱地址提取', category: '提取类', description: '提取文本中的邮箱地址', patterns: { javascript: '/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}/g', python: 'r"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}"', php: '/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}/', java: '[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}' } }, 'color-hex': { title: '颜色值提取', category: '提取类', description: '提取16进制颜色值,支持3位和6位格式', patterns: { javascript: '/#([0-9a-fA-F]{6}|[0-9a-fA-F]{3})/g', python: 'r"#([0-9a-fA-F]{6}|[0-9a-fA-F]{3})"', php: '/#([0-9a-fA-F]{6}|[0-9a-fA-F]{3})/i', java: '#([0-9a-fA-F]{6}|[0-9a-fA-F]{3})' } }, 'ip-extract': { title: 'IP地址提取', category: '提取类', description: '提取IPv4地址', patterns: { javascript: '/\\b(?:\\d{1,3}\\.){3}\\d{1,3}\\b/g', python: 'r"\\b(?:\\d{1,3}\\.){3}\\d{1,3}\\b"', php: '/\\b(?:\\d{1,3}\\.){3}\\d{1,3}\\b/', java: '\\b(?:\\d{1,3}\\.){3}\\d{1,3}\\b' } }, // 替换类 'trim': { title: '去除首尾空格', category: '替换类', description: '去除字符串首尾的空白字符', patterns: { javascript: '/^\\s+|\\s+$/g', python: 'r"^\\s+|\\s+$"', php: '/^\\s+|\\s+$/', java: '^\\s+|\\s+$' } }, 'remove-html': { title: '去除HTML标签', category: '替换类', description: '去除文本中的HTML标签', patterns: { javascript: '/<[^>]*>/g', python: 'r"<[^>]*>"', php: '/<[^>]*>/', java: '<[^>]*>' } }, 'camelcase': { title: '驼峰命名转换', category: '替换类', description: '将下划线命名转换为驼峰命名', patterns: { javascript: '/_([a-z])/g', python: 'r"_([a-z])"', php: '/_([a-z])/', java: '_([a-z])' } }, 'remove-script': { title: '去除Script标签', category: '替换类', description: '去除HTML中的script标签及其内容', patterns: { javascript: '/]*>[\\s\\S]*?<\\/script>/gi', python: 'r"]*>[\\s\\S]*?"', php: '/]*>[\\s\\S]*?<\\/script>/i', java: ']*>[\s\S]*?' } }, 'remove-space': { title: '去除多余空格', category: '替换类', description: '将多个连续空格替换为单个空格', patterns: { javascript: '/\\s+/g', python: 'r"\\s+"', php: '/\\s+/', java: '\\s+' } }, 'remove-comment': { title: '去除注释', category: '替换类', description: '去除JavaScript单行和多行注释', patterns: { javascript: '/\\/\\*[\\s\\S]*?\\*\\/|\\/\\/.*$/gm', python: 'r"#.*$"', php: '/\\/\\*[\\s\\S]*?\\*\\/|\\/\\/.*$/m', java: '\\/\\*[\\s\\S]*?\\*\\/|\\/\\/.*$' } }, // 补全缺失项(占位符) 'number': { title: '数字验证', category: '验证类', description: '验证是否为数字', patterns: { javascript: '/^\\d+$/', python: 'r"^\\d+$"', php: '/^\\d+$/', java: '^\\d+$' } }, 'number-n-digits': { title: 'n位数字验证', category: '验证类', description: '验证是否为n位数字', patterns: { javascript: '/^\\d{n}$/', python: 'r"^\\d{n}$"', php: '/^\\d{n}$/', java: '^\\d{n}$' } }, 'number-min-n-digits': { title: '至少n位数字验证', category: '验证类', description: '验证是否为至少n位数字', patterns: { javascript: '/^\\d{n,}$/', python: 'r"^\\d{n,}$"', php: '/^\\d{n,}$/', java: '^\\d{n,}$' } }, 'number-range-digits': { title: '数字位数范围验证', category: '验证类', description: '验证数字位数范围', patterns: { javascript: '/^\\d{m,n}$/', python: 'r"^\\d{m,n}$"', php: '/^\\d{m,n}$/', java: '^\\d{m,n}$' } }, 'decimal': { title: '小数验证', category: '验证类', description: '验证小数', patterns: { javascript: '/^\\d+\\.\\d+$/', python: 'r"^\\d+\\.\\d+$"', php: '/^\\d+\\.\\d+$/', java: '^\\d+\\.\\d+$' } }, 'integer': { title: '整数验证', category: '验证类', description: '验证整数', patterns: { javascript: '/^-?\\d+$/', python: 'r"^-?\\d+$"', php: '/^-?\\d+$/', java: '^-?\\d+$' } }, 'chinese-name': { title: '中文姓名验证', category: '验证类', description: '验证中文姓名', patterns: { javascript: '/^[\\u4e00-\\u9fa5]{2,}$/', python: 'r"^[\\u4e00-\\u9fa5]{2,}$"', php: '/^[\\x{4e00}-\\x{9fa5}]{2,}$/u', java: '^[\\u4e00-\\u9fa5]{2,}$' } }, 'english-name': { title: '英文姓名验证', category: '验证类', description: '验证英文姓名', patterns: { javascript: '/^[A-Za-z\\s]+$/', python: 'r"^[A-Za-z\\s]+$"', php: '/^[A-Za-z\\s]+$/', java: '^[A-Za-z\\s]+$' } }, 'username': { title: '用户名验证', category: '验证类', description: '验证用户名', patterns: { javascript: '/^[a-zA-Z0-9_]{4,16}$/', python: 'r"^[a-zA-Z0-9_]{4,16}$"', php: '/^[a-zA-Z0-9_]{4,16}$/', java: '^[a-zA-Z0-9_]{4,16}$' } }, 'password-strong': { title: '强密码验证', category: '验证类', description: '强密码(8-16位,含大小写字母、数字、特殊字符)', patterns: { javascript: '/^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[@$!%*?&])[A-Za-z\\d@$!%*?&]{8,16}$/', python: 'r"^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[@$!%*?&])[A-Za-z\\d@$!%*?&]{8,16}$"', php: '/^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[@$!%*?&])[A-Za-z\\d@$!%*?&]{8,16}$/', java: '^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[@$!%*?&])[A-Za-z\\d@$!%*?&]{8,16}$' } }, 'mac-address': { title: 'MAC地址验证', category: '验证类', description: '验证MAC地址', patterns: { javascript: '/^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$/', python: 'r"^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$"', php: '/^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$/', java: '^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$' } }, 'hex-color': { title: '16进制颜色验证', category: '验证类', description: '验证16进制颜色', patterns: { javascript: '/^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/', python: 'r"^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$"', php: '/^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/', java: '^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$' } }, 'version-number': { title: '版本号验证', category: '验证类', description: '验证版本号格式', patterns: { javascript: '/^\\d+\\.\\d+\\.\\d+$/', python: 'r"^\\d+\\.\\d+\\.\\d+$"', php: '/^\\d+\\.\\d+\\.\\d+$/', java: '^\\d+\\.\\d+\\.\\d+$' } }, 'link-extract': { title: '链接提取', category: '提取类', description: '提取文本中的链接', patterns: { javascript: '/https?:\\/\\/[^\\s]+/g', python: 'r"https?:\\/\\/[^\\s]+"', php: '/https?:\\/\\/[^\\s]+/g', java: 'https?:\\/\\/[^\\s]+' } }, // 格式化类 'money': { title: '金额格式化', category: '格式化类', description: '匹配金额(保留两位小数)', patterns: { javascript: '/^\\d+(?:\\.\\d{1,2})?$/', python: 'r"^\\d+(?:\\.\\d{1,2})?$"', php: '/^\\d+(?:\\.\\d{1,2})?$/', java: '^\\d+(?:\\.\\d{1,2})?$' } }, 'phone-format': { title: '手机号格式化', category: '格式化类', description: '匹配中国大陆手机号格式', patterns: { javascript: '/^1[3-9]\\d{9}$/', python: 'r"^1[3-9]\\d{9}$"', php: '/^1[3-9]\\d{9}$/', java: '^1[3-9]\\d{9}$' } }, 'date-format': { title: '日期格式化', category: '格式化类', description: '匹配日期格式(YYYY-MM-DD)', patterns: { javascript: '/^\\d{4}-\\d{2}-\\d{2}$/', python: 'r"^\\d{4}-\\d{2}-\\d{2}$"', php: '/^\\d{4}-\\d{2}-\\d{2}$/', java: '^\\d{4}-\\d{2}-\\d{2}$' } }, 'card-format': { title: '银行卡格式化', category: '格式化类', description: '匹配银行卡号(16或19位数字)', patterns: { javascript: '/^\\d{16}|\\d{19}$/', python: 'r"^\\d{16}|\\d{19}$"', php: '/^\\d{16}|\\d{19}$/', java: '^\\d{16}|\\d{19}$' } }, 'idcard-format': { title: '身份证格式化', category: '格式化类', description: '匹配中国大陆18位身份证号', patterns: { javascript: '/^[1-9]\\d{5}(19|20)\\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\\d|3[01])\\d{3}[0-9Xx]$/', python: 'r"^[1-9]\\d{5}(19|20)\\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\\d|3[01])\\d{3}[0-9Xx]$"', php: '/^[1-9]\\d{5}(19|20)\\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\\d|3[01])\\d{3}[0-9Xx]$/', java: '^[1-9]\\d{5}(19|20)\\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\\d|3[01])\\d{3}[0-9Xx]$' } }, // 特殊字符类 'emoji': { title: 'Emoji表情', category: '特殊字符类', description: '匹配Emoji表情字符', patterns: { javascript: '/[\\uD83C-\\uDBFF\\uDC00-\\uDFFF]+/g', python: 'r"[\\U0001F600-\\U0001F64F]"', php: '/[\\x{1F600}-\\x{1F64F}]/u', java: '[\\uD83C-\\uDBFF\\uDC00-\\uDFFF]' } }, 'special-char': { title: '特殊字符', category: '特殊字符类', description: '匹配特殊字符', patterns: { javascript: '/[!@#$%^&*(),.?\":{}|<>]/g', python: 'r"[!@#$%^&*(),.?\":{}|<>]"', php: '/[!@#$%^&*(),.?\":{}|<>]/', java: '[!@#$%^&*(),.?\":{}|<>]' } }, 'unicode': { title: 'Unicode字符', category: '特殊字符类', description: '匹配Unicode字符', patterns: { javascript: '/\\u[0-9a-fA-F]{4}/g', python: 'r"\\u[0-9a-fA-F]{4}"', php: '/\\u[0-9a-fA-F]{4}/', java: '\\u[0-9a-fA-F]{4}' } }, 'invisible-char': { title: '不可见字符', category: '特殊字符类', description: '匹配不可见字符(如空格、制表符等)', patterns: { javascript: '/[\\s\\u200B-\\u200D\\uFEFF]/g', python: 'r"[\\s\\u200B-\\u200D\\uFEFF]"', php: '/[\\s\\x{200B}-\\x{200D}\\x{FEFF}]/u', java: '[\\s\\u200B-\\u200D\\uFEFF]' } }, // 编程相关 'variable': { title: '变量命名', category: '编程相关', description: '匹配常见变量命名(字母、下划线、数字,不能以数字开头)', patterns: { javascript: '/^[a-zA-Z_][a-zA-Z0-9_]*$/', python: 'r"^[a-zA-Z_][a-zA-Z0-9_]*$"', php: '/^[a-zA-Z_][a-zA-Z0-9_]*$/', java: '^[a-zA-Z_][a-zA-Z0-9_]*$' } }, 'function': { title: '函数声明', category: '编程相关', description: '匹配JavaScript函数声明', patterns: { javascript: '/function\\s+[a-zA-Z_][a-zA-Z0-9_]*\\s*\\(/', python: 'r"def\\s+[a-zA-Z_][a-zA-Z0-9_]*\\s*\\("', php: '/function\\s+[a-zA-Z_][a-zA-Z0-9_]*\\s*\\(/', java: 'function\\s+[a-zA-Z_][a-zA-Z0-9_]*\\s*\\(' } }, 'json': { title: 'JSON格式', category: '编程相关', description: '匹配简单JSON格式', patterns: { javascript: '/^\\{.*\\}$/s', python: 'r"^\\{.*\\}$"', php: '/^\\{.*\\}$/s', java: '^\\{.*\\}$' } }, 'xml': { title: 'XML标签', category: '编程相关', description: '匹配XML标签', patterns: { javascript: '/<([a-zA-Z][a-zA-Z0-9]*)\\b[^>]*>(.*?)<\\/\\1>/', python: 'r"<([a-zA-Z][a-zA-Z0-9]*)\\b[^>]*>(.*?)<\\/\\1>"', php: '/<([a-zA-Z][a-zA-Z0-9]*)\\b[^>]*>(.*?)<\\/\\1>/', java: '<([a-zA-Z][a-zA-Z0-9]*)\\b[^>]*>(.*?)<\\/\\1>' } }, 'css': { title: 'CSS选择器', category: '编程相关', description: '匹配简单CSS选择器', patterns: { javascript: '/^[a-zA-Z][a-zA-Z0-9-_]*$/', python: 'r"^[a-zA-Z][a-zA-Z0-9-_]*$"', php: '/^[a-zA-Z][a-zA-Z0-9-_]*$/', java: '^[a-zA-Z][a-zA-Z0-9-_]*$' } } }; // 1. 补充下拉框正则项到regexDatabase const extraRegexPresets = [ // 常用字符 { key: 'chinese-char', title: '匹配中文字符', category: '常用字符', description: '匹配所有中文字符', patterns: { javascript: '/[\u4e00-\u9fa5]/gm', python: 'r"[\u4e00-\u9fa5]"', php: '/[\\x{4e00}-\\x{9fa5}]/u', java: '[\u4e00-\u9fa5]' } }, { key: 'double-byte-char', title: '匹配双字节字符', category: '常用字符', description: '匹配所有双字节字符', patterns: { javascript: '/[^\x00-\xff]/igm', python: 'r"[^\x00-\xff]"', php: '/[^\x00-\xff]/i', java: '[^\x00-\xff]' } }, { key: 'trim-line', title: '匹配行尾行首空白', category: '常用字符', description: '匹配每行首尾的空白字符', patterns: { javascript: '/(^\\s*)|(\\s*$)/', python: 'r"(^\\s*)|(\\s*$)"', php: '/(^\\s*)|(\\s*$)/', java: '(^\\s*)|(\\s*$)' } }, { key: 'only-number', title: '只能输入数字', category: '常用字符', description: '只能输入数字', patterns: { javascript: '/^\\d+$/', python: 'r"^\\d+$"', php: '/^\\d+$/', java: '^\\d+$' } }, { key: 'number-n', title: '只能输入n个数字', category: '常用字符', description: '只能输入n个数字', patterns: { javascript: '/^\\d{n}$/', python: 'r"^\\d{n}$"', php: '/^\\d{n}$/', java: '^\\d{n}$' } }, { key: 'number-min-n', title: '至少输入n个以上的数字', category: '常用字符', description: '至少输入n个以上的数字', patterns: { javascript: '/^\\d{n,}$/', python: 'r"^\\d{n,}$"', php: '/^\\d{n,}$/', java: '^\\d{n,}$' } }, { key: 'number-m-n', title: '只能输入m到n个数字', category: '常用字符', description: '只能输入m到n个数字', patterns: { javascript: '/^\\d{m,n}$/', python: 'r"^\\d{m,n}$"', php: '/^\\d{m,n}$/', java: '^\\d{m,n}$' } }, { key: 'only-alpha', title: '只能由英文字母组成', category: '常用字符', description: '只能由英文字母组成', patterns: { javascript: '/^[a-z]+$/i', python: 'r"^[a-zA-Z]+$"', php: '/^[a-zA-Z]+$/', java: '^[a-zA-Z]+$' } }, { key: 'only-uppercase', title: '只能由大写英文字母组成', category: '常用字符', description: '只能由大写英文字母组成', patterns: { javascript: '/^[A-Z]+$/', python: 'r"^[A-Z]+$"', php: '/^[A-Z]+$/', java: '^[A-Z]+$' } }, { key: 'alpha-number', title: '只能由英文和数字组成', category: '常用字符', description: '只能由英文和数字组成', patterns: { javascript: '/^[a-z0-9]+$/i', python: 'r"^[a-zA-Z0-9]+$"', php: '/^[a-zA-Z0-9]+$/', java: '^[a-zA-Z0-9]+$' } }, { key: 'alpha-number-underscore', title: '只能由英文、数字、下划线组成', category: '常用字符', description: '只能由英文、数字、下划线组成', patterns: { javascript: '/^\\w+$/', python: 'r"^\\w+$"', php: '/^\\w+$/', java: '^\\w+$' } }, // 常用表单 { key: 'email-simple', title: '匹配Email地址', category: '常用表单', description: '匹配Email地址', patterns: { javascript: '/\w+([-+.\w+])*@\w+([-.]\w+)*\.\w+([-.]\w+)*/', python: 'r"\w+[\-+.\w+]*@\w+[\-.\w+]*\.\w+[\-.\w+]*"', php: '/\w+([-+.\w+])*@\w+([-.]\w+)*\.\w+([-.]\w+)*/', java: '\w+([-+.\w+])*@\w+([-.]\w+)*\.\w+([-.]\w+)*' } }, { key: 'url-simple', title: '匹配URL地址', category: '常用表单', description: '匹配URL地址', patterns: { javascript: '/^https?:\/\/(([a-zA-Z0-9_-])+(\.)?)*(?::\d+)?(\/((\.)?(\?)?=?&?[a-zA-Z0-9_-](\?)?)*)*$/i', python: 'r"^https?:\/\/(([a-zA-Z0-9_-])+(\.)?)*(?::\d+)?(\/((\.)?(\?)?=?&?[a-zA-Z0-9_-](\?)?)*)*$"', php: '/^https?:\/\/(([a-zA-Z0-9_-])+(\.)?)*(?::\d+)?(\/((\.)?(\?)?=?&?[a-zA-Z0-9_-](\?)?)*)*$/i', java: '^https?:\/\/(([a-zA-Z0-9_-])+(\.)?)*(?::\d+)?(\/((\.)?(\?)?=?&?[a-zA-Z0-9_-](\?)?)*)*$' } }, { key: 'mobile-cn', title: '匹配手机号码', category: '常用表单', description: '匹配中国大陆手机号码', patterns: { javascript: '/^(0|86|17951)?(13[0-9]|15[012356789]|166|17[3678]|18[0-9]|14[57])[0-9]{8}$/', python: 'r"^(0|86|17951)?(13[0-9]|15[012356789]|166|17[3678]|18[0-9]|14[57])[0-9]{8}$"', php: '/^(0|86|17951)?(13[0-9]|15[012356789]|166|17[3678]|18[0-9]|14[57])[0-9]{8}$/', java: '^(0|86|17951)?(13[0-9]|15[012356789]|166|17[3678]|18[0-9]|14[57])[0-9]{8}$' } }, { key: 'idcard-mix', title: '匹配身份证号', category: '常用表单', description: '匹配中国大陆身份证号', patterns: { javascript: '/^(^[1-9]\d{7}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}$)|(^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])((\d{4})|\d{3}[Xx])$)$/', python: 'r"(^[1-9]\d{7}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}$)|(^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])((\d{4})|\d{3}[Xx])$)"', php: '/^(^[1-9]\d{7}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}$)|(^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])((\d{4})|\d{3}[Xx])$)$/', java: '(^[1-9]\d{7}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}$)|(^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])((\d{4})|\d{3}[Xx])$)' } }, { key: 'postal-code', title: '匹配邮编号', category: '常用表单', description: '匹配中国邮政编码', patterns: { javascript: '/^[1-9]\d{5}(?!\d)$/', python: 'r"^[1-9]\d{5}(?!\d)$"', php: '/^[1-9]\d{5}(?!\d)$/', java: '^[1-9]\d{5}(?!\d)$' } }, { key: 'date-ymd', title: '匹配日期(yyyy-MM-dd)', category: '常用表单', description: '匹配日期格式(yyyy-MM-dd)', patterns: { javascript: '/^[1-2][0-9][0-9][0-9]-[0-1]{0,1}[0-9]-[0-3]{0,1}[0-9]$/', python: 'r"^[1-2][0-9][0-9][0-9]-[0-1]{0,1}[0-9]-[0-3]{0,1}[0-9]$"', php: '/^[1-2][0-9][0-9][0-9]-[0-1]{0,1}[0-9]-[0-3]{0,1}[0-9]$/', java: '^[1-2][0-9][0-9][0-9]-[0-1]{0,1}[0-9]-[0-3]{0,1}[0-9]$' } }, { key: 'car-plate-blue', title: '匹配车牌号(蓝牌)', category: '常用表单', description: '匹配中国大陆蓝牌车牌号', patterns: { javascript: '/^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z][A-Z][A-Z0-9]{4}[A-Z0-9挂学警港澳]$/', python: 'r"^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z][A-Z][A-Z0-9]{4}[A-Z0-9挂学警港澳]$"', php: '/^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z][A-Z][A-Z0-9]{4}[A-Z0-9挂学警港澳]$/', java: '^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z][A-Z][A-Z0-9]{4}[A-Z0-9挂学警港澳]$' } }, { key: 'car-plate-green', title: '匹配车牌号(绿牌)', category: '常用表单', description: '匹配中国大陆绿牌车牌号', patterns: { javascript: '/^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z][A-Z][A-Z0-9]{5}[A-Z0-9挂学警港澳]$/', python: 'r"^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z][A-Z][A-Z0-9]{5}[A-Z0-9挂学警港澳]$"', php: '/^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z][A-Z][A-Z0-9]{5}[A-Z0-9挂学警港澳]$/', java: '^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z][A-Z][A-Z0-9]{5}[A-Z0-9挂学警港澳]$' } }, // 浏览器UA { key: 'ua-ie', title: '从UA判断是否为IE浏览器', category: '浏览器UA', description: '匹配IE浏览器的userAgent', patterns: { javascript: '/msie (\\d+\\.\\d+)/i', python: 'r"msie (\\d+\\.\\d+)"', php: '/msie (\\d+\\.\\d+)/i', java: 'msie (\\d+\\.\\d+)' } }, { key: 'ua-webkit', title: '从UA判断是否为webkit内核', category: '浏览器UA', description: '匹配webkit内核的userAgent', patterns: { javascript: '/webkit/i', python: 'r"webkit"', php: '/webkit/i', java: 'webkit' } }, { key: 'ua-chrome', title: '从UA判断是否为chrome浏览器', category: '浏览器UA', description: '匹配chrome浏览器的userAgent', patterns: { javascript: '/chrome\\/(\\d+\\.\\d+)/i', python: 'r"chrome/(\\d+\\.\\d+)"', php: '/chrome\\/(\\d+\\.\\d+)/i', java: 'chrome/(\\d+\\.\\d+)' } }, { key: 'ua-firefox', title: '从UA判断是否为firefox浏览器', category: '浏览器UA', description: '匹配firefox浏览器的userAgent', patterns: { javascript: '/firefox\\/(\\d+\\.\\d+)/i', python: 'r"firefox/(\\d+\\.\\d+)"', php: '/firefox\\/(\\d+\\.\\d+)/i', java: 'firefox/(\\d+\\.\\d+)' } }, { key: 'ua-opera', title: '从UA判断是否为opera浏览器', category: '浏览器UA', description: '匹配opera浏览器的userAgent', patterns: { javascript: '/opera(\\/| )(\\d+(\\.\\d+)?)(.+?(version\\/(\\d+(\\.\\d+)?)))?/i', python: 'r"opera(/| )(\\d+(\\.\\d+)?)(.+?(version/(\\d+(\\.\\d+)?)))?"', php: '/opera(\\/| )(\\d+(\\.\\d+)?)(.+?(version\\/(\\d+(\\.\\d+)?)))?/i', java: 'opera(/| )(\\d+(\\.\\d+)?)(.+?(version/(\\d+(\\.\\d+)?)))?' } }, { key: 'ua-safari', title: '从UA判断是否为Safari浏览器', category: '浏览器UA', description: '匹配Safari浏览器的userAgent', patterns: { javascript: '/(\\d+\\.\\d)?(?:\\.\\d)?\\s+safari\\/?(\\d+\\.\\d+)?/i', python: 'r"(\\d+\\.\\d)?(?:\\.\\d)?\\s+safari/?(\\d+\\.\\d+)?"', php: '/(\\d+\\.\\d)?(?:\\.\\d)?\\s+safari\\/?(\\d+\\.\\d+)?/i', java: '(\\d+\\.\\d)?(?:\\.\\d)?\\s+safari/?(\\d+\\.\\d+)?' } }, { key: 'ua-android', title: '从UA中判断是否为Android系统', category: '浏览器UA', description: '匹配Android系统的userAgent', patterns: { javascript: '/android/i', python: 'r"android"', php: '/android/i', java: 'android' } }, { key: 'ua-ipad', title: '从UA中判断是否为iPad', category: '浏览器UA', description: '匹配iPad的userAgent', patterns: { javascript: '/ipad/i', python: 'r"ipad"', php: '/ipad/i', java: 'ipad' } }, { key: 'ua-iphone', title: '从UA中判断是否为iPhone', category: '浏览器UA', description: '匹配iPhone的userAgent', patterns: { javascript: '/iphone/i', python: 'r"iphone"', php: '/iphone/i', java: 'iphone' } }, { key: 'ua-mac', title: '从UA判断是否为Mac OS平台', category: '浏览器UA', description: '匹配Mac OS的userAgent', patterns: { javascript: '/macintosh/i', python: 'r"macintosh"', php: '/macintosh/i', java: 'macintosh' } }, { key: 'ua-windows', title: '从UA中判断是否为Windows平台', category: '浏览器UA', description: '匹配Windows平台的userAgent', patterns: { javascript: '/windows/i', python: 'r"windows"', php: '/windows/i', java: 'windows' } }, { key: 'ua-mobile', title: '从UA中判断是否为移动终端', category: '浏览器UA', description: '匹配移动终端的userAgent', patterns: { javascript: '/(nokia|iphone|android|ipad|motorola|^mot\-|softbank|foma|docomo|kddi|up\\.browser|up\\.link|htc|dopod|blazer|netfront|helio|hosin|huawei|novarra|CoolPad|webos|techfaith|palmsource|blackberry|alcatel|amoi|ktouch|nexian|samsung|^sam\-|s[cg]h|^lge|ericsson|philips|sagem|wellcom|bunjalloo|maui|symbian|smartphone|midp|wap|phone|windows ce|iemobile|^spice|^bird|^zte\-|longcos|pantech|gionee|^sie\-|portalmmm|jig\\s browser|hiptop|^ucweb|^benq|haier|^lct|opera\\s*mobi|opera\*mini|320x320|240x320|176x220)/i', python: 'r"(nokia|iphone|android|ipad|motorola|^mot\-|softbank|foma|docomo|kddi|up\\.browser|up\\.link|htc|dopod|blazer|netfront|helio|hosin|huawei|novarra|CoolPad|webos|techfaith|palmsource|blackberry|alcatel|amoi|ktouch|nexian|samsung|^sam\-|s[cg]h|^lge|ericsson|philips|sagem|wellcom|bunjalloo|maui|symbian|smartphone|midp|wap|phone|windows ce|iemobile|^spice|^bird|^zte\-|longcos|pantech|gionee|^sie\-|portalmmm|jig\\s browser|hiptop|^ucweb|^benq|haier|^lct|opera\\s*mobi|opera\*mini|320x320|240x320|176x220)"', php: '/(nokia|iphone|android|ipad|motorola|^mot\-|softbank|foma|docomo|kddi|up\\.browser|up\\.link|htc|dopod|blazer|netfront|helio|hosin|huawei|novarra|CoolPad|webos|techfaith|palmsource|blackberry|alcatel|amoi|ktouch|nexian|samsung|^sam\-|s[cg]h|^lge|ericsson|philips|sagem|wellcom|bunjalloo|maui|symbian|smartphone|midp|wap|phone|windows ce|iemobile|^spice|^bird|^zte\-|longcos|pantech|gionee|^sie\-|portalmmm|jig\\s browser|hiptop|^ucweb|^benq|haier|^lct|opera\\s*mobi|opera\*mini|320x320|240x320|176x220)/i', java: '(nokia|iphone|android|ipad|motorola|^mot\-|softbank|foma|docomo|kddi|up\\.browser|up\\.link|htc|dopod|blazer|netfront|helio|hosin|huawei|novarra|CoolPad|webos|techfaith|palmsource|blackberry|alcatel|amoi|ktouch|nexian|samsung|^sam\-|s[cg]h|^lge|ericsson|philips|sagem|wellcom|bunjalloo|maui|symbian|smartphone|midp|wap|phone|windows ce|iemobile|^spice|^bird|^zte\-|longcos|pantech|gionee|^sie\-|portalmmm|jig\\s browser|hiptop|^ucweb|^benq|haier|^lct|opera\\s*mobi|opera\*mini|320x320|240x320|176x220)' } }, // HTML相关 { key: 'html-link', title: '匹配link标签', category: 'HTML相关', description: '匹配HTML中的link标签', patterns: { javascript: '/)|(\\/>)|(\\<\\/link>))/gi', python: 'r")|(\\/>)|(\\<\\/link>))"', php: '/)|(\\/>)|(\\<\\/link>))/i', java: ')|(\\/>)|(\\<\\/link>))' } }, { key: 'html-tag', title: '匹配HTML标签', category: 'HTML相关', description: '匹配HTML标签', patterns: { javascript: '/<(\\S*?) [^>]*>.*?<\\/\\1>|<.*?\/>/gm', python: 'r"<(\\S*?) [^>]*>.*?|<.*?/>"', php: '/<(\\S*?) [^>]*>.*?<\\/\\1>|<.*?\/>/m', java: '<(\\S*?) [^>]*>.*?|<.*?/>' } }, { key: 'not-html-tag', title: '匹配非HTML标签', category: 'HTML相关', description: '匹配非HTML标签', patterns: { javascript: '/^[^<>`~!\/@#}$%:;)(_^{&*=|\'+]+$/', python: 'r"^[^<>`~!/@#}$%:;)(_^{&*=|\'+]+$"', php: '/^[^<>`~!\/@#}$%:;)(_^{&*=|\'+]+$/', java: '^[^<>`~!/@#}$%:;)(_^{&*=|\'+]+$' } }, { key: 'html-script', title: '匹配script标签', category: 'HTML相关', description: '匹配script标签', patterns: { javascript: '/]*>[\s\S]*?<\\/[^>]*script>/gi', python: 'r"]*>[\s\S]*?]*script>"', php: '/]*>[\s\S]*?<\\/[^>]*script>/i', java: ']*>[\s\S]*?]*script>' } }, { key: 'html-comment', title: '匹配HTML注释', category: 'HTML相关', description: '匹配HTML注释', patterns: { javascript: '/"', php: '/' } }, { key: 'html-cond-comment', title: '匹配HTML条件注释', category: 'HTML相关', description: '匹配HTML条件注释', patterns: { javascript: '/\[\s*if\s+[^\]][\s\w]*\]/i', python: 'r"\[\s*if\s+[^\]][\s\w]*\]"', php: '/\[\s*if\s+[^\]][\s\w]*\]/i', java: '\[\s*if\s+[^\]][\s\w]*\]' } }, { key: 'html-cond-comment-not-ie', title: '匹配非IE的条件注释', category: 'HTML相关', description: '匹配非IE的条件注释', patterns: { javascript: '/^\[if\s+(!IE|false)\]>.*.*.*.*/gi', python: 'r"<\W+>"', php: '/<\W+>/i', java: '<\W+>' } }, { key: 'html-textarea', title: '匹配textarea标签', category: 'HTML相关', description: '匹配textarea标签', patterns: { javascript: '/]*>[\s\S]*?<\\/[^>]*textarea>/gi', python: 'r"]*>[\s\S]*?]*textarea>"', php: '/]*>[\s\S]*?<\\/[^>]*textarea>/i', java: ']*>[\s\S]*?]*textarea>' } } ]; // 合并到regexDatabase(避免重复) extraRegexPresets.forEach(item => { if (!regexDatabase[item.key]) { regexDatabase[item.key] = item; } }); /** * 自动生成旧版下拉框正则项到regexDatabase */ function populateOldVersionPreset() { const select = document.getElementById('regList'); if (!select) return; select.innerHTML = ''; // 添加自定义选项 const customOption = document.createElement('option'); customOption.value = ''; customOption.textContent = '自定义/选择常用正则'; select.appendChild(customOption); // 按分类分组 const grouped = {}; Object.keys(regexDatabase).forEach(key => { const item = regexDatabase[key]; const cat = item.category || '其他'; if (!grouped[cat]) grouped[cat] = []; grouped[cat].push({ key, title: item.title, pattern: item.patterns.javascript }); }); Object.keys(grouped).forEach(cat => { const group = document.createElement('optgroup'); group.label = cat; grouped[cat].forEach(opt => { const option = document.createElement('option'); option.value = opt.pattern; option.textContent = opt.title; group.appendChild(option); }); select.appendChild(group); }); } /** * 自适应高度的jQuery插件 - 用于旧版功能 */ if (typeof $ !== 'undefined') { $.fn.extend({ textareaAutoHeight: function (options) { this._options = { minHeight: 0, maxHeight: 100000 }; this.init = function () { for (var p in options) { this._options[p] = options[p]; } if (this._options.minHeight === 0) { this._options.minHeight = parseFloat($(this).height()); } for (var p in this._options) { if ($(this).attr(p) == null) { $(this).attr(p, this._options[p]); } } $(this).keyup(this.resetHeight).change(this.resetHeight) .focus(this.resetHeight); }; this.resetHeight = function () { var _minHeight = parseFloat($(this).attr("minHeight")); var _maxHeight = parseFloat($(this).attr("maxHeight")); $(this).height(0); var h = parseFloat(this.scrollHeight); h = h < _minHeight ? _minHeight : h > _maxHeight ? _maxHeight : h; $(this).height(h).scrollTop(h); if (h >= _maxHeight) { $(this).css("overflow-y", "scroll"); } else { $(this).css("overflow-y", "hidden"); } }; this.init(); } }); } /** * 旧版正则表达式工具类 */ var RegExpTools = (function () { "use strict"; var regElm, srcElm, rstElm, rstCount, srcBackgroundElm, srcWrapperElm, regListElm; var ID_PREFIX = 'tmp_id_'; var TAG_MATCHED = 'b'; var TAG_NOT_MATCHED = 'i'; var TR_ID_PREFIX = 'tr_' + ID_PREFIX; var _getRegExp = function (regTxt) { try { return new Function('return ' + regTxt)(); } catch (e) { return null; } }; var _buildTable = function (rstArray) { var tbl = [""]; tbl.push('') $.each(rstArray, function (i, item) { tbl.push(''); tbl.push('' + '' + ''); tbl.push(''); }); tbl.push('
序号匹配结果在原字符串中的位置
' + (i + 1) + '' + item.text + '' + item.index + '
'); return tbl.join(''); }; var _createTag = function (type, item) { var tags = []; for (var i = 0, len = item.text.length; i < len; i++) { tags.push('<' + type + ' data-id="' + ID_PREFIX + item.index + '">' + item.text.charAt(i) + ''); } return tags.join(''); }; var _blinkHighlight = function () { $('tr[id^=' + TR_ID_PREFIX + ']').click(function (e) { var index = $(this).attr('data-index'); var tags = $(TAG_MATCHED + '[data-id=' + ID_PREFIX + index + ']'); tags.animate({ opacity: 0 }, 200).delay().animate({ opacity: 1 }, 200).delay().animate({ opacity: 0 }, 200).delay().animate({ opacity: 1 }, 200); }); }; var _highlight = function (srcText, rstArray) { if (!srcText) { srcBackgroundElm.html(''); return; } var hl = []; var preIndex = 0; $.each(rstArray, function (i, item) { if (i === 0) { if (item.index === 0) { hl.push(_createTag(TAG_MATCHED, item)); } else { hl.push(_createTag(TAG_NOT_MATCHED, { index: 0, text: srcText.substring(0, item.index) })); hl.push(_createTag(TAG_MATCHED, item)); } } else { preIndex = rstArray[i - 1].index + rstArray[i - 1].text.length; hl.push(_createTag(TAG_NOT_MATCHED, { index: preIndex, text: srcText.substring(preIndex, item.index) })); hl.push(_createTag(TAG_MATCHED, item)); } }); srcBackgroundElm.html(hl.join('')); _blinkHighlight(); }; var _emptyTable = function (message) { var tbl = [""]; tbl.push(''); tbl.push(''); tbl.push('
序号匹配结果
' + message + '
'); return tbl.join(''); }; var _dealRegMatch = function (e) { if (!srcWrapperElm || !srcElm) return; srcWrapperElm.height(srcElm.height() + 24); var regTxt = regElm.val().trim(); var srcTxt = srcElm.val().trim(); if (!regTxt || !srcTxt) { rstElm.html(_emptyTable('不能匹配')); rstCount.html('0个'); _highlight(); } else { var reg = _getRegExp(regTxt); if (!reg || !reg instanceof RegExp) { rstElm.html(_emptyTable('正则表达式错误!')); rstCount.html('0个'); _highlight(); return; } var rst = []; // 用字符串的replace方法来找到匹配目标在元字符串中的准确位置 srcTxt.replace(reg, function () { var matchedTxt = arguments[0]; var txtIndex = arguments[arguments.length - 2]; rst.push({ text: matchedTxt, index: txtIndex }); }); if (!rst || !rst.length) { rstElm.html(_emptyTable('不能匹配')); rstCount.html('0个'); _highlight(); } else { rstElm.html(_buildTable(rst)); rstCount.html(rst.length + '个'); _highlight(srcElm.val(), rst); } } }; var _init = function () { regElm = $('#regText'); srcElm = $('#srcCode'); srcBackgroundElm = $('#srcBackground'); srcWrapperElm = $('#srcWrapper'); rstElm = $('#rstCode'); rstCount = $('#rstCount'); regListElm = $('#regList'); if (!regElm.length) return; // 如果元素不存在,直接返回 rstElm.html(_emptyTable('暂无输入')); // 输入框自适应高度 if (regElm.textareaAutoHeight) { regElm.textareaAutoHeight({ minHeight: 40 }); srcElm.textareaAutoHeight({ minHeight: 60 }); srcBackgroundElm.textareaAutoHeight({ minHeight: 60 }); } // 监听两个输入框的按键、paste、change事件 $('#regText,#srcCode').keyup(_dealRegMatch).change(_dealRegMatch) .bind('paste', _dealRegMatch); regListElm.change(function (e) { var reg = $(this).val(); var regTipElm = $('#regTip'); regElm.val(reg); if (!reg) { if (regTipElm.length) regTipElm.hide(); } else { if (regTipElm.length) regTipElm.show(); } _dealRegMatch(); // 触发匹配 }); }; return { init: _init }; })(); // 主要的应用程序逻辑 document.addEventListener('DOMContentLoaded', function() { initTabSwitching(); initNewVersionFeatures(); initOldVersionFeatures(); initCommonFeatures(); }); /** * 初始化Tab切换功能 */ function initTabSwitching() { const tabBtns = document.querySelectorAll('.tab-btn'); const tabContents = document.querySelectorAll('.tab-content'); // 先移除所有active类,防止多个高亮 tabBtns.forEach(btn => btn.classList.remove('active')); tabContents.forEach(content => content.classList.remove('active')); // 新增:页面加载时恢复上次选中的Tab let lastTab = localStorage.getItem('fh-regexp-last-tab'); let found = false; if (lastTab) { tabBtns.forEach(btn => { if (btn.getAttribute('data-tab') === lastTab) { btn.classList.add('active'); const tabContent = document.getElementById(lastTab); if (tabContent) tabContent.classList.add('active'); found = true; } }); } // 如果没有记录或找不到,默认激活第一个 if (!found && tabBtns.length > 0 && tabContents.length > 0) { tabBtns[0].classList.add('active'); tabContents[0].classList.add('active'); } tabBtns.forEach(btn => { btn.addEventListener('click', function() { const targetTab = this.getAttribute('data-tab'); // 移除所有active类 tabBtns.forEach(b => b.classList.remove('active')); tabContents.forEach(c => c.classList.remove('active')); // 添加active类到当前选中的 this.classList.add('active'); document.getElementById(targetTab).classList.add('active'); // 新增:切换Tab时保存当前Tab到localStorage localStorage.setItem('fh-regexp-last-tab', targetTab); }); }); } /** * 初始化新版功能 */ function initNewVersionFeatures() { populateVisualRegexPreset(); initVisualDebugger(); initRegexSearch(); initRegexItems(); initModal(); } /** * 初始化旧版功能 */ function initOldVersionFeatures() { // 确保jQuery已加载后再初始化旧版功能 if (typeof $ !== 'undefined') { // 自动生成下拉框内容 populateOldVersionPreset(); RegExpTools.init(); } } /** * 初始化通用功能 */ function initCommonFeatures() { // 打赏和其他工具链接 const donateLink = document.getElementById('donateLink'); const otherToolsBtn = document.getElementById('btnOtherTools'); if (donateLink) { donateLink.addEventListener('click', function(event) { event.preventDefault(); event.stopPropagation(); chrome.runtime.sendMessage({ type: 'fh-dynamic-any-thing', thing: 'open-donate-modal', params: { toolName: 'regexp' } }); }); } if (otherToolsBtn) { otherToolsBtn.addEventListener('click', function(event) { event.preventDefault(); event.stopPropagation(); chrome.runtime.openOptionsPage(); }); } } /** * 填充可视化调试器的预设正则表达式 */ function populateVisualRegexPreset() { const select = document.getElementById('visualRegexPreset'); if (!select) return; // 先清空 select.innerHTML = ''; // 添加自定义选项 const customOption = document.createElement('option'); customOption.value = ''; customOption.textContent = '自定义/选择常用正则'; select.appendChild(customOption); // 按分类分组 const grouped = {}; Object.keys(regexDatabase).forEach(key => { const item = regexDatabase[key]; const cat = item.category || '其他'; if (!grouped[cat]) grouped[cat] = []; grouped[cat].push({ key, title: item.title }); }); Object.keys(grouped).forEach(cat => { const group = document.createElement('optgroup'); group.label = cat; grouped[cat].forEach(opt => { const option = document.createElement('option'); option.value = opt.key; option.textContent = opt.title; group.appendChild(option); }); select.appendChild(group); }); // 事件绑定逻辑不变 select.addEventListener('change', function() { const selectedKey = this.value; if (selectedKey && regexDatabase[selectedKey]) { const pattern = regexDatabase[selectedKey].patterns.javascript; // 去掉前后的斜杠和标志 const cleanPattern = pattern.replace(/^\/?|\/[gimuy]*$/g, ''); document.getElementById('visualRegex').value = cleanPattern; } }); } /** * 初始化可视化调试器 */ function initVisualDebugger() { const testBtn = document.getElementById('visualTestBtn'); const regexInput = document.getElementById('visualRegex'); const testTextArea = document.getElementById('visualTestText'); const resultDiv = document.getElementById('visualResult'); const errorMsg = document.getElementById('visualErrorMsg'); const flagCheckboxes = document.querySelectorAll('.flag-btn input[type="checkbox"]'); if (!testBtn) return; // 标志复选框事件 flagCheckboxes.forEach(checkbox => { checkbox.addEventListener('change', updateFlagsInputFromCheckbox); }); function updateFlagsInputFromCheckbox() { const flags = Array.from(flagCheckboxes) .filter(cb => cb.checked) .map(cb => cb.value) .join(''); document.getElementById('visualFlags').value = flags; } testBtn.addEventListener('click', doVisualTest); regexInput.addEventListener('keyup', function(e) { if (e.key === 'Enter') doVisualTest(); }); testTextArea.addEventListener('keyup', function(e) { if (e.key === 'Enter' && e.ctrlKey) doVisualTest(); }); // 新增:输入时自动实时匹配 regexInput.addEventListener('input', doVisualTest); testTextArea.addEventListener('input', doVisualTest); function doVisualTest() { let regexStr = regexInput.value.trim(); const testText = testTextArea.value.trim(); errorMsg.textContent = ''; if (!regexStr) { errorMsg.textContent = '请输入正则表达式'; return; } if (!testText) { errorMsg.textContent = '请输入要测试的文本'; return; } // 新增:支持 /pattern/flags 写法 let pattern = regexStr; let flags = Array.from(flagCheckboxes) .filter(cb => cb.checked) .map(cb => cb.value) .join(''); // 如果是 /pattern/flags 形式 const regSlash = /^\/(.+)\/([gimsuy]*)$/; const match = regexStr.match(regSlash); if (match) { pattern = match[1]; flags = match[2]; } try { const regex = new RegExp(pattern, flags); const matches = []; let match; if (flags.includes('g')) { while ((match = regex.exec(testText)) !== null) { matches.push({ match: match[0], index: match.index, groups: match.slice(1) }); if (match.index === regex.lastIndex) break; } } else { match = regex.exec(testText); if (match) { matches.push({ match: match[0], index: match.index, groups: match.slice(1) }); } } displayVisualResults(matches, testText, regex); } catch (e) { errorMsg.textContent = '正则表达式语法错误: ' + e.message; resultDiv.innerHTML = ''; } } function displayVisualResults(matches, text, regex) { if (matches.length === 0) { resultDiv.innerHTML = '
没有找到匹配项
'; return; } let html = `
找到 ${matches.length} 个匹配项
`; // 高亮显示文本 let highlightedText = text; let offset = 0; matches.forEach((match, index) => { const startTag = ``; const endTag = ''; const insertPos = match.index + offset; highlightedText = highlightedText.slice(0, insertPos) + startTag + highlightedText.slice(insertPos, insertPos + match.match.length) + endTag + highlightedText.slice(insertPos + match.match.length); offset += startTag.length + endTag.length; }); html += `
${highlightedText}
`; // 匹配详情 html += '
'; matches.forEach((match, index) => { html += `
匹配 ${index + 1}: "${match.match}" (位置: ${match.index}-${match.index + match.match.length - 1})`; if (match.groups.length > 0) { html += '
分组: '; match.groups.forEach((group, groupIndex) => { if (group !== undefined) { html += `$${groupIndex + 1}: "${group}" `; } }); html += '
'; } html += '
'; }); html += '
'; resultDiv.innerHTML = html; } } /** * 初始化正则表达式搜索功能 */ function initRegexSearch() { const searchInput = document.getElementById('regexSearch'); if (!searchInput) return; searchInput.addEventListener('input', function() { const searchTerm = this.value.toLowerCase(); const regexItems = document.querySelectorAll('.regex-item'); const categories = document.querySelectorAll('.category-section'); if (!searchTerm) { // 恢复所有分类和按钮显示 regexItems.forEach(item => { item.style.display = ''; }); categories.forEach(cat => { cat.style.display = ''; }); return; } // 原有筛选逻辑 regexItems.forEach(item => { const text = item.textContent.toLowerCase(); const category = item.closest('.category-section'); if (text.includes(searchTerm)) { item.style.display = 'block'; if (category) category.style.display = ''; } else { item.style.display = 'none'; } }); // 隐藏没有可见项的分类 categories.forEach(category => { const visibleItems = category.querySelectorAll('.regex-item[style*="block"], .regex-item:not([style*="none"])'); if (visibleItems.length === 0 && searchTerm) { category.style.display = 'none'; } else if (!searchTerm) { category.style.display = ''; } }); }); } /** * 初始化正则表达式项目点击事件和新版分类按钮区域 * 自动遍历regexDatabase,按category分组渲染所有分类和按钮 */ function initRegexItems() { // 新版分类按钮区域容器 const container = document.getElementById('regexCategoryContainer'); if (!container) return; container.innerHTML = ''; // 按分类分组 const grouped = {}; Object.keys(regexDatabase).forEach(key => { const item = regexDatabase[key]; const cat = item.category || '其他'; if (!grouped[cat]) grouped[cat] = []; grouped[cat].push({ key, title: item.title }); }); // 渲染每个分类 Object.keys(grouped).forEach(cat => { // 分类标题 const section = document.createElement('div'); section.className = 'category-section'; section.innerHTML = `
${cat}
`; // 分类按钮区 const btnGroup = document.createElement('div'); btnGroup.className = 'category-btn-group'; grouped[cat].forEach(opt => { const btn = document.createElement('button'); btn.className = 'regex-item'; btn.setAttribute('data-regex-id', opt.key); btn.textContent = opt.title; // 点击弹出模态框 btn.addEventListener('click', function() { if (regexDatabase[opt.key]) { showRegexModal(regexDatabase[opt.key]); } else { alert('该正则表达式暂未收录,敬请期待!'); } }); btnGroup.appendChild(btn); }); section.appendChild(btnGroup); container.appendChild(section); }); } /** * 初始化模态框 */ function initModal() { const modal = document.getElementById('regexModal'); const closeBtn = document.querySelector('.close'); if (!modal) return; closeBtn.addEventListener('click', function() { modal.style.display = 'none'; }); window.addEventListener('click', function(event) { if (event.target === modal) { modal.style.display = 'none'; } }); // 复制按钮事件 document.querySelectorAll('.copy-btn').forEach(btn => { btn.addEventListener('click', function() { const targetId = this.getAttribute('data-target'); const targetElement = document.getElementById(targetId); if (targetElement) { const textToCopy = targetElement.textContent; if (navigator.clipboard) { navigator.clipboard.writeText(textToCopy).then(() => { this.textContent = '已复制!'; setTimeout(() => { this.textContent = '复制'; }, 2000); }); } else { // 降级方案 const textArea = document.createElement('textarea'); textArea.value = textToCopy; document.body.appendChild(textArea); textArea.select(); document.execCommand('copy'); document.body.removeChild(textArea); this.textContent = '已复制!'; setTimeout(() => { this.textContent = '复制'; }, 2000); } } }); }); } /** * 显示正则表达式模态框 */ function showRegexModal(regexData) { const modal = document.getElementById('regexModal'); const modalTitle = document.getElementById('modalTitle'); const jsRegex = document.getElementById('jsRegex'); const pythonRegex = document.getElementById('pythonRegex'); const phpRegex = document.getElementById('phpRegex'); const javaRegex = document.getElementById('javaRegex'); const regexDescription = document.getElementById('regexDescription'); modalTitle.textContent = regexData.title; jsRegex.textContent = regexData.patterns.javascript; pythonRegex.textContent = regexData.patterns.python; phpRegex.textContent = regexData.patterns.php; javaRegex.textContent = regexData.patterns.java; regexDescription.textContent = regexData.description; modal.style.display = 'block'; } // 工具函数 function parsePatternAndFlags(input) { const match = input.match(/^\/(.+)\/([gimuy]*)$/); if (match) { return { pattern: match[1], flags: match[2] }; } return { pattern: input, flags: '' }; } function highlightMatchesV2(text, regex) { return text.replace(regex, '$&'); } function loadPatchHotfix() { // 可以在这里添加热修复逻辑 console.log('正则表达式工具已加载'); } // jQuery自适应高度插件(用于旧版功能) if (typeof $ !== 'undefined') { $.fn.extend({ textareaAutoHeight: function (options) { this._options = { minHeight: 0, maxHeight: 100000 }; this.init = function () { for (var p in options) { this._options[p] = options[p]; } if (this._options.minHeight === 0) { this._options.minHeight = parseFloat($(this).height()); } for (var p in this._options) { if ($(this).attr(p) == null) { $(this).attr(p, this._options[p]); } } $(this).keyup(this.resetHeight).change(this.resetHeight) .focus(this.resetHeight); }; this.resetHeight = function () { var _minHeight = parseFloat($(this).attr("minHeight")); var _maxHeight = parseFloat($(this).attr("maxHeight")); $(this).height(0); var h = parseFloat(this.scrollHeight); h = h < _minHeight ? _minHeight : h > _maxHeight ? _maxHeight : h; $(this).height(h).scrollTop(h); if (h >= _maxHeight) { $(this).css("overflow-y", "scroll"); } else { $(this).css("overflow-y", "hidden"); } }; this.init(); } }); } // 旧版正则表达式工具类 var RegExpTools = (function () { "use strict"; var regElm, srcElm, rstElm, rstCount, srcBackgroundElm, srcWrapperElm, regListElm; var ID_PREFIX = 'tmp_id_'; var TAG_MATCHED = 'b'; var TAG_NOT_MATCHED = 'i'; var TR_ID_PREFIX = 'tr_' + ID_PREFIX; var _getRegExp = function (regTxt) { try { return new Function('return ' + regTxt)(); } catch (e) { return null; } }; var _buildTable = function (rstArray) { var tbl = [""]; tbl.push('') $.each(rstArray, function (i, item) { tbl.push(''); tbl.push('' + '' + ''); tbl.push(''); }); tbl.push('
序号匹配结果在原字符串中的位置
' + (i + 1) + '' + item.text + '' + item.index + '
'); return tbl.join(''); }; var _createTag = function (type, item) { var tags = []; for (var i = 0, len = item.text.length; i < len; i++) { tags.push('<' + type + ' data-id="' + ID_PREFIX + item.index + '">' + item.text.charAt(i) + ''); } return tags.join(''); }; var _blinkHighlight = function () { $('tr[id^=' + TR_ID_PREFIX + ']').click(function (e) { var index = $(this).attr('data-index'); var tags = $(TAG_MATCHED + '[data-id=' + ID_PREFIX + index + ']'); tags.animate({ opacity: 0 }, 200).delay().animate({ opacity: 1 }, 200).delay().animate({ opacity: 0 }, 200).delay().animate({ opacity: 1 }, 200); }); }; var _highlight = function (srcText, rstArray) { if (!srcText) { srcBackgroundElm.html(''); return; } var hl = []; var preIndex = 0; $.each(rstArray, function (i, item) { if (i === 0) { if (item.index === 0) { hl.push(_createTag(TAG_MATCHED, item)); } else { hl.push(_createTag(TAG_NOT_MATCHED, { index: 0, text: srcText.substring(0, item.index) })); hl.push(_createTag(TAG_MATCHED, item)); } } else { preIndex = rstArray[i - 1].index + rstArray[i - 1].text.length; hl.push(_createTag(TAG_NOT_MATCHED, { index: preIndex, text: srcText.substring(preIndex, item.index) })); hl.push(_createTag(TAG_MATCHED, item)); } }); srcBackgroundElm.html(hl.join('')); _blinkHighlight(); }; var _emptyTable = function (message) { var tbl = [""]; tbl.push(''); tbl.push(''); tbl.push('
序号匹配结果
' + message + '
'); return tbl.join(''); }; var _dealRegMatch = function (e) { if (!srcWrapperElm || !srcElm) return; srcWrapperElm.height(srcElm.height() + 24); var regTxt = regElm.val().trim(); var srcTxt = srcElm.val().trim(); if (!regTxt || !srcTxt) { rstElm.html(_emptyTable('不能匹配')); rstCount.html('0个'); _highlight(); } else { var reg = _getRegExp(regTxt); if (!reg || !reg instanceof RegExp) { rstElm.html(_emptyTable('正则表达式错误!')); rstCount.html('0个'); _highlight(); return; } var rst = []; // 用字符串的replace方法来找到匹配目标在元字符串中的准确位置 srcTxt.replace(reg, function () { var matchedTxt = arguments[0]; var txtIndex = arguments[arguments.length - 2]; rst.push({ text: matchedTxt, index: txtIndex }); }); if (!rst || !rst.length) { rstElm.html(_emptyTable('不能匹配')); rstCount.html('0个'); _highlight(); } else { rstElm.html(_buildTable(rst)); rstCount.html(rst.length + '个'); _highlight(srcElm.val(), rst); } } }; var _init = function () { regElm = $('#regText'); srcElm = $('#srcCode'); srcBackgroundElm = $('#srcBackground'); srcWrapperElm = $('#srcWrapper'); rstElm = $('#rstCode'); rstCount = $('#rstCount'); regListElm = $('#regList'); if (!regElm.length) return; // 如果元素不存在,直接返回 rstElm.html(_emptyTable('暂无输入')); // 监听两个输入框的按键、paste、change事件 $('#regText,#srcCode').keyup(_dealRegMatch).change(_dealRegMatch) .bind('paste', _dealRegMatch); regListElm.change(function (e) { var reg = $(this).val(); var regTipElm = $('#regTip'); regElm.val(reg); if (!reg) { if (regTipElm.length) regTipElm.hide(); } else { if (regTipElm.length) regTipElm.show(); } _dealRegMatch(); // 触发匹配 }); }; return { init: _init }; })();