1/** 2 * å·¥å ·å½æ°æ¨¡å 3 * æä¾å®å ¨å¤çåéç¨å·¥å ·å½æ° 4 */ 5 6'use strict'; 7 8/** 9 * HTML ç¹æ®åç¬¦è½¬ä¹ 10 * 鲿¢ XSS æ»å»ï¼å°ç¹æ®å符转æ¢ä¸º HTML å®ä½ 11 * @param {string} text - éè¦è½¬ä¹çææ¬ 12 * @returns {string} - 转ä¹åçå®å ¨ææ¬ 13 */ 14function escapeHtml(text) { 15 if (text === null || text === undefined) { 16 return ''; 17 } 18 var str = String(text); 19 var div = document.createElement('div'); 20 div.textContent = str; 21 return div.innerHTML; 22} 23 24/** 25 * å建å®å ¨ç DOM å ç´ 26 * é¿å ä½¿ç¨ innerHTMLï¼ä½¿ç¨ createElement å建å ç´ 27 * @param {string} tag - HTML æ ç¾å 28 * @param {Object} attributes - å ç´ å±æ§å¯¹è±¡ 29 * @param {string} textContent - ææ¬å 容ï¼ä¼èªå¨è½¬ä¹ï¼ 30 * @returns {HTMLElement} - å建çå ç´ 31 */ 32function createSafeElement(tag, attributes, textContent) { 33 var element = document.createElement(tag); 34 35 // è®¾ç½®å±æ§ 36 for (var key in attributes) { 37 if (attributes.hasOwnProperty(key)) { 38 var value = attributes[key]; 39 if (key === 'className') { 40 element.className = value; 41 } else if (key === 'style' && typeof value === 'object') { 42 for (var styleKey in value) { 43 if (value.hasOwnProperty(styleKey)) { 44 element.style[styleKey] = value[styleKey]; 45 } 46 } 47 } else if (key.startsWith('on') && typeof value === 'function') { 48 // äºä»¶å¤çå¨ 49 var eventName = key.slice(2).toLowerCase(); 50 element.addEventListener(eventName, value); 51 } else if (key === 'data' && typeof value === 'object') { 52 // data-* 屿§ 53 for (var dataKey in value) { 54 if (value.hasOwnProperty(dataKey)) { 55 element.dataset[dataKey] = value[dataKey]; 56 } 57 } 58 } else { 59 element.setAttribute(key, value); 60 } 61 } 62 } 63 64 // è®¾ç½®ææ¬å 容ï¼èªå¨è½¬ä¹ï¼ 65 if (textContent) { 66 element.textContent = textContent; 67 } 68 69 return element; 70} 71 72/** 73 * éªè¯åè¯ ID æ ¼å¼ 74 * ç¡®ä¿ ID 符å ebv2_æ°å çæ ¼å¼ 75 * @param {string} id - åè¯ ID 76 * @returns {boolean} - æ¯å¦ææ 77 */ 78function isValidWordId(id) { 79 if (typeof id !== 'string') { 80 return false; 81 } 82 return /^ebv2_\d+$/.test(id); 83} 84 85/** 86 * éªè¯ç¨æ·è¿åº¦æ°æ® 87 * @param {Object} progress - ç¨æ·è¿åº¦å¯¹è±¡ 88 * @returns {boolean} - æ¯å¦ææ 89 */ 90function isValidProgress(progress) { 91 if (!progress || typeof progress !== 'object') { 92 return false; 93 } 94 95 // æ£æ¥ level æ¯å¦ä¸º 0-5 çæ´æ° 96 if (!Number.isInteger(progress.level) || progress.level < 0 || progress.level > 5) { 97 return false; 98 } 99 100 // æ£æ¥ nextReview æ¯å¦ä¸ºæ°å 101 if (typeof progress.nextReview !== 'number') { 102 return false; 103 } 104 105 return true; 106} 107 108/** 109 * 使ç¨å å¯å®å ¨çéæºæ°çæå¨ 110 * æ¿ä»£ä¸å®å ¨ç Math.random() 111 * @param {number} max - æå¤§å¼ï¼ä¸å å«ï¼ 112 * @returns {number} - 0 å° max-1 ä¹é´çéæºæ´æ° 113 */ 114function getSecureRandomInt(max) { 115 if (max <= 0 || !Number.isInteger(max)) { 116 throw new Error('max must be a positive integer'); 117 } 118 119 var array = new Uint32Array(1); 120 crypto.getRandomValues(array); 121 return array[0] % max; 122} 123 124/** 125 * æ´çç®æ³ï¼Fisher-Yatesï¼ 126 * 使ç¨å å¯å®å ¨çéæºæ° 127 * @param {Array} array - éè¦æ´ççæ°ç» 128 * @returns {Array} - æ´çåçæ°æ°ç»ï¼ä¸ä¿®æ¹åæ°ç»ï¼ 129 */ 130function shuffleArray(array) { 131 if (!Array.isArray(array)) { 132 throw new Error('Input must be an array'); 133 } 134 135 // åå»ºå¯æ¬ï¼é¿å 使ç¨å±å¼è¿ç®ç¬¦ï¼ 136 var result = array.slice(); 137 for (var i = result.length - 1; i > 0; i--) { 138 var j = getSecureRandomInt(i + 1); 139 // 交æ¢ï¼é¿å 使ç¨è§£æèµå¼ï¼ 140 var temp = result[i]; 141 result[i] = result[j]; 142 result[j] = temp; 143 } 144 return result; 145} 146 147/** 148 * 鲿彿° 149 * @param {Function} func - éè¦é²æç彿° 150 * @param {number} wait - çå¾ æ¶é´ï¼æ¯«ç§ï¼ 151 * @returns {Function} - 鲿åç彿° 152 */ 153function debounce(func, wait) { 154 if (wait === undefined) wait = 300; 155 var timeout;
156 return function executedFunction() { 157 var args = Array.prototype.slice.call(arguments); 158 var later = function() { 159 clearTimeout(timeout); 160 func.apply(this, args); 161 }; 162 clearTimeout(timeout); 163 timeout = setTimeout(later, wait); 164 }; 165} 166 167/** 168 * èæµå½æ° 169 * @param {Function} func - éè¦èæµç彿° 170 * @param {number} limit - éå¶æ¶é´ï¼æ¯«ç§ï¼ 171 * @returns {Function} - èæµåç彿° 172 */ 173function throttle(func, limit) { 174 if (limit === undefined) limit = 300; 175 var inThrottle; 176 return function executedFunction() { 177 var args = Array.prototype.slice.call(arguments); 178 if (!inThrottle) { 179 func.apply(this, args); 180 inThrottle = true; 181 setTimeout(function() { inThrottle = false; }, limit); 182 } 183 }; 184} 185 186/** 187 * å®å ¨å°è§£æ JSON 188 * é¿å è§£æé误导è´ç¨åºå´©æº 189 * @param {string} jsonString - JSON å符串 190 * @param {*} defaultValue - è§£æå¤±è´¥æ¶çé»è®¤å¼ 191 * @returns {*} - è§£æç»ææé»è®¤å¼ 192 */ 193function safeJsonParse(jsonString, defaultValue) { 194 if (defaultValue === undefined) defaultValue = null; 195 try { 196 return JSON.parse(jsonString); 197 } catch (e) { 198 console.warn('[Utils] JSON parse error:', e.message); 199 return defaultValue; 200 } 201} 202 203/** 204 * å®å ¨å°åºåå为 JSON 205 * é¿å 循ç¯å¼ç¨å¯¼è´é误 206 * @param {*} value - éè¦åºååçå¼ 207 * @param {string} defaultValue - åºåå失败æ¶çé»è®¤å¼ 208 * @returns {string} - JSON å符串æé»è®¤å¼ 209 */ 210function safeJsonStringify(value, defaultValue) { 211 if (defaultValue === undefined) defaultValue = '{}'; 212 try { 213 return JSON.stringify(value); 214 } catch (e) { 215 console.warn('[Utils] JSON stringify error:', e.message); 216 return defaultValue; 217 } 218} 219 220/** 221 * æ ¼å¼åæ¥æ 222 * @param {number} timestamp - æ¶é´æ³ 223 * @returns {string} - æ ¼å¼ååçæ¥æå符串 224 */ 225function formatDate(timestamp) { 226 if (!timestamp) return '-'; 227 var date = new Date(timestamp); 228 return date.toLocaleDateString('zh-CN'); 229} 230 231/** 232 * æ ¼å¼åæ¶é´ 233 * @param {number} timestamp - æ¶é´æ³ 234 * @returns {string} - æ ¼å¼ååçæ¶é´å符串 235 */ 236function formatTime(timestamp) { 237 if (!timestamp) return '-'; 238 var date = new Date(timestamp); 239 return date.toLocaleString('zh-CN'); 240} 241 242// å¯¼åºæ¨¡å 243if (typeof module !== 'undefined' && module.exports) { 244 module.exports = { 245 escapeHtml: escapeHtml, 246 createSafeElement: createSafeElement, 247 isValidWordId: isValidWordId, 248 isValidProgress: isValidProgress, 249 getSecureRandomInt: getSecureRandomInt, 250 shuffleArray: shuffleArray, 251 debounce: debounce, 252 throttle: throttle, 253 safeJsonParse: safeJsonParse, 254 safeJsonStringify: safeJsonStringify, 255 formatDate: formatDate, 256 formatTime: formatTime 257 }; 258}
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.