nv-basic-bw 1.0.11 → 1.0.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/DIST/nv-basci-bw.js +23 -13
- package/attr.js +1 -1
- package/calc.js +144 -0
- package/cls-accessor.js +1 -1
- package/code.js +181 -0
- package/com.sh +1 -0
- package/const.js +48 -0
- package/dnld.js +90 -0
- package/ele.js +365 -0
- package/index.js +21 -593
- package/limit.js +103 -0
- package/nd.js +390 -0
- package/package.json +2 -2
- package/util.js +75 -0
package/calc.js
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
const {wait_for_layout_ready} = require("./wait");
|
|
2
|
+
|
|
3
|
+
const creat_rect_desc = () => ({
|
|
4
|
+
"x": 0,
|
|
5
|
+
"y": 0,
|
|
6
|
+
"width": 0,
|
|
7
|
+
"height": 0,
|
|
8
|
+
"top": 0,
|
|
9
|
+
"right": 0,
|
|
10
|
+
"bottom": 0,
|
|
11
|
+
"left": 0
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
const get_rect_desc_bfr_transfrom = async(el) => {
|
|
15
|
+
let rect_bfr_transfrom = creat_rect_desc();
|
|
16
|
+
// 方法:临时移除 transform 获取真实位置
|
|
17
|
+
// 保存原始 transform
|
|
18
|
+
const originalTransform = el.style.transform;
|
|
19
|
+
// 临时移除 transform(会触发一次 reflow)
|
|
20
|
+
el.style.transform = 'none';
|
|
21
|
+
await wait_for_layout_ready();
|
|
22
|
+
// 获取无 transform 的位置
|
|
23
|
+
const rect = el.getBoundingClientRect();
|
|
24
|
+
// 立即恢复 transform
|
|
25
|
+
el.style.transform = originalTransform;
|
|
26
|
+
// 复制所有值
|
|
27
|
+
rect_bfr_transfrom.x = rect.x;
|
|
28
|
+
rect_bfr_transfrom.y = rect.y;
|
|
29
|
+
rect_bfr_transfrom.width = rect.width;
|
|
30
|
+
rect_bfr_transfrom.height = rect.height;
|
|
31
|
+
rect_bfr_transfrom.top = rect.top;
|
|
32
|
+
rect_bfr_transfrom.left = rect.left;
|
|
33
|
+
rect_bfr_transfrom.right = rect.right;
|
|
34
|
+
rect_bfr_transfrom.bottom = rect.bottom;
|
|
35
|
+
return rect_bfr_transfrom;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const get_all_positions_bfr_transform = async (
|
|
39
|
+
el,
|
|
40
|
+
filter = (el) => true
|
|
41
|
+
) => {
|
|
42
|
+
//使用editonly_sdfs_for_each 获取
|
|
43
|
+
//类似 .getBoundingClientRect()
|
|
44
|
+
let results = [];
|
|
45
|
+
await editonly_sdfs_for_each(el, async (curr_el) => {
|
|
46
|
+
if (filter(curr_el)) {
|
|
47
|
+
let rect_bfr_transfrom = await get_rect_desc_bfr_transfrom(curr_el);
|
|
48
|
+
results.push({
|
|
49
|
+
el: curr_el,
|
|
50
|
+
rect: rect_bfr_transfrom
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
return results;
|
|
56
|
+
}
|
|
57
|
+
const get_all_positions_aft_transform = async (
|
|
58
|
+
el,
|
|
59
|
+
filter = (el) => true
|
|
60
|
+
) => {
|
|
61
|
+
// 获取 transform 之后的最终位置
|
|
62
|
+
let results = [];
|
|
63
|
+
await editonly_sdfs_for_each(el, async (curr_el) => {
|
|
64
|
+
if (filter(curr_el)) {
|
|
65
|
+
await wait_for_layout_ready();
|
|
66
|
+
let rect_aft_transfrom = curr_el.getBoundingClientRect();
|
|
67
|
+
results.push({
|
|
68
|
+
el: curr_el,
|
|
69
|
+
rect: rect_aft_transfrom
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
return results;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const get_all_positions_transform_diff = async(
|
|
78
|
+
el,
|
|
79
|
+
filter = (el) => true
|
|
80
|
+
) => {
|
|
81
|
+
// 计算 transform 前后的差异
|
|
82
|
+
let results = [];
|
|
83
|
+
await editonly_sdfs_for_each(el, async (curr_el) => {
|
|
84
|
+
if (filter(curr_el)) {
|
|
85
|
+
let rect_bfr_transfrom = await get_rect_desc_bfr_transfrom(curr_el);
|
|
86
|
+
await wait_for_layout_ready();//因为 get_rect_desc_bfr_transfrom 会改回去 transform,需要等待布局稳定
|
|
87
|
+
let rect_aft_transfrom = curr_el.getBoundingClientRect();
|
|
88
|
+
let rect_diff = {
|
|
89
|
+
x: rect_aft_transfrom.x - rect_bfr_transfrom.x,
|
|
90
|
+
y: rect_aft_transfrom.y - rect_bfr_transfrom.y,
|
|
91
|
+
width: rect_aft_transfrom.width - rect_bfr_transfrom.width,
|
|
92
|
+
height: rect_aft_transfrom.height - rect_bfr_transfrom.height,
|
|
93
|
+
top: rect_aft_transfrom.top - rect_bfr_transfrom.top,
|
|
94
|
+
right: rect_aft_transfrom.right - rect_bfr_transfrom.right,
|
|
95
|
+
bottom: rect_aft_transfrom.bottom - rect_bfr_transfrom.bottom,
|
|
96
|
+
left: rect_aft_transfrom.left - rect_bfr_transfrom.left
|
|
97
|
+
};
|
|
98
|
+
if (rect_diff.x === 0 && rect_diff.y === 0 &&
|
|
99
|
+
rect_diff.width === 0 && rect_diff.height === 0 &&
|
|
100
|
+
rect_diff.top === 0 && rect_diff.right === 0 &&
|
|
101
|
+
rect_diff.bottom === 0 && rect_diff.left === 0) {
|
|
102
|
+
} else {
|
|
103
|
+
results.push({
|
|
104
|
+
el: curr_el,
|
|
105
|
+
rect: rect_diff
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
return results;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const is_rect_overlapping = (rect1, rect2) => {
|
|
115
|
+
return !(rect1.right < rect2.left ||
|
|
116
|
+
rect1.left > rect2.right ||
|
|
117
|
+
rect1.bottom < rect2.top ||
|
|
118
|
+
rect1.top > rect2.bottom);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const is_ele_overlapping_with_eles = (checkEle, eleList) => {
|
|
122
|
+
let isOverlapping = false;
|
|
123
|
+
for (const element of eleList) {
|
|
124
|
+
const elementRect = element.getBoundingClientRect();
|
|
125
|
+
// 检查元素是否与标题重叠
|
|
126
|
+
if (is_rect_overlapping(elementRect, checkEle.getBoundingClientRect())) {
|
|
127
|
+
isOverlapping = true;
|
|
128
|
+
break;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return isOverlapping;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
module.exports = {
|
|
136
|
+
creat_rect_desc,
|
|
137
|
+
get_rect_desc_bfr_transfrom,
|
|
138
|
+
get_all_positions_bfr_transform,
|
|
139
|
+
get_all_positions_aft_transform,
|
|
140
|
+
get_all_positions_transform_diff,
|
|
141
|
+
is_rect_overlapping,
|
|
142
|
+
is_ele_overlapping_with_eles,
|
|
143
|
+
|
|
144
|
+
}
|
package/cls-accessor.js
CHANGED
package/code.js
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
const {fullfill_url} = require("./util");
|
|
2
|
+
|
|
3
|
+
const get_all_scripts = async()=>{
|
|
4
|
+
const scripts = document.querySelectorAll('script');
|
|
5
|
+
const codePromises = [];
|
|
6
|
+
|
|
7
|
+
for (const script of scripts) {
|
|
8
|
+
if (script.src) {
|
|
9
|
+
codePromises.push(
|
|
10
|
+
fetch(script.src)
|
|
11
|
+
.then(r => r.ok ? r.text() : '')
|
|
12
|
+
.catch(() => '')
|
|
13
|
+
);
|
|
14
|
+
} else {
|
|
15
|
+
const inlineCode = script.textContent;
|
|
16
|
+
if (inlineCode.trim()) {
|
|
17
|
+
codePromises.push(Promise.resolve(inlineCode));
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const codes = await Promise.all(codePromises);
|
|
23
|
+
const allJsCode = codes.filter(code => code).join('\n\n');
|
|
24
|
+
|
|
25
|
+
return `<script>\n${allJsCode}\n</script>`;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const get_all_csses = async()=>{
|
|
29
|
+
let css = '';
|
|
30
|
+
for (const link of document.querySelectorAll('link[rel="stylesheet"]')) {
|
|
31
|
+
if (link.href) {
|
|
32
|
+
try {
|
|
33
|
+
css += await (await fetch(link.href)).text() + '\n';
|
|
34
|
+
} catch {}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
for (const style of document.querySelectorAll('style')) {
|
|
38
|
+
css += style.outerHtml + '\n';
|
|
39
|
+
}
|
|
40
|
+
return css;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const collect_img_like_urls = (root = document) => {
|
|
44
|
+
const urls = new Set();
|
|
45
|
+
|
|
46
|
+
// <img src>
|
|
47
|
+
root.querySelectorAll("img[src]").forEach(el => {
|
|
48
|
+
urls.add(fullfill_url(el.getAttribute("src")));
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
// <img srcset>
|
|
52
|
+
root.querySelectorAll("img[srcset]").forEach(el => {
|
|
53
|
+
const srcset = el.getAttribute("srcset");
|
|
54
|
+
if (!srcset) return;
|
|
55
|
+
for (const part of srcset.split(",")) {
|
|
56
|
+
const [u] = part.trim().split(/\s+/);
|
|
57
|
+
if (u) urls.add(fullfill_url(u));
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
// <svg image href / xlink:href>
|
|
62
|
+
root.querySelectorAll("svg").forEach(el => {
|
|
63
|
+
const text = new XMLSerializer().serializeToString(el);
|
|
64
|
+
const blob = new Blob([text], { type: "image/svg+xml" });
|
|
65
|
+
let url = URL.createObjectURL(blob);
|
|
66
|
+
urls.add([url,".svg"]);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
// CSS background-image (非常保守版)
|
|
70
|
+
root.querySelectorAll("*").forEach(el => {
|
|
71
|
+
const bg = getComputedStyle(el).backgroundImage;
|
|
72
|
+
if (!bg || bg === "none") return;
|
|
73
|
+
const m = bg.match(/url\(["']?(.*?)["']?\)/);
|
|
74
|
+
if (m && m[1]) urls.add(m[1]);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
return Array.from(urls).filter(Boolean);
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
const extract_css_varnms_from_rule = (rule, target)=> {
|
|
82
|
+
// 处理普通样式规则
|
|
83
|
+
if (rule instanceof CSSStyleRule) {
|
|
84
|
+
const style = rule.style;
|
|
85
|
+
for (let i = 0; i < style.length; i++) {
|
|
86
|
+
const property = style[i];
|
|
87
|
+
if (property.startsWith('--')) {
|
|
88
|
+
const value = style.getPropertyValue(property).trim();
|
|
89
|
+
if (value) {
|
|
90
|
+
target[property] = value;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
// 处理嵌套规则(如@media, @supports等)
|
|
96
|
+
else if (rule instanceof CSSMediaRule ||
|
|
97
|
+
rule instanceof CSSSupportsRule ||
|
|
98
|
+
rule instanceof CSSKeyframesRule) {
|
|
99
|
+
const nestedRules = rule.cssRules || rule.rules;
|
|
100
|
+
if (nestedRules) {
|
|
101
|
+
for (let i = 0; i < nestedRules.length; i++) {
|
|
102
|
+
extract_css_varnms_from_rule(nestedRules[i], target);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
// 处理@font-face规则
|
|
107
|
+
else if (rule instanceof CSSFontFaceRule) {
|
|
108
|
+
const style = rule.style;
|
|
109
|
+
for (let i = 0; i < style.length; i++) {
|
|
110
|
+
const property = style[i];
|
|
111
|
+
if (property.startsWith('--')) {
|
|
112
|
+
const value = style.getPropertyValue(property).trim();
|
|
113
|
+
if (value) {
|
|
114
|
+
target[property] = value;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* 获取页面中所有CSS自定义属性(变量)
|
|
123
|
+
* 返回格式: { [样式表来源] : { [变量名] : 变量值 } }
|
|
124
|
+
*
|
|
125
|
+
* @returns {Object} 包含所有CSS变量的对象
|
|
126
|
+
*/
|
|
127
|
+
const get_all_css_vars =()=>{
|
|
128
|
+
const result = {};
|
|
129
|
+
|
|
130
|
+
// 处理所有样式表
|
|
131
|
+
const styleSheets = document.styleSheets;
|
|
132
|
+
|
|
133
|
+
for (let i = 0; i < styleSheets.length; i++) {
|
|
134
|
+
const sheet = styleSheets[i];
|
|
135
|
+
let source = '';
|
|
136
|
+
|
|
137
|
+
// 确定样式表来源
|
|
138
|
+
if (sheet.href) {
|
|
139
|
+
source = sheet.href;
|
|
140
|
+
} else if (sheet.ownerNode) {
|
|
141
|
+
if (sheet.ownerNode.tagName === 'STYLE') {
|
|
142
|
+
source = 'inline-style';
|
|
143
|
+
} else if (sheet.ownerNode.tagName === 'LINK') {
|
|
144
|
+
source = sheet.ownerNode.href || 'external-stylesheet';
|
|
145
|
+
} else {
|
|
146
|
+
source = 'unknown';
|
|
147
|
+
}
|
|
148
|
+
} else {
|
|
149
|
+
source = 'unknown';
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// 初始化该来源的对象
|
|
153
|
+
if (!result[source]) {
|
|
154
|
+
result[source] = {};
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
try {
|
|
158
|
+
// 遍历样式表中的所有规则
|
|
159
|
+
const rules = sheet.cssRules || sheet.rules;
|
|
160
|
+
if (!rules) continue;
|
|
161
|
+
|
|
162
|
+
for (let j = 0; j < rules.length; j++) {
|
|
163
|
+
const rule = rules[j];
|
|
164
|
+
extract_css_varnms_from_rule(rule, result[source]);
|
|
165
|
+
}
|
|
166
|
+
} catch (error) {
|
|
167
|
+
// 处理跨域样式表等无法访问的情况
|
|
168
|
+
console.warn(`无法访问样式表 ${source} 的规则:`, error);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return result;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
module.exports = {
|
|
177
|
+
get_all_scripts,
|
|
178
|
+
get_all_csses,
|
|
179
|
+
collect_img_like_urls,
|
|
180
|
+
get_all_css_vars
|
|
181
|
+
}
|
package/com.sh
CHANGED
package/const.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
const TAGS = new Set([
|
|
2
|
+
"html","head","body","title","meta","link","base","style",
|
|
3
|
+
|
|
4
|
+
"header","footer","nav","main","section","article","aside",
|
|
5
|
+
"h1","h2","h3","h4","h5","h6","address",
|
|
6
|
+
|
|
7
|
+
"p","hr","pre","blockquote",
|
|
8
|
+
"ol","ul","li","dl","dt","dd",
|
|
9
|
+
"figure","figcaption","div",
|
|
10
|
+
|
|
11
|
+
"a","em","strong","small","s","cite","q","dfn","abbr",
|
|
12
|
+
"ruby","rt","rp","data","time",
|
|
13
|
+
"code","var","samp","kbd",
|
|
14
|
+
"sub","sup","i","b","u","mark",
|
|
15
|
+
"bdi","bdo","span","br","wbr",
|
|
16
|
+
|
|
17
|
+
"ins","del",
|
|
18
|
+
|
|
19
|
+
"img","iframe","embed","object","param",
|
|
20
|
+
"video","audio","source","track","picture",
|
|
21
|
+
"map","area","canvas","svg",
|
|
22
|
+
|
|
23
|
+
"table","caption","colgroup","col",
|
|
24
|
+
"thead","tbody","tfoot","tr","th","td",
|
|
25
|
+
|
|
26
|
+
"form","label","input","button","select","datalist",
|
|
27
|
+
"optgroup","option","textarea","output",
|
|
28
|
+
"progress","meter","fieldset","legend",
|
|
29
|
+
|
|
30
|
+
"details","summary","dialog",
|
|
31
|
+
|
|
32
|
+
"script","noscript","template","slot"
|
|
33
|
+
]);
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
const NEED_TO_SP_ATTR_NMS = new Set([
|
|
37
|
+
"class",
|
|
38
|
+
"rel",
|
|
39
|
+
"sandbox",
|
|
40
|
+
"allow",
|
|
41
|
+
"headers",
|
|
42
|
+
"aria-describedby",
|
|
43
|
+
"aria-labelledby"
|
|
44
|
+
]);
|
|
45
|
+
|
|
46
|
+
module.exports = {
|
|
47
|
+
TAGS,NEED_TO_SP_ATTR_NMS
|
|
48
|
+
}
|
package/dnld.js
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
const {creat_dt_prefix} = require("./util");
|
|
2
|
+
const {simple_alert} = require("./ui");
|
|
3
|
+
const {collect_img_like_urls} = require("./code");
|
|
4
|
+
|
|
5
|
+
const save_blob = async (blob, name,dir) => {
|
|
6
|
+
if(dir) {} else {
|
|
7
|
+
try{
|
|
8
|
+
dir = await window.showDirectoryPicker();
|
|
9
|
+
} catch(e) {
|
|
10
|
+
return [false,String(e.stack)]
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
try {
|
|
14
|
+
const fh = await dir.getFileHandle(name, { create: true });
|
|
15
|
+
const w = await fh.createWritable();
|
|
16
|
+
await w.write(blob);
|
|
17
|
+
await w.close();
|
|
18
|
+
return [true,null]
|
|
19
|
+
} catch(e) {
|
|
20
|
+
return [false,String(e.stack)]
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const fetch_save = async (url,dirHandle,name,recv) => {
|
|
25
|
+
if(name) {
|
|
26
|
+
if(Array.isArray(url)) { url = url[0];}
|
|
27
|
+
} else {
|
|
28
|
+
if(Array.isArray(url)) {
|
|
29
|
+
name = creat_dt_prefix()+"_" +"inline"+url[1];
|
|
30
|
+
url = url[0];
|
|
31
|
+
} else {
|
|
32
|
+
let U = new URL(url);
|
|
33
|
+
let arr = U.pathname.split("/");
|
|
34
|
+
name = creat_dt_prefix()+"_" + arr[arr.length-1];
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
if(dirHandle) {} else {
|
|
38
|
+
try{
|
|
39
|
+
dir = await window.showDirectoryPicker();
|
|
40
|
+
} catch(e) {
|
|
41
|
+
if(recv) {
|
|
42
|
+
recv.fails.push([url,String(e.stack)])
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
try {
|
|
47
|
+
const r = await fetch(url);
|
|
48
|
+
const b = await r.blob();
|
|
49
|
+
let [cond,reason] = await save_blob(b, name,dirHandle);
|
|
50
|
+
if(recv) {
|
|
51
|
+
if(cond) {
|
|
52
|
+
recv.succs.push(url)
|
|
53
|
+
} else {
|
|
54
|
+
recv.fails.push([url,String(reason)])
|
|
55
|
+
}
|
|
56
|
+
} else {}
|
|
57
|
+
} catch(e) {
|
|
58
|
+
if(recv) {
|
|
59
|
+
recv.fails.push([url,String(e.stack)])
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const dnld_img_like = async (fltr = (_url) => true) => {
|
|
65
|
+
const succs = [];
|
|
66
|
+
const fails = [];
|
|
67
|
+
// 1️⃣ 让用户选择目录
|
|
68
|
+
const dirHandle = await window.showDirectoryPicker();
|
|
69
|
+
// 2️⃣ 扫描 DOM,得到 url 列表
|
|
70
|
+
const urls = collect_img_like_urls(document);
|
|
71
|
+
// 3️⃣ 过滤 + 规范化
|
|
72
|
+
|
|
73
|
+
// 4️⃣ 去重
|
|
74
|
+
const uniq = Array.from(new Set(urls));
|
|
75
|
+
// 5️⃣ 顺序下载(避免并发 IO 爆炸)
|
|
76
|
+
for (const url of uniq) {
|
|
77
|
+
await fetch_save(url, dirHandle, undefined, { succs, fails });
|
|
78
|
+
}
|
|
79
|
+
simple_alert(
|
|
80
|
+
"dnld_img_like",
|
|
81
|
+
JSON.stringify({ succs, fails }, null, 2),
|
|
82
|
+
"success"
|
|
83
|
+
);
|
|
84
|
+
return { succs, fails };
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
module.exports = {
|
|
88
|
+
save_blob ,fetch_save,
|
|
89
|
+
dnld_img_like
|
|
90
|
+
}
|