Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 | 9x 9x 9x 10x 10x 10x 82x 48x 48x 48x 36x 20x 12x 24x 12x 14x 6x 4x 6x 1x 4x 6x 6x 12x 9x 9x 9x 9x 171x 171x 34x 14x 20x 14x 34x 137x 9x 8x 8x 80x 80x 62x 62x 8x 8x 80x 70x 70x 70x 21x 16x 16x 16x 14x 14x 14x 42x 14x 15x 11x 11x 33x 11x 7x 14x 10x 10x 12x 12x 12x 12x 328x 12x 11x 145x 11x 11x 11x 170x 11x 45x 45x 42x 42x 22x 42x 42x 30x 1x | /**
* 获取指定的 querystring 中指定 name 的 value
* @param {String} name 查询名
* @param {String} querystring 查询到的值
* @return {String|undefined} 查询到返回String,没有返回undefined
*/
function query (name, querystring) {
if (name && name.match(/(?:http[s]?:|^)\/{2}.*?(?:\.)/)) name = encodeURIComponent(name) // 仅编码Url
const r = (querystring + '').match(new RegExp('(?:^|&)\\s*?' + name + '\\s*=\\s*(.*?)\\s*(?:&|$)'))
return r === null ? undefined : decodeURIComponent(r[1])
}
/**
* 序列化对象,把对象转成URL查询字符串
* @param {Object} data 对象
* @return {String} 返回查询字符串
*/
function serialize (data) {
let queryString = []
Object.keys(data || {}).forEach(key => typeof key === 'string' && queryString.push(encodeURIComponent(key) + '=' + encodeURIComponent(typeof data[key] === 'object' ? JSON.stringify(data[key]) : data[key])))
return queryString.join('&')
}
/**
* 根据选择器查找 DOM
* @param {String} selector 选择器名称,用法同CSS选择器
* @return {DOM|Null|Array} 群组选择器(含,)返回Array。其他选择器,找到返回第一个DOM,没有返回Null
*/
function $ (selector) {
if (!selector) return null
let selectorStr = (selector + '').replace(/,$/, '') , i = -1
try {
const nComma = selectorStr.split(',').length, r = document.querySelectorAll(selectorStr), res = []
if (nComma > 1) {
while (++i < nComma && r[i]) res[i] = r[i]
return res
}
return r[0] || null
} catch (e) {
return null
}
}
/**
* 删除 DOM 节点
* @param {DOM|ArrayLike} node 要删除的节点或节点列表
* @return {DOM|ArrayLike} 返回值被删除的节点或节点列表,与传参 node 相同
*/
function removeNode (node) {
return (node && node.length ? Array.from(node) : [node]).map(node => node?.parentNode?.removeChild(node)), node
}
/**
* 在 target 节点之后插入 node 节点
* @param {DOM|ArrayLike} node 要插入的节点或节点列表
* @param {DOM|Null} target 插入目标节点的后方,若目标节点不存在,则插入父节点的末尾
* @return {DOM|ArrayLike} 返回被插入的节点或节点列表,与传参 node 相同
*/
function insertAfter (node, target) {
if (!node || node.length === 0) return null
let fragment = document.createDocumentFragment()
if (node && node.length) for (let i = 0; i < node.length; i++) fragment.appendChild(node[i])
else fragment = node
return (target?.parentNode || document.body).insertBefore(fragment, target?.nextSibling || null), node
}
/**
* 添加类名
* @param {DOM|ArrayLike} node 要添加类名的节点或节点列表
* @param {String|Array} className 要添加的类名
* @return {DOM|ArrayLike} 返回添加类名的节点或节点列表,与传参 node 相同
*/
function addClass (node, className) {
return (node && node.length ? Array.from(node) : [node]).map(node => node?.classList.add(className)), node
}
/**
* 移除类名
* @param {DOM|Array<Dom>} node 要移除类名的节点或节点列表
* @param {String|Array} className 要移除的类名
* @return {DOM|ArrayLike} 返回移除类名的节点或节点列表,与传参 node 相同
*/
function removeClass (node, className) {
return (node && node.length ? Array.from(node) : [node]).map(node => node?.classList.remove(className)), node
}
/**
* 获取绝对路径
* @param {String} url // 相对路径
* @return {String} // 返回路径的值与浏览器处理相对路径相同
*/
function getAbsoluteUrl (url) {
if (!url) return location.href
url += ''
const { origin, pathname } = location
const stack = pathname.replace(/^\/|\/$/g, '').split('/')
for (let i = 0, w = ''; i <= url.length; i++) {
const s = url[i]
if (s === '/' || s === void 0) {
if (w.substring(0, 2) === '..') {
stack.pop()
} else if (w !== '.') {
i === 0 ? stack.length = 0 : stack.push(w)
}
w = ''
} else w += s
}
return origin + '/' + stack.join('/')
}
/**
* 防抖
* @param {Function} callback 回调函数
* @param {Number} time 延迟时间
* @return {Function} 防抖的函数
*/
function debounce (callback, time) {
let timer = null
return function (...args) {
if (timer) clearTimeout(timer)
timer = setTimeout(() => {
timer = null
callback.apply(this, args)
}, time + '' | 0 || 1000 / 60)
}
}
/**
* 节流
* @param {Function} callback 回调函数
* @param {Number} time 时间间隔
* @return {Function} 节流的函数
*/
function throttle (callback, time) {
let timer = null
return function (...args) {
if (timer) return
timer = setTimeout(() => {
timer = null
callback.apply(this, args)
}, time + '' | 0 || 1000 / 60)
}
}
/**
* 根据索引移出数组的某一项
* @param {Number} index 要删除的索引。正负和边界处理与Array.prototype.splice的第一参数相同
* @param {Array} arr 要操作的数组
* @return {Array} 返回操作后的数组
*/
function removeItemByIndex (index, arr) {
if (!arr || !arr.length || typeof index === 'bigint') return arr
const n = arr.length
index |= 0
if (index >= n) return arr
if (index < 0) index = index <= -n ? 0 : index % n + n
const r = Array(n - 1)
for (let i = 0, j = 0; i < n; i++)
if (i !== index) r[j++] = arr[i]
return r
}
/**
* 根据值移出数组的某一项
* @param {Number} index 要删除的值。严格相等匹配与Array.prototype.indexOf相同
* @param {Array} arr 要操作的数组
* @return {Array} 返回操作后的数组
*/
function removeItemByValue (value, arr) {
if (!arr || !arr.length) return arr
const n = arr.length, r = []
for (let i = 0, j = 0; i < n; i++)
if (arr[i] !== value) r[j++] = arr[i]
return r
}
/**
* JSON.stringify:保留对象中undefined、BigInt值和Function、Symbol结构
* @param {Object} obj JS对象
* @return {String} 返回序列化后的字符串
*/
function stringifyJSON (obj) {
const s = new Set(['undefined', 'bigint', 'function', 'symbol'])
return JSON.stringify(obj, (_, v) => s.has(typeof v) ? String(v) : v)
}
/**
* 富文本 → 纯文本
* @param {String} html 富文本字符串
* @return {String} 返回纯文本字符串
* ALL HTML TAG List Source:https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element
*/
function delHtmlTag (html) {
const TAG = 'html|base|head|link|meta|style|title|body|address|article|aside|footer|header|h1|h2|h3|h4|h5|h6|hgroup|main|nav|section|blockquote|dd|dir|div|dl|dt|figcaption|figure|hr|li|main|ol|p|pre|ul|a|abbr|b|bdi|bdo|br|cite|code|data|dfn|em|i|kbd|mark|q|rb|rp|rt|rtc|ruby|s|samp|small|span|strong|sub|sup|time|tt|u|var|wbr|area|audio|img|map|track|video|applet|embed|iframe|noembed|object|param|picture|source|canvas|noscript|script|del|ins|caption|col|colgroup|table|tbody|td|tfoot|th|thead|tr|button|datalist|fieldset|form|input|label|legend|meter|optgroup|option|output|progress|select|textarea|details|dialog|menu|menuitem|summary|content|element|shadow|slot|template|acronym|applet|basefont|bgsound|big|blink|center|command|content|dir|element|font|frame|frameset|image|isindex|keygen|listing|marquee|menuitem|multicol|nextid|nobr|noembed|noframes|plaintext|shadow|spacer|strike|tt|xmp'
return ((html || '') + '').replace(new RegExp('<\\/?(' + TAG + ')([\\s\\S]*?)>((?!<)>)*', 'ig'), '')
}
/**
* 转义:HTML → HTMLEntites
* @param {String} html 富文本字符串
* @return {String} 返回转义后的字符串,转换非ASCII编码字符
*/
function htmlEncode (html) {
const h = { "10": "<br/>", "32": " ", "34": """, "38": "&", "39": "'", "47": "/", "60": "<", "62": ">", "162": "¢", "192": "À", "193": "Á", "194": "Â", "195": "Ã", "196": "Ä", "197": "Å", "198": "Æ", "199": "Ç", "200": "È", "201": "É", "202": "Ê", "203": "Ë", "204": "Ì", "205": "Í", "206": "Î", "207": "Ï", "208": "Ð", "209": "Ñ", "210": "Ò", "211": "Ó", "212": "Ô", "213": "Õ", "214": "Ö", "216": "Ø", "217": "Ù", "218": "Ú", "219": "Û", "220": "Ü", "221": "Ý", "222": "Þ", "223": "ß", "224": "à", "225": "á", "226": "â", "227": "ã", "228": "ä", "229": "å", "230": "æ", "231": "ç", "232": "è", "233": "é", "234": "ê", "235": "ë", "236": "ì", "237": "í", "238": "î", "239": "ï", "240": "ð", "241": "ñ", "242": "ò", "243": "ó", "244": "ô", "245": "õ", "246": "ö", "248": "ø", "249": "ù", "250": "ú", "251": "û", "252": "ü", "253": "ý", "254": "þ", "255": "ÿ" }
html = (html || '') + ''
let r = '', t, i = -1
while (++i < html.length)
r += h[t = html.charCodeAt(i)] || (t > 127 ? `&#x${t.toString(16).padStart(4, 0)};` : html[i])
return r
}
/**
* 转义:JavaScript代码 → 字符串
* @param {String} js JavaScript代码
* @return {String} 返回转义后的字符串,转换非ASCII编码字符
*/
function javaScriptEncode (js) {
const h = { 8: '\\b', 9: '\\t', 10: '\\n', 12: '\\f', 13: '\\r', 34: '\\"', 38: '\\&', 39: '\\\'', 47: '\\x2f', 60: '\\x3c', 62: '\\x3e', 92: '\\\\' }
const isW = t => t > 47 && t < 58 || t > 64 && t < 91 || t > 96 && t < 123 // 大小写字母 + 数字(不含_)
js = (js || '') + ''
let r = '', t, i = -1
while (++i < js.length)
r += h[t = js.charCodeAt(i)] || (t > 127 ? `\\u${t.toString(16).padStart(4, 0)}` : isW(t) ? js[i] : `\\x${t.toString(16)}`)
return r
}
/**
* @param {String} referer 来源
* @param {Boolean} nullable 允许为空:默认允许
* @param {Array}} whiteList 白名单:域名 + 端口 白名单(不含http和https)
*/
function isRefererValid (referer, { nullable = true, whiteList = [] }) {
const punycode = require('./punycode')
return nullable && !referer
|| RegExp('^http[s]?://(' + whiteList.map(v => {
let [ host, port ] = v.split(':')
if (port === void 0) port = '(443|80)'
else if (port === '*') port = '\\d+'
host = punycode.ToASCII(host).replace(/\*+(?!$)/g, '\\w+').replace(/\*+$/, '')
return host + ':' + port
}).join('|') + ')(\\?|/|$)').test(
referer.replace(/(?<=^http(s)?:\/\/[^:]*?)(\/|\?|$)/, (_, ssl, end) => ':' + (ssl ? 443 : 80) + end)
)
}
module.exports = { query, serialize, $, removeNode, insertAfter, addClass, removeClass, getAbsoluteUrl, debounce, throttle, removeItemByIndex, removeItemByValue, stringifyJSON, delHtmlTag, htmlEncode, javaScriptEncode, isRefererValid } |