@aglyn/plugins-ai 1.0.0-beta.292 → 1.0.0-beta.293
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/package.json +17 -17
- package/src/lib/components/ai-site-import-job.component.js +5 -4
- package/src/lib/components/ai-site-import-job.component.js.map +1 -1
- package/src/lib/components/ai-site-import-start.component.js +3 -3
- package/src/lib/components/ai-site-import-start.component.js.map +1 -1
- package/src/lib/components/ai-site-start-card.component.d.ts +1 -1
- package/src/lib/components/ai-site-start-card.component.js +129 -29
- package/src/lib/components/ai-site-start-card.component.js.map +1 -1
- package/src/lib/components/ai-site-start-steps.component.d.ts +15 -3
- package/src/lib/components/ai-site-start-steps.component.js +42 -25
- package/src/lib/components/ai-site-start-steps.component.js.map +1 -1
- package/src/lib/components/ai-site-style-previews.d.ts +17 -0
- package/src/lib/components/ai-site-style-previews.js +26 -95
- package/src/lib/components/ai-site-style-previews.js.map +1 -1
- package/src/lib/declarations.server.js +22 -0
- package/src/lib/declarations.server.js.map +1 -1
- package/src/lib/jobs/ai-job-import-step.d.ts +21 -2
- package/src/lib/jobs/ai-job-import-step.js +179 -49
- package/src/lib/jobs/ai-job-import-step.js.map +1 -1
- package/src/lib/jobs/ai-job-layout-language.d.ts +27 -3
- package/src/lib/jobs/ai-job-layout-language.js +156 -113
- package/src/lib/jobs/ai-job-layout-language.js.map +1 -1
- package/src/lib/jobs/ai-job-layout-step.js +56 -17
- package/src/lib/jobs/ai-job-layout-step.js.map +1 -1
- package/src/lib/jobs/ai-job-page-language.d.ts +25 -15
- package/src/lib/jobs/ai-job-page-language.js +120 -66
- package/src/lib/jobs/ai-job-page-language.js.map +1 -1
- package/src/lib/jobs/ai-job-page-step.js +27 -7
- package/src/lib/jobs/ai-job-page-step.js.map +1 -1
- package/src/lib/jobs/ai-job-site-datasets.d.ts +12 -4
- package/src/lib/jobs/ai-job-site-datasets.js +36 -16
- package/src/lib/jobs/ai-job-site-datasets.js.map +1 -1
- package/src/lib/jobs/ai-jobs.js +6 -2
- package/src/lib/jobs/ai-jobs.js.map +1 -1
- package/src/lib/jobs/ai-layout-stock-photos.d.ts +50 -1
- package/src/lib/jobs/ai-layout-stock-photos.js +586 -35
- package/src/lib/jobs/ai-layout-stock-photos.js.map +1 -1
- package/src/lib/jobs/ai-site-import-compile.d.ts +147 -0
- package/src/lib/jobs/ai-site-import-compile.js +1143 -0
- package/src/lib/jobs/ai-site-import-compile.js.map +1 -0
- package/src/lib/jobs/ai-site-import-document.d.ts +9 -36
- package/src/lib/jobs/ai-site-import-document.js +23 -489
- package/src/lib/jobs/ai-site-import-document.js.map +1 -1
- package/src/lib/jobs/ai-site-import-look.d.ts +54 -7
- package/src/lib/jobs/ai-site-import-look.js +163 -52
- package/src/lib/jobs/ai-site-import-look.js.map +1 -1
- package/src/lib/jobs/ai-site-import-publish.d.ts +7 -1
- package/src/lib/jobs/ai-site-import-publish.js +19 -5
- package/src/lib/jobs/ai-site-import-publish.js.map +1 -1
- package/src/lib/jobs/ai-site-late-photos.js +4 -2
- package/src/lib/jobs/ai-site-late-photos.js.map +1 -1
- package/src/lib/jobs/ai-site-publish.d.ts +10 -1
- package/src/lib/jobs/ai-site-publish.js +8 -3
- package/src/lib/jobs/ai-site-publish.js.map +1 -1
- package/src/lib/jobs/fixtures/ai-stock-photo-agl4092-1029-recordings.d.ts +23 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-agl4092-1029-recordings.js +677 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-agl4092-1029-recordings.js.map +1 -0
- package/src/lib/layout-language/ai-layout-compiler.d.ts +26 -0
- package/src/lib/layout-language/ai-layout-compiler.js +677 -33
- package/src/lib/layout-language/ai-layout-compiler.js.map +1 -1
- package/src/lib/layout-language/ai-layout-frame-hollows.js +5 -1
- package/src/lib/layout-language/ai-layout-frame-hollows.js.map +1 -1
- package/src/lib/layout-language/ai-layout-frame-source.d.ts +196 -0
- package/src/lib/layout-language/ai-layout-frame-source.js +726 -0
- package/src/lib/layout-language/ai-layout-frame-source.js.map +1 -0
- package/src/lib/layout-language/ai-layout-frame.d.ts +22 -0
- package/src/lib/layout-language/ai-layout-frame.js +121 -29
- package/src/lib/layout-language/ai-layout-frame.js.map +1 -1
- package/src/lib/layout-language/ai-layout-language.d.ts +103 -37
- package/src/lib/layout-language/ai-layout-language.js +85 -120
- package/src/lib/layout-language/ai-layout-language.js.map +1 -1
- package/src/lib/model/ai-credit-estimate.d.ts +11 -7
- package/src/lib/model/ai-credit-estimate.js +11 -7
- package/src/lib/model/ai-credit-estimate.js.map +1 -1
- package/src/lib/model/ai-site-import.d.ts +307 -13
- package/src/lib/model/ai-site-import.js +227 -19
- package/src/lib/model/ai-site-import.js.map +1 -1
- package/src/lib/model/ai-site-look.d.ts +65 -1
- package/src/lib/model/ai-site-look.js +141 -23
- package/src/lib/model/ai-site-look.js.map +1 -1
- package/src/lib/model/ai-site-photos.d.ts +2 -0
- package/src/lib/model/ai-site-photos.js.map +1 -1
- package/src/lib/model/ai-site-start-steps.d.ts +4 -3
- package/src/lib/model/ai-site-start-steps.js +11 -3
- package/src/lib/model/ai-site-start-steps.js.map +1 -1
- package/src/lib/model/ai-site-start.d.ts +23 -2
- package/src/lib/model/ai-site-start.js +25 -3
- package/src/lib/model/ai-site-start.js.map +1 -1
- package/src/lib/providers/routing.d.ts +11 -0
- package/src/lib/providers/routing.js +23 -0
- package/src/lib/providers/routing.js.map +1 -1
- package/src/lib/runtime/ai-doctrine-validators.d.ts +4 -2
- package/src/lib/runtime/ai-doctrine-validators.js +7 -2
- package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
- package/src/lib/runtime/ai-icon-library.d.ts +4 -0
- package/src/lib/runtime/ai-icon-library.js +4 -0
- package/src/lib/runtime/ai-icon-library.js.map +1 -1
- package/src/lib/runtime/ai-node-tree.js +5 -1
- package/src/lib/runtime/ai-node-tree.js.map +1 -1
- package/src/lib/runtime/ai-palette.d.ts +2 -0
- package/src/lib/runtime/ai-palette.generated.js +19 -3
- package/src/lib/runtime/ai-palette.generated.js.map +1 -1
- package/src/lib/runtime/ai-palette.js +2 -1
- package/src/lib/runtime/ai-palette.js.map +1 -1
- package/src/lib/site-import/fixtures/site-layout-recordings.d.ts +50 -0
- package/src/lib/site-import/fixtures/site-layout-recordings.js +86 -0
- package/src/lib/site-import/fixtures/site-layout-recordings.js.map +1 -0
- package/src/lib/site-import/site-import-crawl.d.ts +18 -1
- package/src/lib/site-import/site-import-crawl.js +70 -10
- package/src/lib/site-import/site-import-crawl.js.map +1 -1
- package/src/lib/site-import/site-import-css.d.ts +110 -0
- package/src/lib/site-import/site-import-css.js +826 -0
- package/src/lib/site-import/site-import-css.js.map +1 -0
- package/src/lib/site-import/site-import-html.d.ts +116 -1
- package/src/lib/site-import/site-import-html.js +538 -46
- package/src/lib/site-import/site-import-html.js.map +1 -1
- package/src/lib/site-import/site-import-layout.d.ts +79 -0
- package/src/lib/site-import/site-import-layout.js +1608 -0
- package/src/lib/site-import/site-import-layout.js.map +1 -0
- package/src/lib/site-import/site-import-nav.d.ts +40 -0
- package/src/lib/site-import/site-import-nav.js +437 -0
- package/src/lib/site-import/site-import-nav.js.map +1 -0
- package/src/lib/site-import/site-import-page.d.ts +8 -1
- package/src/lib/site-import/site-import-page.js +24 -2
- package/src/lib/site-import/site-import-page.js.map +1 -1
- package/src/lib/assets/site-styles/beauty.webp +0 -0
- package/src/lib/assets/site-styles/blog.webp +0 -0
- package/src/lib/assets/site-styles/business.webp +0 -0
- package/src/lib/assets/site-styles/coming-soon.webp +0 -0
- package/src/lib/assets/site-styles/education.webp +0 -0
- package/src/lib/assets/site-styles/events.webp +0 -0
- package/src/lib/assets/site-styles/fitness.webp +0 -0
- package/src/lib/assets/site-styles/landing.webp +0 -0
- package/src/lib/assets/site-styles/music.webp +0 -0
- package/src/lib/assets/site-styles/nonprofit.webp +0 -0
- package/src/lib/assets/site-styles/personal.webp +0 -0
- package/src/lib/assets/site-styles/photography.webp +0 -0
- package/src/lib/assets/site-styles/portfolio.webp +0 -0
- package/src/lib/assets/site-styles/professional.webp +0 -0
- package/src/lib/assets/site-styles/realestate.webp +0 -0
- package/src/lib/assets/site-styles/restaurant.webp +0 -0
- package/src/lib/assets/site-styles/store.webp +0 -0
- package/src/lib/assets/site-styles/studio.webp +0 -0
- package/src/lib/assets/site-styles/trades.webp +0 -0
- package/src/lib/assets/site-styles/wellness.webp +0 -0
- package/src/lib/assets/site-styles/yoga.webp +0 -0
- package/src/lib/site-import/site-import-review.d.ts +0 -40
- package/src/lib/site-import/site-import-review.js +0 -149
- package/src/lib/site-import/site-import-review.js.map +0 -1
|
@@ -0,0 +1,437 @@
|
|
|
1
|
+
import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
2
|
+
/**
|
|
3
|
+
* @license
|
|
4
|
+
* Copyright 2026 Aglyn LLC
|
|
5
|
+
*
|
|
6
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
* you may not use this file except in compliance with the License.
|
|
8
|
+
* You may obtain a copy of the License at
|
|
9
|
+
*
|
|
10
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
*
|
|
12
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
* See the License for the specific language governing permissions and
|
|
16
|
+
* limitations under the License.
|
|
17
|
+
*/ /*==========================================
|
|
18
|
+
* THE SOURCE'S MENU AND FOOTER, AS IT BUILDS THEM (AGL-4106).
|
|
19
|
+
*
|
|
20
|
+
* The import's header used to be the wave's page titles, seven of them, cut
|
|
21
|
+
* to 28 characters, with no menus: the source's own menu was read as a flat
|
|
22
|
+
* list of links and used only for their order. This reads it as the source
|
|
23
|
+
* builds it, with no model:
|
|
24
|
+
*
|
|
25
|
+
* - **The menu** (`siteImportNavTree`). Each top-level item of the header's
|
|
26
|
+
* `<nav>` row: its link, and the panel that opens under it (a container of
|
|
27
|
+
* two or more links), each entry with its name, the line under it in a mega
|
|
28
|
+
* menu, its address, its icon and the heading of the column it sits in.
|
|
29
|
+
* The header's buttons outside the row ("Sign in", "Start free") with their
|
|
30
|
+
* addresses and how they are drawn; whether the source shows a menu button
|
|
31
|
+
* on phones; the header bar's background.
|
|
32
|
+
* - **The footer** (`siteImportFooterColumns`). Each column: its heading and
|
|
33
|
+
* its links. Its profiles on other sites, its other links, its lines of
|
|
34
|
+
* text, its address, phone and email (from its structured data where the
|
|
35
|
+
* footer prints none), and its © line word for word.
|
|
36
|
+
*
|
|
37
|
+
* Every label is the source's, as written. Untrusted data: it is drawn by the
|
|
38
|
+
* frame compiler as words, and fenced wherever a prompt reads it.
|
|
39
|
+
*=========================================*/ import { AI_SITE_IMPORT_LABEL_MAX_CHARS } from "../model/ai-site-import.js";
|
|
40
|
+
import { siteImportBackgroundOf, siteImportCssOf, siteImportHex, siteImportStylesOf } from "./site-import-css.js";
|
|
41
|
+
import { siteImportButtonStyleOf } from "./site-import-layout.js";
|
|
42
|
+
/** The most top-level items, entries per menu and footer links kept. */ export const SITE_IMPORT_NAV_LIMITS = {
|
|
43
|
+
items: 12,
|
|
44
|
+
entries: 40,
|
|
45
|
+
footerLinks: 160
|
|
46
|
+
};
|
|
47
|
+
const PHONE_MENU = /\b(?:menu|navigation|nav)\b/i;
|
|
48
|
+
const PHONE_MENU_CLASS = /hamburger|burger|menu-?toggle|nav-?toggle|mobile-?menu|navbar-toggler|menu-?button|header-menu-toggle|drawer-?toggle/i;
|
|
49
|
+
const SOCIAL_NETWORKS = [
|
|
50
|
+
[
|
|
51
|
+
/(?:^|\.)(?:twitter\.com|x\.com)$/i,
|
|
52
|
+
'x'
|
|
53
|
+
],
|
|
54
|
+
[
|
|
55
|
+
/(?:^|\.)facebook\.com$/i,
|
|
56
|
+
'facebook'
|
|
57
|
+
],
|
|
58
|
+
[
|
|
59
|
+
/(?:^|\.)instagram\.com$/i,
|
|
60
|
+
'instagram'
|
|
61
|
+
],
|
|
62
|
+
[
|
|
63
|
+
/(?:^|\.)linkedin\.com$/i,
|
|
64
|
+
'linkedin'
|
|
65
|
+
],
|
|
66
|
+
[
|
|
67
|
+
/(?:^|\.)youtube\.com$/i,
|
|
68
|
+
'youtube'
|
|
69
|
+
],
|
|
70
|
+
[
|
|
71
|
+
/(?:^|\.)tiktok\.com$/i,
|
|
72
|
+
'tiktok'
|
|
73
|
+
],
|
|
74
|
+
[
|
|
75
|
+
/(?:^|\.)github\.com$/i,
|
|
76
|
+
'github'
|
|
77
|
+
],
|
|
78
|
+
[
|
|
79
|
+
/(?:^|\.)pinterest\.com$/i,
|
|
80
|
+
'pinterest'
|
|
81
|
+
],
|
|
82
|
+
[
|
|
83
|
+
/(?:^|\.)threads\.net$/i,
|
|
84
|
+
'threads'
|
|
85
|
+
],
|
|
86
|
+
[
|
|
87
|
+
/(?:^|\.)yelp\.com$/i,
|
|
88
|
+
'yelp'
|
|
89
|
+
],
|
|
90
|
+
[
|
|
91
|
+
/(?:^|\.)tripadvisor\.com$/i,
|
|
92
|
+
'tripadvisor'
|
|
93
|
+
],
|
|
94
|
+
[
|
|
95
|
+
/(?:^|\.)nextdoor\.com$/i,
|
|
96
|
+
'nextdoor'
|
|
97
|
+
],
|
|
98
|
+
[
|
|
99
|
+
/(?:^|\.)vimeo\.com$/i,
|
|
100
|
+
'vimeo'
|
|
101
|
+
],
|
|
102
|
+
[
|
|
103
|
+
/(?:^|\.)bsky\.app$/i,
|
|
104
|
+
'bluesky'
|
|
105
|
+
],
|
|
106
|
+
[
|
|
107
|
+
/(?:^|\.)mastodon\.social$/i,
|
|
108
|
+
'mastodon'
|
|
109
|
+
],
|
|
110
|
+
[
|
|
111
|
+
/(?:^|\.)discord\.(?:gg|com)$/i,
|
|
112
|
+
'discord'
|
|
113
|
+
],
|
|
114
|
+
[
|
|
115
|
+
/(?:^|\.)whatsapp\.com$|(?:^|\.)wa\.me$/i,
|
|
116
|
+
'whatsapp'
|
|
117
|
+
]
|
|
118
|
+
];
|
|
119
|
+
/** The network a profile address is on, or `null` for an address that is no profile. */ export function siteImportSocialNetwork(href) {
|
|
120
|
+
try {
|
|
121
|
+
var _ref;
|
|
122
|
+
var _SOCIAL_NETWORKS_find;
|
|
123
|
+
const host = new URL(href).hostname;
|
|
124
|
+
return (_ref = (_SOCIAL_NETWORKS_find = SOCIAL_NETWORKS.find(([pattern])=>pattern.test(host))) == null ? void 0 : _SOCIAL_NETWORKS_find[1]) != null ? _ref : null;
|
|
125
|
+
} catch (unused) {
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
const clean = (text, max = 300)=>{
|
|
130
|
+
const line = String(text != null ? text : '').replace(/\s+/g, ' ').trim();
|
|
131
|
+
return line.length > max ? `${line.slice(0, max - 1).trimEnd()}…` : line;
|
|
132
|
+
};
|
|
133
|
+
let FrameReader = class FrameReader {
|
|
134
|
+
anchors(element) {
|
|
135
|
+
const held = this.anchorMemo.get(element);
|
|
136
|
+
if (held) return held;
|
|
137
|
+
const node = this.elements[element];
|
|
138
|
+
if (!node) return [];
|
|
139
|
+
const own = this.anchorAt.get(element);
|
|
140
|
+
const list = own ? [
|
|
141
|
+
own
|
|
142
|
+
] : [];
|
|
143
|
+
for (const child of node.children)list.push(...this.anchors(child));
|
|
144
|
+
this.anchorMemo.set(element, list);
|
|
145
|
+
return list;
|
|
146
|
+
}
|
|
147
|
+
contains(ancestor, element) {
|
|
148
|
+
var _ref;
|
|
149
|
+
var _this_elements_at;
|
|
150
|
+
for(let at = element; at >= 0; at = (_ref = (_this_elements_at = this.elements[at]) == null ? void 0 : _this_elements_at.parent) != null ? _ref : -1)if (at === ancestor) return true;
|
|
151
|
+
return false;
|
|
152
|
+
}
|
|
153
|
+
/** The words a menu holds outside its links, by element: a column's heading, a button's name. */ texts(element) {
|
|
154
|
+
var _this_page_structure_texts;
|
|
155
|
+
const texts = (_this_page_structure_texts = this.page.structure.texts) != null ? _this_page_structure_texts : [];
|
|
156
|
+
return texts.filter((entry)=>this.contains(element, entry.element));
|
|
157
|
+
}
|
|
158
|
+
first(test) {
|
|
159
|
+
return this.elements.find(test);
|
|
160
|
+
}
|
|
161
|
+
background(element) {
|
|
162
|
+
for(let at = element, depth = 0; at >= 0 && depth < 4; depth += 1){
|
|
163
|
+
var _ref;
|
|
164
|
+
var _this_elements_at;
|
|
165
|
+
const color = siteImportBackgroundOf(this.styles.of(at)).color;
|
|
166
|
+
if (color && color.a >= 0.5) return siteImportHex(color);
|
|
167
|
+
const children = ((_ref = (_this_elements_at = this.elements[at]) == null ? void 0 : _this_elements_at.children) != null ? _ref : []).filter((child)=>{
|
|
168
|
+
var _this_elements_child;
|
|
169
|
+
return this.anchors(child).length || ((_this_elements_child = this.elements[child]) == null ? void 0 : _this_elements_child.children.length);
|
|
170
|
+
});
|
|
171
|
+
at = children.length === 1 ? children[0] : -1;
|
|
172
|
+
}
|
|
173
|
+
return null;
|
|
174
|
+
}
|
|
175
|
+
buttonStyle(element) {
|
|
176
|
+
var _ref;
|
|
177
|
+
var _this_elements_at;
|
|
178
|
+
const node = this.elements[element];
|
|
179
|
+
if (!node) return null;
|
|
180
|
+
let under = null;
|
|
181
|
+
for(let at = node.parent; at >= 0; at = (_ref = (_this_elements_at = this.elements[at]) == null ? void 0 : _this_elements_at.parent) != null ? _ref : -1){
|
|
182
|
+
const color = siteImportBackgroundOf(this.styles.of(at)).color;
|
|
183
|
+
if (color && color.a >= 0.5) {
|
|
184
|
+
under = siteImportHex(color);
|
|
185
|
+
break;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return siteImportButtonStyleOf(node, this.styles.of(element), under != null ? under : '#ffffff');
|
|
189
|
+
}
|
|
190
|
+
constructor(page, styles){
|
|
191
|
+
this.page = page;
|
|
192
|
+
this.styles = styles;
|
|
193
|
+
/** Each element's anchors, its subtree's, in order. */ this.anchorMemo = new Map();
|
|
194
|
+
this.anchorAt = new Map();
|
|
195
|
+
this.elements = page.structure.elements;
|
|
196
|
+
for (const anchor of page.structure.anchors)this.anchorAt.set(anchor.element, anchor);
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
function frameReader(page, stylesheets) {
|
|
200
|
+
const css = siteImportCssOf([
|
|
201
|
+
...page.structure.styles,
|
|
202
|
+
...stylesheets
|
|
203
|
+
]);
|
|
204
|
+
return new FrameReader(page, siteImportStylesOf(page.structure, css));
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* The source's header menu (AGL-4106), as its home page builds it; `null`
|
|
208
|
+
* for a page with no header menu the reader can find.
|
|
209
|
+
*/ export function siteImportNavTree(page, stylesheets = []) {
|
|
210
|
+
var _ref, _reader_first, _ref1, _reader_first1, _ref2;
|
|
211
|
+
var _reader_elements_row;
|
|
212
|
+
if (!page.structure) return null;
|
|
213
|
+
const reader = frameReader(page, stylesheets);
|
|
214
|
+
const header = (_ref = (_reader_first = reader.first((node)=>node.tag === 'header' && node.region === 'header')) != null ? _reader_first : reader.first((node)=>node.attrs.role === 'banner')) != null ? _ref : reader.first((node)=>node.tag === 'nav' && node.region !== 'footer');
|
|
215
|
+
if (!header) return null;
|
|
216
|
+
const nav = (_ref1 = (_reader_first1 = reader.first((node)=>node.tag === 'nav' && reader.contains(header.id, node.id) && reader.anchors(node.id).length >= 2)) != null ? _reader_first1 : reader.first((node)=>node.tag === 'nav' && node.region !== 'footer' && reader.anchors(node.id).length >= 2)) != null ? _ref1 : header;
|
|
217
|
+
// The row: down through wrappers to the element whose children are the items.
|
|
218
|
+
let row = nav.id;
|
|
219
|
+
for(let guard = 0; guard < 32; guard += 1){
|
|
220
|
+
var _ref3;
|
|
221
|
+
var _reader_elements_row1;
|
|
222
|
+
const children = ((_ref3 = (_reader_elements_row1 = reader.elements[row]) == null ? void 0 : _reader_elements_row1.children) != null ? _ref3 : []).filter((child)=>reader.anchors(child).length || reader.texts(child).length);
|
|
223
|
+
if (children.length !== 1) break;
|
|
224
|
+
row = children[0];
|
|
225
|
+
}
|
|
226
|
+
const brand = (anchor)=>{
|
|
227
|
+
try {
|
|
228
|
+
var _anchor_href;
|
|
229
|
+
return new URL((_anchor_href = anchor.href) != null ? _anchor_href : '').pathname === '/' && (!!anchor.image || !anchor.label);
|
|
230
|
+
} catch (unused) {
|
|
231
|
+
return false;
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
const items = [];
|
|
235
|
+
const ctas = [];
|
|
236
|
+
const inRow = new Set();
|
|
237
|
+
for (const child of (_ref2 = (_reader_elements_row = reader.elements[row]) == null ? void 0 : _reader_elements_row.children) != null ? _ref2 : []){
|
|
238
|
+
var _ref4, _ref5, _ref6;
|
|
239
|
+
const anchors = reader.anchors(child).filter((anchor)=>!brand(anchor) && (anchor.label || anchor.href));
|
|
240
|
+
const texts = reader.texts(child);
|
|
241
|
+
if (!anchors.length && !texts.length) continue;
|
|
242
|
+
anchors.forEach((anchor)=>inRow.add(anchor));
|
|
243
|
+
// A top-level link drawn as a button is a call to action, as the source draws it.
|
|
244
|
+
if (anchors.length === 1 && !texts.length) {
|
|
245
|
+
const anchor = anchors[0];
|
|
246
|
+
const style = reader.buttonStyle(anchor.element);
|
|
247
|
+
if (style && style !== 'quiet') {
|
|
248
|
+
ctas.push({
|
|
249
|
+
label: clean(anchor.label, AI_SITE_IMPORT_LABEL_MAX_CHARS * 2),
|
|
250
|
+
href: anchor.href,
|
|
251
|
+
style
|
|
252
|
+
});
|
|
253
|
+
continue;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
// The item's name: its first link, or the words of the button that opens its menu.
|
|
257
|
+
const trigger = anchors[0];
|
|
258
|
+
const opener = texts.find((entry)=>!trigger || entry.element < trigger.element);
|
|
259
|
+
const label = clean((_ref4 = (_ref5 = opener == null ? void 0 : opener.text) != null ? _ref5 : trigger == null ? void 0 : trigger.label) != null ? _ref4 : '', AI_SITE_IMPORT_LABEL_MAX_CHARS * 2);
|
|
260
|
+
if (!label) continue;
|
|
261
|
+
const entries = (opener ? anchors : anchors.slice(1)).slice(0, SITE_IMPORT_NAV_LIMITS.entries);
|
|
262
|
+
items.push({
|
|
263
|
+
label,
|
|
264
|
+
href: opener ? null : (_ref6 = trigger == null ? void 0 : trigger.href) != null ? _ref6 : null,
|
|
265
|
+
items: entries.filter((anchor)=>anchor.href).map((anchor)=>navEntry(reader, anchor, child))
|
|
266
|
+
});
|
|
267
|
+
if (items.length >= SITE_IMPORT_NAV_LIMITS.items) break;
|
|
268
|
+
}
|
|
269
|
+
// The header's buttons outside the menu row.
|
|
270
|
+
for (const anchor of reader.anchors(header.id)){
|
|
271
|
+
if (inRow.has(anchor) || brand(anchor) || !anchor.label || reader.contains(row, anchor.element)) continue;
|
|
272
|
+
const style = reader.buttonStyle(anchor.element);
|
|
273
|
+
if (!style) continue;
|
|
274
|
+
if (!ctas.some((cta)=>cta.label === anchor.label && cta.href === anchor.href)) ctas.push({
|
|
275
|
+
label: clean(anchor.label, AI_SITE_IMPORT_LABEL_MAX_CHARS * 2),
|
|
276
|
+
href: anchor.href,
|
|
277
|
+
style
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
const phoneMenu = reader.elements.some((node)=>{
|
|
281
|
+
var _node_attrs_class, _node_attrs_label, _node_attrs_class1, _node_attrs_controls;
|
|
282
|
+
if (node.region !== 'header' && !reader.contains(header.id, node.id)) return false;
|
|
283
|
+
if (node.tag !== 'button' && node.attrs.role !== 'button' && node.tag !== 'summary' && node.tag !== 'a') return false;
|
|
284
|
+
if (node.tag === 'a' && !PHONE_MENU_CLASS.test((_node_attrs_class = node.attrs.class) != null ? _node_attrs_class : '')) return false;
|
|
285
|
+
return PHONE_MENU.test((_node_attrs_label = node.attrs.label) != null ? _node_attrs_label : '') || PHONE_MENU_CLASS.test((_node_attrs_class1 = node.attrs.class) != null ? _node_attrs_class1 : '') || /menu|nav|drawer/i.test((_node_attrs_controls = node.attrs.controls) != null ? _node_attrs_controls : '');
|
|
286
|
+
});
|
|
287
|
+
if (!items.length && !ctas.length) return null;
|
|
288
|
+
return {
|
|
289
|
+
items,
|
|
290
|
+
ctas,
|
|
291
|
+
phoneMenu,
|
|
292
|
+
background: reader.background(header.id)
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
/** One entry of a menu, with the heading of the column it sits in. */ function navEntry(reader, anchor, item) {
|
|
296
|
+
const icon = anchor.icon !== undefined ? reader.page.structure.icons[anchor.icon] : undefined;
|
|
297
|
+
const column = columnHeading(reader, anchor.element, item);
|
|
298
|
+
return _extends({
|
|
299
|
+
label: clean(anchor.label, AI_SITE_IMPORT_LABEL_MAX_CHARS * 2)
|
|
300
|
+
}, anchor.text ? {
|
|
301
|
+
text: clean(anchor.text)
|
|
302
|
+
} : {}, {
|
|
303
|
+
href: anchor.href
|
|
304
|
+
}, (icon == null ? void 0 : icon.path) ? {
|
|
305
|
+
iconPath: icon.path,
|
|
306
|
+
viewBox: icon.viewBox
|
|
307
|
+
} : {}, column ? {
|
|
308
|
+
column
|
|
309
|
+
} : {});
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* The words that head the column an entry sits in: the first words, outside
|
|
313
|
+
* any link, of the nearest container around the entry that has words before
|
|
314
|
+
* its first link ("By team" over Agencies, Startups, Creators).
|
|
315
|
+
*/ function columnHeading(reader, element, item) {
|
|
316
|
+
var _ref, _ref1;
|
|
317
|
+
var _reader_elements_element, _reader_elements_at;
|
|
318
|
+
const own = reader.texts(item).filter((entry)=>!reader.anchors(item).some((anchor)=>reader.contains(anchor.element, entry.element)));
|
|
319
|
+
for(let at = (_ref = (_reader_elements_element = reader.elements[element]) == null ? void 0 : _reader_elements_element.parent) != null ? _ref : -1; at >= 0 && at !== item; at = (_ref1 = (_reader_elements_at = reader.elements[at]) == null ? void 0 : _reader_elements_at.parent) != null ? _ref1 : -1){
|
|
320
|
+
const first = own.find((entry)=>reader.contains(at, entry.element));
|
|
321
|
+
if (!first) continue;
|
|
322
|
+
const firstLink = reader.anchors(at)[0];
|
|
323
|
+
if (firstLink && first.element > firstLink.element) return null;
|
|
324
|
+
return first.text.length <= AI_SITE_IMPORT_LABEL_MAX_CHARS ? clean(first.text, AI_SITE_IMPORT_LABEL_MAX_CHARS) : null;
|
|
325
|
+
}
|
|
326
|
+
return null;
|
|
327
|
+
}
|
|
328
|
+
const EMAIL = /[\w.+-]+@[\w-]+(?:\.[\w-]+)+/;
|
|
329
|
+
const PHONE = /(?:\+?\d[\d\s().-]{7,}\d)/;
|
|
330
|
+
const ADDRESS = RegExp("\\d+\\s+[\\u0041-\\u005A\\u0061-\\u007A\\u00AA\\u00B5\\u00BA\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02C1\\u02C6-\\u02D1\\u02E0-\\u02E4\\u02EC\\u02EE\\u0370-\\u0374\\u0376-\\u0377\\u037A-\\u037D\\u037F\\u0386\\u0388-\\u038A\\u038C\\u038E-\\u03A1\\u03A3-\\u03F5\\u03F7-\\u0481\\u048A-\\u052F\\u0531-\\u0556\\u0559\\u0560-\\u0588\\u05D0-\\u05EA\\u05EF-\\u05F2\\u0620-\\u064A\\u066E-\\u066F\\u0671-\\u06D3\\u06D5\\u06E5-\\u06E6\\u06EE-\\u06EF\\u06FA-\\u06FC\\u06FF\\u0710\\u0712-\\u072F\\u074D-\\u07A5\\u07B1\\u07CA-\\u07EA\\u07F4-\\u07F5\\u07FA\\u0800-\\u0815\\u081A\\u0824\\u0828\\u0840-\\u0858\\u0860-\\u086A\\u0870-\\u0887\\u0889-\\u088F\\u08A0-\\u08C9\\u0904-\\u0939\\u093D\\u0950\\u0958-\\u0961\\u0971-\\u0980\\u0985-\\u098C\\u098F-\\u0990\\u0993-\\u09A8\\u09AA-\\u09B0\\u09B2\\u09B6-\\u09B9\\u09BD\\u09CE\\u09DC-\\u09DD\\u09DF-\\u09E1\\u09F0-\\u09F1\\u09FC\\u0A05-\\u0A0A\\u0A0F-\\u0A10\\u0A13-\\u0A28\\u0A2A-\\u0A30\\u0A32-\\u0A33\\u0A35-\\u0A36\\u0A38-\\u0A39\\u0A59-\\u0A5C\\u0A5E\\u0A72-\\u0A74\\u0A85-\\u0A8D\\u0A8F-\\u0A91\\u0A93-\\u0AA8\\u0AAA-\\u0AB0\\u0AB2-\\u0AB3\\u0AB5-\\u0AB9\\u0ABD\\u0AD0\\u0AE0-\\u0AE1\\u0AF9\\u0B05-\\u0B0C\\u0B0F-\\u0B10\\u0B13-\\u0B28\\u0B2A-\\u0B30\\u0B32-\\u0B33\\u0B35-\\u0B39\\u0B3D\\u0B5C-\\u0B5D\\u0B5F-\\u0B61\\u0B71\\u0B83\\u0B85-\\u0B8A\\u0B8E-\\u0B90\\u0B92-\\u0B95\\u0B99-\\u0B9A\\u0B9C\\u0B9E-\\u0B9F\\u0BA3-\\u0BA4\\u0BA8-\\u0BAA\\u0BAE-\\u0BB9\\u0BD0\\u0C05-\\u0C0C\\u0C0E-\\u0C10\\u0C12-\\u0C28\\u0C2A-\\u0C39\\u0C3D\\u0C58-\\u0C5A\\u0C5C-\\u0C5D\\u0C60-\\u0C61\\u0C80\\u0C85-\\u0C8C\\u0C8E-\\u0C90\\u0C92-\\u0CA8\\u0CAA-\\u0CB3\\u0CB5-\\u0CB9\\u0CBD\\u0CDC-\\u0CDE\\u0CE0-\\u0CE1\\u0CF1-\\u0CF2\\u0D04-\\u0D0C\\u0D0E-\\u0D10\\u0D12-\\u0D3A\\u0D3D\\u0D4E\\u0D54-\\u0D56\\u0D5F-\\u0D61\\u0D7A-\\u0D7F\\u0D85-\\u0D96\\u0D9A-\\u0DB1\\u0DB3-\\u0DBB\\u0DBD\\u0DC0-\\u0DC6\\u0E01-\\u0E30\\u0E32-\\u0E33\\u0E40-\\u0E46\\u0E81-\\u0E82\\u0E84\\u0E86-\\u0E8A\\u0E8C-\\u0EA3\\u0EA5\\u0EA7-\\u0EB0\\u0EB2-\\u0EB3\\u0EBD\\u0EC0-\\u0EC4\\u0EC6\\u0EDC-\\u0EDF\\u0F00\\u0F40-\\u0F47\\u0F49-\\u0F6C\\u0F88-\\u0F8C\\u1000-\\u102A\\u103F\\u1050-\\u1055\\u105A-\\u105D\\u1061\\u1065-\\u1066\\u106E-\\u1070\\u1075-\\u1081\\u108E\\u10A0-\\u10C5\\u10C7\\u10CD\\u10D0-\\u10FA\\u10FC-\\u1248\\u124A-\\u124D\\u1250-\\u1256\\u1258\\u125A-\\u125D\\u1260-\\u1288\\u128A-\\u128D\\u1290-\\u12B0\\u12B2-\\u12B5\\u12B8-\\u12BE\\u12C0\\u12C2-\\u12C5\\u12C8-\\u12D6\\u12D8-\\u1310\\u1312-\\u1315\\u1318-\\u135A\\u1380-\\u138F\\u13A0-\\u13F5\\u13F8-\\u13FD\\u1401-\\u166C\\u166F-\\u167F\\u1681-\\u169A\\u16A0-\\u16EA\\u16F1-\\u16F8\\u1700-\\u1711\\u171F-\\u1731\\u1740-\\u1751\\u1760-\\u176C\\u176E-\\u1770\\u1780-\\u17B3\\u17D7\\u17DC\\u1820-\\u1878\\u1880-\\u1884\\u1887-\\u18A8\\u18AA\\u18B0-\\u18F5\\u1900-\\u191E\\u1950-\\u196D\\u1970-\\u1974\\u1980-\\u19AB\\u19B0-\\u19C9\\u1A00-\\u1A16\\u1A20-\\u1A54\\u1AA7\\u1B05-\\u1B33\\u1B45-\\u1B4C\\u1B83-\\u1BA0\\u1BAE-\\u1BAF\\u1BBA-\\u1BE5\\u1C00-\\u1C23\\u1C4D-\\u1C4F\\u1C5A-\\u1C7D\\u1C80-\\u1C8A\\u1C90-\\u1CBA\\u1CBD-\\u1CBF\\u1CE9-\\u1CEC\\u1CEE-\\u1CF3\\u1CF5-\\u1CF6\\u1CFA\\u1D00-\\u1DBF\\u1E00-\\u1F15\\u1F18-\\u1F1D\\u1F20-\\u1F45\\u1F48-\\u1F4D\\u1F50-\\u1F57\\u1F59\\u1F5B\\u1F5D\\u1F5F-\\u1F7D\\u1F80-\\u1FB4\\u1FB6-\\u1FBC\\u1FBE\\u1FC2-\\u1FC4\\u1FC6-\\u1FCC\\u1FD0-\\u1FD3\\u1FD6-\\u1FDB\\u1FE0-\\u1FEC\\u1FF2-\\u1FF4\\u1FF6-\\u1FFC\\u2071\\u207F\\u2090-\\u209C\\u2102\\u2107\\u210A-\\u2113\\u2115\\u2119-\\u211D\\u2124\\u2126\\u2128\\u212A-\\u212D\\u212F-\\u2139\\u213C-\\u213F\\u2145-\\u2149\\u214E\\u2183-\\u2184\\u2C00-\\u2CE4\\u2CEB-\\u2CEE\\u2CF2-\\u2CF3\\u2D00-\\u2D25\\u2D27\\u2D2D\\u2D30-\\u2D67\\u2D6F\\u2D80-\\u2D96\\u2DA0-\\u2DA6\\u2DA8-\\u2DAE\\u2DB0-\\u2DB6\\u2DB8-\\u2DBE\\u2DC0-\\u2DC6\\u2DC8-\\u2DCE\\u2DD0-\\u2DD6\\u2DD8-\\u2DDE\\u2E2F\\u3005-\\u3006\\u3031-\\u3035\\u303B-\\u303C\\u3041-\\u3096\\u309D-\\u309F\\u30A1-\\u30FA\\u30FC-\\u30FF\\u3105-\\u312F\\u3131-\\u318E\\u31A0-\\u31BF\\u31F0-\\u31FF\\u3400-\\u4DBF\\u4E00-\\uA48C\\uA4D0-\\uA4FD\\uA500-\\uA60C\\uA610-\\uA61F\\uA62A-\\uA62B\\uA640-\\uA66E\\uA67F-\\uA69D\\uA6A0-\\uA6E5\\uA717-\\uA71F\\uA722-\\uA788\\uA78B-\\uA7DC\\uA7F1-\\uA801\\uA803-\\uA805\\uA807-\\uA80A\\uA80C-\\uA822\\uA840-\\uA873\\uA882-\\uA8B3\\uA8F2-\\uA8F7\\uA8FB\\uA8FD-\\uA8FE\\uA90A-\\uA925\\uA930-\\uA946\\uA960-\\uA97C\\uA984-\\uA9B2\\uA9CF\\uA9E0-\\uA9E4\\uA9E6-\\uA9EF\\uA9FA-\\uA9FE\\uAA00-\\uAA28\\uAA40-\\uAA42\\uAA44-\\uAA4B\\uAA60-\\uAA76\\uAA7A\\uAA7E-\\uAAAF\\uAAB1\\uAAB5-\\uAAB6\\uAAB9-\\uAABD\\uAAC0\\uAAC2\\uAADB-\\uAADD\\uAAE0-\\uAAEA\\uAAF2-\\uAAF4\\uAB01-\\uAB06\\uAB09-\\uAB0E\\uAB11-\\uAB16\\uAB20-\\uAB26\\uAB28-\\uAB2E\\uAB30-\\uAB5A\\uAB5C-\\uAB69\\uAB70-\\uABE2\\uAC00-\\uD7A3\\uD7B0-\\uD7C6\\uD7CB-\\uD7FB\\uF900-\\uFA6D\\uFA70-\\uFAD9\\uFB00-\\uFB06\\uFB13-\\uFB17\\uFB1D\\uFB1F-\\uFB28\\uFB2A-\\uFB36\\uFB38-\\uFB3C\\uFB3E\\uFB40-\\uFB41\\uFB43-\\uFB44\\uFB46-\\uFBB1\\uFBD3-\\uFD3D\\uFD50-\\uFD8F\\uFD92-\\uFDC7\\uFDF0-\\uFDFB\\uFE70-\\uFE74\\uFE76-\\uFEFC\\uFF21-\\uFF3A\\uFF41-\\uFF5A\\uFF66-\\uFFBE\\uFFC2-\\uFFC7\\uFFCA-\\uFFCF\\uFFD2-\\uFFD7\\uFFDA-\\uFFDC\\u{10000}-\\u{1000B}\\u{1000D}-\\u{10026}\\u{10028}-\\u{1003A}\\u{1003C}-\\u{1003D}\\u{1003F}-\\u{1004D}\\u{10050}-\\u{1005D}\\u{10080}-\\u{100FA}\\u{10280}-\\u{1029C}\\u{102A0}-\\u{102D0}\\u{10300}-\\u{1031F}\\u{1032D}-\\u{10340}\\u{10342}-\\u{10349}\\u{10350}-\\u{10375}\\u{10380}-\\u{1039D}\\u{103A0}-\\u{103C3}\\u{103C8}-\\u{103CF}\\u{10400}-\\u{1049D}\\u{104B0}-\\u{104D3}\\u{104D8}-\\u{104FB}\\u{10500}-\\u{10527}\\u{10530}-\\u{10563}\\u{10570}-\\u{1057A}\\u{1057C}-\\u{1058A}\\u{1058C}-\\u{10592}\\u{10594}-\\u{10595}\\u{10597}-\\u{105A1}\\u{105A3}-\\u{105B1}\\u{105B3}-\\u{105B9}\\u{105BB}-\\u{105BC}\\u{105C0}-\\u{105F3}\\u{10600}-\\u{10736}\\u{10740}-\\u{10755}\\u{10760}-\\u{10767}\\u{10780}-\\u{10785}\\u{10787}-\\u{107B0}\\u{107B2}-\\u{107BA}\\u{10800}-\\u{10805}\\u{10808}\\u{1080A}-\\u{10835}\\u{10837}-\\u{10838}\\u{1083C}\\u{1083F}-\\u{10855}\\u{10860}-\\u{10876}\\u{10880}-\\u{1089E}\\u{108E0}-\\u{108F2}\\u{108F4}-\\u{108F5}\\u{10900}-\\u{10915}\\u{10920}-\\u{10939}\\u{10940}-\\u{10959}\\u{10980}-\\u{109B7}\\u{109BE}-\\u{109BF}\\u{10A00}\\u{10A10}-\\u{10A13}\\u{10A15}-\\u{10A17}\\u{10A19}-\\u{10A35}\\u{10A60}-\\u{10A7C}\\u{10A80}-\\u{10A9C}\\u{10AC0}-\\u{10AC7}\\u{10AC9}-\\u{10AE4}\\u{10B00}-\\u{10B35}\\u{10B40}-\\u{10B55}\\u{10B60}-\\u{10B72}\\u{10B80}-\\u{10B91}\\u{10C00}-\\u{10C48}\\u{10C80}-\\u{10CB2}\\u{10CC0}-\\u{10CF2}\\u{10D00}-\\u{10D23}\\u{10D4A}-\\u{10D65}\\u{10D6F}-\\u{10D85}\\u{10E80}-\\u{10EA9}\\u{10EB0}-\\u{10EB1}\\u{10EC2}-\\u{10EC7}\\u{10F00}-\\u{10F1C}\\u{10F27}\\u{10F30}-\\u{10F45}\\u{10F70}-\\u{10F81}\\u{10FB0}-\\u{10FC4}\\u{10FE0}-\\u{10FF6}\\u{11003}-\\u{11037}\\u{11071}-\\u{11072}\\u{11075}\\u{11083}-\\u{110AF}\\u{110D0}-\\u{110E8}\\u{11103}-\\u{11126}\\u{11144}\\u{11147}\\u{11150}-\\u{11172}\\u{11176}\\u{11183}-\\u{111B2}\\u{111C1}-\\u{111C4}\\u{111DA}\\u{111DC}\\u{11200}-\\u{11211}\\u{11213}-\\u{1122B}\\u{1123F}-\\u{11240}\\u{11280}-\\u{11286}\\u{11288}\\u{1128A}-\\u{1128D}\\u{1128F}-\\u{1129D}\\u{1129F}-\\u{112A8}\\u{112B0}-\\u{112DE}\\u{11305}-\\u{1130C}\\u{1130F}-\\u{11310}\\u{11313}-\\u{11328}\\u{1132A}-\\u{11330}\\u{11332}-\\u{11333}\\u{11335}-\\u{11339}\\u{1133D}\\u{11350}\\u{1135D}-\\u{11361}\\u{11380}-\\u{11389}\\u{1138B}\\u{1138E}\\u{11390}-\\u{113B5}\\u{113B7}\\u{113D1}\\u{113D3}\\u{11400}-\\u{11434}\\u{11447}-\\u{1144A}\\u{1145F}-\\u{11461}\\u{11480}-\\u{114AF}\\u{114C4}-\\u{114C5}\\u{114C7}\\u{11580}-\\u{115AE}\\u{115D8}-\\u{115DB}\\u{11600}-\\u{1162F}\\u{11644}\\u{11680}-\\u{116AA}\\u{116B8}\\u{11700}-\\u{1171A}\\u{11740}-\\u{11746}\\u{11800}-\\u{1182B}\\u{118A0}-\\u{118DF}\\u{118FF}-\\u{11906}\\u{11909}\\u{1190C}-\\u{11913}\\u{11915}-\\u{11916}\\u{11918}-\\u{1192F}\\u{1193F}\\u{11941}\\u{119A0}-\\u{119A7}\\u{119AA}-\\u{119D0}\\u{119E1}\\u{119E3}\\u{11A00}\\u{11A0B}-\\u{11A32}\\u{11A3A}\\u{11A50}\\u{11A5C}-\\u{11A89}\\u{11A9D}\\u{11AB0}-\\u{11AF8}\\u{11BC0}-\\u{11BE0}\\u{11C00}-\\u{11C08}\\u{11C0A}-\\u{11C2E}\\u{11C40}\\u{11C72}-\\u{11C8F}\\u{11D00}-\\u{11D06}\\u{11D08}-\\u{11D09}\\u{11D0B}-\\u{11D30}\\u{11D46}\\u{11D60}-\\u{11D65}\\u{11D67}-\\u{11D68}\\u{11D6A}-\\u{11D89}\\u{11D98}\\u{11DB0}-\\u{11DDB}\\u{11EE0}-\\u{11EF2}\\u{11F02}\\u{11F04}-\\u{11F10}\\u{11F12}-\\u{11F33}\\u{11FB0}\\u{12000}-\\u{12399}\\u{12480}-\\u{12543}\\u{12F90}-\\u{12FF0}\\u{13000}-\\u{1342F}\\u{13441}-\\u{13446}\\u{13460}-\\u{143FA}\\u{14400}-\\u{14646}\\u{16100}-\\u{1611D}\\u{16800}-\\u{16A38}\\u{16A40}-\\u{16A5E}\\u{16A70}-\\u{16ABE}\\u{16AD0}-\\u{16AED}\\u{16B00}-\\u{16B2F}\\u{16B40}-\\u{16B43}\\u{16B63}-\\u{16B77}\\u{16B7D}-\\u{16B8F}\\u{16D40}-\\u{16D6C}\\u{16E40}-\\u{16E7F}\\u{16EA0}-\\u{16EB8}\\u{16EBB}-\\u{16ED3}\\u{16F00}-\\u{16F4A}\\u{16F50}\\u{16F93}-\\u{16F9F}\\u{16FE0}-\\u{16FE1}\\u{16FE3}\\u{16FF2}-\\u{16FF3}\\u{17000}-\\u{18CD5}\\u{18CFF}-\\u{18D1E}\\u{18D80}-\\u{18DF2}\\u{1AFF0}-\\u{1AFF3}\\u{1AFF5}-\\u{1AFFB}\\u{1AFFD}-\\u{1AFFE}\\u{1B000}-\\u{1B122}\\u{1B132}\\u{1B150}-\\u{1B152}\\u{1B155}\\u{1B164}-\\u{1B167}\\u{1B170}-\\u{1B2FB}\\u{1BC00}-\\u{1BC6A}\\u{1BC70}-\\u{1BC7C}\\u{1BC80}-\\u{1BC88}\\u{1BC90}-\\u{1BC99}\\u{1D400}-\\u{1D454}\\u{1D456}-\\u{1D49C}\\u{1D49E}-\\u{1D49F}\\u{1D4A2}\\u{1D4A5}-\\u{1D4A6}\\u{1D4A9}-\\u{1D4AC}\\u{1D4AE}-\\u{1D4B9}\\u{1D4BB}\\u{1D4BD}-\\u{1D4C3}\\u{1D4C5}-\\u{1D505}\\u{1D507}-\\u{1D50A}\\u{1D50D}-\\u{1D514}\\u{1D516}-\\u{1D51C}\\u{1D51E}-\\u{1D539}\\u{1D53B}-\\u{1D53E}\\u{1D540}-\\u{1D544}\\u{1D546}\\u{1D54A}-\\u{1D550}\\u{1D552}-\\u{1D6A5}\\u{1D6A8}-\\u{1D6C0}\\u{1D6C2}-\\u{1D6DA}\\u{1D6DC}-\\u{1D6FA}\\u{1D6FC}-\\u{1D714}\\u{1D716}-\\u{1D734}\\u{1D736}-\\u{1D74E}\\u{1D750}-\\u{1D76E}\\u{1D770}-\\u{1D788}\\u{1D78A}-\\u{1D7A8}\\u{1D7AA}-\\u{1D7C2}\\u{1D7C4}-\\u{1D7CB}\\u{1DF00}-\\u{1DF1E}\\u{1DF25}-\\u{1DF2A}\\u{1E030}-\\u{1E06D}\\u{1E100}-\\u{1E12C}\\u{1E137}-\\u{1E13D}\\u{1E14E}\\u{1E290}-\\u{1E2AD}\\u{1E2C0}-\\u{1E2EB}\\u{1E4D0}-\\u{1E4EB}\\u{1E5D0}-\\u{1E5ED}\\u{1E5F0}\\u{1E6C0}-\\u{1E6DE}\\u{1E6E0}-\\u{1E6E2}\\u{1E6E4}-\\u{1E6E5}\\u{1E6E7}-\\u{1E6ED}\\u{1E6F0}-\\u{1E6F4}\\u{1E6FE}-\\u{1E6FF}\\u{1E7E0}-\\u{1E7E6}\\u{1E7E8}-\\u{1E7EB}\\u{1E7ED}-\\u{1E7EE}\\u{1E7F0}-\\u{1E7FE}\\u{1E800}-\\u{1E8C4}\\u{1E900}-\\u{1E943}\\u{1E94B}\\u{1EE00}-\\u{1EE03}\\u{1EE05}-\\u{1EE1F}\\u{1EE21}-\\u{1EE22}\\u{1EE24}\\u{1EE27}\\u{1EE29}-\\u{1EE32}\\u{1EE34}-\\u{1EE37}\\u{1EE39}\\u{1EE3B}\\u{1EE42}\\u{1EE47}\\u{1EE49}\\u{1EE4B}\\u{1EE4D}-\\u{1EE4F}\\u{1EE51}-\\u{1EE52}\\u{1EE54}\\u{1EE57}\\u{1EE59}\\u{1EE5B}\\u{1EE5D}\\u{1EE5F}\\u{1EE61}-\\u{1EE62}\\u{1EE64}\\u{1EE67}-\\u{1EE6A}\\u{1EE6C}-\\u{1EE72}\\u{1EE74}-\\u{1EE77}\\u{1EE79}-\\u{1EE7C}\\u{1EE7E}\\u{1EE80}-\\u{1EE89}\\u{1EE8B}-\\u{1EE9B}\\u{1EEA1}-\\u{1EEA3}\\u{1EEA5}-\\u{1EEA9}\\u{1EEAB}-\\u{1EEBB}\\u{20000}-\\u{2A6DF}\\u{2A700}-\\u{2B81D}\\u{2B820}-\\u{2CEAD}\\u{2CEB0}-\\u{2EBE0}\\u{2EBF0}-\\u{2EE5D}\\u{2F800}-\\u{2FA1D}\\u{30000}-\\u{3134A}\\u{31350}-\\u{33479}\\d .'-]+(?:street|st\\.?|avenue|ave\\.?|road|rd\\.?|drive|dr\\.?|lane|ln\\.?|boulevard|blvd\\.?|way|court|ct\\.?|place|pl\\.?|suite|ste\\.?|highway|hwy\\.?|parkway|pkwy)\\b", "iu");
|
|
331
|
+
/**
|
|
332
|
+
* The source's footer (AGL-4106), as its home page builds it; `null` for a
|
|
333
|
+
* page with no footer.
|
|
334
|
+
*/ export function siteImportFooterColumns(page, stylesheets = []) {
|
|
335
|
+
if (!page.structure) return null;
|
|
336
|
+
const reader = frameReader(page, stylesheets);
|
|
337
|
+
const footers = reader.elements.filter((node)=>(node.tag === 'footer' || node.attrs.role === 'contentinfo') && node.region === 'footer');
|
|
338
|
+
const footer = footers.find((node)=>!footers.some((other)=>other !== node && reader.contains(other.id, node.id)));
|
|
339
|
+
if (!footer) return null;
|
|
340
|
+
const { blocks, structure } = page;
|
|
341
|
+
const leaves = [];
|
|
342
|
+
structure.blockElement.forEach((element, index)=>{
|
|
343
|
+
const block = blocks[index];
|
|
344
|
+
if (!block || block.region !== 'footer' || block.kind === 'image' || block.kind === 'link') return;
|
|
345
|
+
// A line that is only its link's words is that link.
|
|
346
|
+
const next = blocks[index + 1];
|
|
347
|
+
if ((next == null ? void 0 : next.kind) === 'link' && next.inline && next.text === block.text) return;
|
|
348
|
+
if (reader.contains(footer.id, element)) leaves.push({
|
|
349
|
+
element,
|
|
350
|
+
text: clean(block.text, 1000),
|
|
351
|
+
href: null
|
|
352
|
+
});
|
|
353
|
+
});
|
|
354
|
+
for (const entry of reader.texts(footer.id))leaves.push({
|
|
355
|
+
element: entry.element,
|
|
356
|
+
text: clean(entry.text, 1000),
|
|
357
|
+
href: null
|
|
358
|
+
});
|
|
359
|
+
for (const anchor of reader.anchors(footer.id)){
|
|
360
|
+
var _reader_elements_anchor_element;
|
|
361
|
+
// An icon link is named by its label: a profile's network.
|
|
362
|
+
const label = anchor.label || ((_reader_elements_anchor_element = reader.elements[anchor.element]) == null ? void 0 : _reader_elements_anchor_element.attrs.label) || '';
|
|
363
|
+
if (anchor.href && label) leaves.push({
|
|
364
|
+
element: anchor.element,
|
|
365
|
+
text: clean(label),
|
|
366
|
+
href: anchor.href
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
leaves.sort((a, b)=>a.element - b.element);
|
|
370
|
+
const within = (element)=>leaves.filter((leaf)=>reader.contains(element, leaf.element));
|
|
371
|
+
// A column: a heading, then its links, nothing else.
|
|
372
|
+
const columns = [];
|
|
373
|
+
const inColumns = new Set();
|
|
374
|
+
for (const node of reader.elements){
|
|
375
|
+
if (!reader.contains(footer.id, node.id) || node.id === footer.id) continue;
|
|
376
|
+
const own = within(node.id);
|
|
377
|
+
const [heading, ...rest] = own;
|
|
378
|
+
if (!heading || heading.href || heading.text.length > AI_SITE_IMPORT_LABEL_MAX_CHARS || rest.length < 2 || rest.some((leaf)=>!leaf.href)) continue;
|
|
379
|
+
if (own.some((leaf)=>inColumns.has(leaf))) continue;
|
|
380
|
+
own.forEach((leaf)=>inColumns.add(leaf));
|
|
381
|
+
columns.push({
|
|
382
|
+
heading: heading.text,
|
|
383
|
+
links: rest.slice(0, SITE_IMPORT_NAV_LIMITS.footerLinks).map((leaf)=>({
|
|
384
|
+
label: leaf.text,
|
|
385
|
+
href: leaf.href
|
|
386
|
+
}))
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
const social = [];
|
|
390
|
+
const links = [];
|
|
391
|
+
const text = [];
|
|
392
|
+
let copyright = null;
|
|
393
|
+
let address = null;
|
|
394
|
+
let phone = null;
|
|
395
|
+
let email = null;
|
|
396
|
+
for (const leaf of leaves){
|
|
397
|
+
if (inColumns.has(leaf)) continue;
|
|
398
|
+
if (leaf.href) {
|
|
399
|
+
const network = siteImportSocialNetwork(leaf.href);
|
|
400
|
+
if (network) {
|
|
401
|
+
if (!social.some((entry)=>entry.href === leaf.href)) social.push({
|
|
402
|
+
label: leaf.text,
|
|
403
|
+
href: leaf.href,
|
|
404
|
+
network
|
|
405
|
+
});
|
|
406
|
+
} else if (leaf.href.startsWith('mailto:')) email != null ? email : email = leaf.href.slice('mailto:'.length);
|
|
407
|
+
else if (leaf.href.startsWith('tel:')) phone != null ? phone : phone = leaf.text;
|
|
408
|
+
else if (!links.some((entry)=>entry.href === leaf.href && entry.label === leaf.text)) links.push({
|
|
409
|
+
label: leaf.text,
|
|
410
|
+
href: leaf.href
|
|
411
|
+
});
|
|
412
|
+
continue;
|
|
413
|
+
}
|
|
414
|
+
if (!copyright && /©|\(c\)\s*\d{4}|\bcopyright\b/i.test(leaf.text)) {
|
|
415
|
+
copyright = leaf.text;
|
|
416
|
+
continue;
|
|
417
|
+
}
|
|
418
|
+
if (!email && EMAIL.test(leaf.text) && leaf.text.length <= 120) email = EMAIL.exec(leaf.text)[0];
|
|
419
|
+
else if (!phone && PHONE.test(leaf.text) && leaf.text.length <= 40) phone = leaf.text;
|
|
420
|
+
else if (!address && ADDRESS.test(leaf.text) && leaf.text.length <= 200) address = leaf.text;
|
|
421
|
+
else if (!text.includes(leaf.text)) text.push(leaf.text);
|
|
422
|
+
}
|
|
423
|
+
// What the footer does not print, the site's own structured data says.
|
|
424
|
+
const organization = page.organization;
|
|
425
|
+
return {
|
|
426
|
+
columns,
|
|
427
|
+
social,
|
|
428
|
+
links,
|
|
429
|
+
text,
|
|
430
|
+
address: address != null ? address : (organization == null ? void 0 : organization.address) || null,
|
|
431
|
+
phone: phone != null ? phone : (organization == null ? void 0 : organization.telephone) || null,
|
|
432
|
+
email: email != null ? email : (organization == null ? void 0 : organization.email) || null,
|
|
433
|
+
copyright
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
//# sourceMappingURL=site-import-nav.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/site-import/site-import-nav.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*==========================================\n * THE SOURCE'S MENU AND FOOTER, AS IT BUILDS THEM (AGL-4106).\n *\n * The import's header used to be the wave's page titles, seven of them, cut\n * to 28 characters, with no menus: the source's own menu was read as a flat\n * list of links and used only for their order. This reads it as the source\n * builds it, with no model:\n *\n * - **The menu** (`siteImportNavTree`). Each top-level item of the header's\n * `<nav>` row: its link, and the panel that opens under it (a container of\n * two or more links), each entry with its name, the line under it in a mega\n * menu, its address, its icon and the heading of the column it sits in.\n * The header's buttons outside the row (\"Sign in\", \"Start free\") with their\n * addresses and how they are drawn; whether the source shows a menu button\n * on phones; the header bar's background.\n * - **The footer** (`siteImportFooterColumns`). Each column: its heading and\n * its links. Its profiles on other sites, its other links, its lines of\n * text, its address, phone and email (from its structured data where the\n * footer prints none), and its © line word for word.\n *\n * Every label is the source's, as written. Untrusted data: it is drawn by the\n * frame compiler as words, and fenced wherever a prompt reads it.\n *=========================================*/\n\nimport {\n AI_SITE_IMPORT_LABEL_MAX_CHARS,\n type SiteImportButtonStyle,\n type SiteImportFooter,\n type SiteImportFooterLink,\n type SiteImportNavCta,\n type SiteImportNavEntry,\n type SiteImportNavGroup,\n type SiteImportNavTree,\n} from '../model/ai-site-import'\nimport { siteImportBackgroundOf, siteImportCssOf, siteImportHex, siteImportStylesOf, type SiteImportStyles } from './site-import-css'\nimport type { SiteImportAnchor, SiteImportElement, SiteImportExtract, SiteImportStructure } from './site-import-html'\nimport { siteImportButtonStyleOf } from './site-import-layout'\n\n/** A page as the menu reader takes it: its structure, its blocks and its structured data. */\nexport type SiteImportFrameSource = Pick<SiteImportExtract, 'blocks' | 'organization'> & { structure: SiteImportStructure }\n\n/** The most top-level items, entries per menu and footer links kept. */\nexport const SITE_IMPORT_NAV_LIMITS = { items: 12, entries: 40, footerLinks: 160 } as const\n\nconst PHONE_MENU = /\\b(?:menu|navigation|nav)\\b/i\nconst PHONE_MENU_CLASS = /hamburger|burger|menu-?toggle|nav-?toggle|mobile-?menu|navbar-toggler|menu-?button|header-menu-toggle|drawer-?toggle/i\n\nconst SOCIAL_NETWORKS: ReadonlyArray<[RegExp, string]> = [\n [/(?:^|\\.)(?:twitter\\.com|x\\.com)$/i, 'x'],\n [/(?:^|\\.)facebook\\.com$/i, 'facebook'],\n [/(?:^|\\.)instagram\\.com$/i, 'instagram'],\n [/(?:^|\\.)linkedin\\.com$/i, 'linkedin'],\n [/(?:^|\\.)youtube\\.com$/i, 'youtube'],\n [/(?:^|\\.)tiktok\\.com$/i, 'tiktok'],\n [/(?:^|\\.)github\\.com$/i, 'github'],\n [/(?:^|\\.)pinterest\\.com$/i, 'pinterest'],\n [/(?:^|\\.)threads\\.net$/i, 'threads'],\n [/(?:^|\\.)yelp\\.com$/i, 'yelp'],\n [/(?:^|\\.)tripadvisor\\.com$/i, 'tripadvisor'],\n [/(?:^|\\.)nextdoor\\.com$/i, 'nextdoor'],\n [/(?:^|\\.)vimeo\\.com$/i, 'vimeo'],\n [/(?:^|\\.)bsky\\.app$/i, 'bluesky'],\n [/(?:^|\\.)mastodon\\.social$/i, 'mastodon'],\n [/(?:^|\\.)discord\\.(?:gg|com)$/i, 'discord'],\n [/(?:^|\\.)whatsapp\\.com$|(?:^|\\.)wa\\.me$/i, 'whatsapp'],\n]\n\n/** The network a profile address is on, or `null` for an address that is no profile. */\nexport function siteImportSocialNetwork(href: string): string | null {\n try {\n const host = new URL(href).hostname\n return SOCIAL_NETWORKS.find(([pattern]) => pattern.test(host))?.[1] ?? null\n } catch {\n return null\n }\n}\n\nconst clean = (text: string, max = 300) => {\n const line = String(text ?? '').replace(/\\s+/g, ' ').trim()\n return line.length > max ? `${line.slice(0, max - 1).trimEnd()}…` : line\n}\n\nclass FrameReader {\n readonly elements: readonly SiteImportElement[]\n /** Each element's anchors, its subtree's, in order. */\n private readonly anchorMemo = new Map<number, SiteImportAnchor[]>()\n private readonly anchorAt = new Map<number, SiteImportAnchor>()\n\n constructor(\n readonly page: SiteImportFrameSource,\n readonly styles: SiteImportStyles,\n ) {\n this.elements = page.structure.elements\n for (const anchor of page.structure.anchors) this.anchorAt.set(anchor.element, anchor)\n }\n\n anchors(element: number): SiteImportAnchor[] {\n const held = this.anchorMemo.get(element)\n if (held) return held\n const node = this.elements[element]\n if (!node) return []\n const own = this.anchorAt.get(element)\n const list = own ? [own] : []\n for (const child of node.children) list.push(...this.anchors(child))\n this.anchorMemo.set(element, list)\n return list\n }\n\n contains(ancestor: number, element: number): boolean {\n for (let at = element; at >= 0; at = this.elements[at]?.parent ?? -1) if (at === ancestor) return true\n return false\n }\n\n /** The words a menu holds outside its links, by element: a column's heading, a button's name. */\n texts(element: number): Array<{ element: number; text: string }> {\n const texts = this.page.structure.texts ?? []\n return texts.filter((entry) => this.contains(element, entry.element))\n }\n\n first(test: (node: SiteImportElement) => boolean): SiteImportElement | undefined {\n return this.elements.find(test)\n }\n\n background(element: number): string | null {\n for (let at = element, depth = 0; at >= 0 && depth < 4; depth += 1) {\n const color = siteImportBackgroundOf(this.styles.of(at)).color\n if (color && color.a >= 0.5) return siteImportHex(color)\n const children: number[] = (this.elements[at]?.children ?? []).filter((child) => this.anchors(child).length || this.elements[child]?.children.length)\n at = children.length === 1 ? (children[0] as number) : -1\n }\n return null\n }\n\n buttonStyle(element: number): SiteImportButtonStyle | null {\n const node = this.elements[element]\n if (!node) return null\n let under: string | null = null\n for (let at = node.parent; at >= 0; at = this.elements[at]?.parent ?? -1) {\n const color = siteImportBackgroundOf(this.styles.of(at)).color\n if (color && color.a >= 0.5) {\n under = siteImportHex(color)\n break\n }\n }\n return siteImportButtonStyleOf(node, this.styles.of(element), under ?? '#ffffff')\n }\n}\n\nfunction frameReader(page: SiteImportFrameSource, stylesheets: readonly string[]): FrameReader {\n const css = siteImportCssOf([...page.structure.styles, ...stylesheets])\n return new FrameReader(page, siteImportStylesOf(page.structure, css))\n}\n\n/**\n * The source's header menu (AGL-4106), as its home page builds it; `null`\n * for a page with no header menu the reader can find.\n */\nexport function siteImportNavTree(page: SiteImportFrameSource, stylesheets: readonly string[] = []): SiteImportNavTree | null {\n if (!page.structure) return null\n const reader = frameReader(page, stylesheets)\n const header =\n reader.first((node) => node.tag === 'header' && node.region === 'header') ??\n reader.first((node) => node.attrs.role === 'banner') ??\n reader.first((node) => node.tag === 'nav' && node.region !== 'footer')\n if (!header) return null\n const nav =\n reader.first((node) => node.tag === 'nav' && reader.contains(header.id, node.id) && reader.anchors(node.id).length >= 2) ??\n reader.first((node) => node.tag === 'nav' && node.region !== 'footer' && reader.anchors(node.id).length >= 2) ??\n header\n // The row: down through wrappers to the element whose children are the items.\n let row = nav.id\n for (let guard = 0; guard < 32; guard += 1) {\n const children = (reader.elements[row]?.children ?? []).filter((child) => reader.anchors(child).length || reader.texts(child).length)\n if (children.length !== 1) break\n row = children[0] as number\n }\n const brand = (anchor: SiteImportAnchor) => {\n try {\n return new URL(anchor.href ?? '').pathname === '/' && (!!anchor.image || !anchor.label)\n } catch {\n return false\n }\n }\n const items: SiteImportNavGroup[] = []\n const ctas: SiteImportNavCta[] = []\n const inRow = new Set<SiteImportAnchor>()\n for (const child of reader.elements[row]?.children ?? []) {\n const anchors = reader.anchors(child).filter((anchor) => !brand(anchor) && (anchor.label || anchor.href))\n const texts = reader.texts(child)\n if (!anchors.length && !texts.length) continue\n anchors.forEach((anchor) => inRow.add(anchor))\n // A top-level link drawn as a button is a call to action, as the source draws it.\n if (anchors.length === 1 && !texts.length) {\n const anchor = anchors[0] as SiteImportAnchor\n const style = reader.buttonStyle(anchor.element)\n if (style && style !== 'quiet') {\n ctas.push({ label: clean(anchor.label, AI_SITE_IMPORT_LABEL_MAX_CHARS * 2), href: anchor.href, style })\n continue\n }\n }\n // The item's name: its first link, or the words of the button that opens its menu.\n const trigger = anchors[0]\n const opener = texts.find((entry) => !trigger || entry.element < trigger.element)\n const label = clean(opener?.text ?? trigger?.label ?? '', AI_SITE_IMPORT_LABEL_MAX_CHARS * 2)\n if (!label) continue\n const entries = (opener ? anchors : anchors.slice(1)).slice(0, SITE_IMPORT_NAV_LIMITS.entries)\n items.push({\n label,\n href: opener ? null : (trigger?.href ?? null),\n items: entries.filter((anchor) => anchor.href).map((anchor) => navEntry(reader, anchor, child)),\n })\n if (items.length >= SITE_IMPORT_NAV_LIMITS.items) break\n }\n // The header's buttons outside the menu row.\n for (const anchor of reader.anchors(header.id)) {\n if (inRow.has(anchor) || brand(anchor) || !anchor.label || reader.contains(row, anchor.element)) continue\n const style = reader.buttonStyle(anchor.element)\n if (!style) continue\n if (!ctas.some((cta) => cta.label === anchor.label && cta.href === anchor.href)) ctas.push({ label: clean(anchor.label, AI_SITE_IMPORT_LABEL_MAX_CHARS * 2), href: anchor.href, style })\n }\n const phoneMenu = reader.elements.some((node) => {\n if (node.region !== 'header' && !reader.contains(header.id, node.id)) return false\n if (node.tag !== 'button' && node.attrs.role !== 'button' && node.tag !== 'summary' && node.tag !== 'a') return false\n if (node.tag === 'a' && !PHONE_MENU_CLASS.test(node.attrs.class ?? '')) return false\n return PHONE_MENU.test(node.attrs.label ?? '') || PHONE_MENU_CLASS.test(node.attrs.class ?? '') || /menu|nav|drawer/i.test(node.attrs.controls ?? '')\n })\n if (!items.length && !ctas.length) return null\n return { items, ctas, phoneMenu, background: reader.background(header.id) }\n}\n\n/** One entry of a menu, with the heading of the column it sits in. */\nfunction navEntry(reader: FrameReader, anchor: SiteImportAnchor, item: number): SiteImportNavEntry {\n const icon = anchor.icon !== undefined ? reader.page.structure.icons[anchor.icon] : undefined\n const column = columnHeading(reader, anchor.element, item)\n return {\n label: clean(anchor.label, AI_SITE_IMPORT_LABEL_MAX_CHARS * 2),\n ...(anchor.text ? { text: clean(anchor.text) } : {}),\n href: anchor.href as string,\n ...(icon?.path ? { iconPath: icon.path, viewBox: icon.viewBox } : {}),\n ...(column ? { column } : {}),\n }\n}\n\n/**\n * The words that head the column an entry sits in: the first words, outside\n * any link, of the nearest container around the entry that has words before\n * its first link (\"By team\" over Agencies, Startups, Creators).\n */\nfunction columnHeading(reader: FrameReader, element: number, item: number): string | null {\n const own = reader.texts(item).filter((entry) => !reader.anchors(item).some((anchor) => reader.contains(anchor.element, entry.element)))\n for (let at = reader.elements[element]?.parent ?? -1; at >= 0 && at !== item; at = reader.elements[at]?.parent ?? -1) {\n const first = own.find((entry) => reader.contains(at, entry.element))\n if (!first) continue\n const firstLink = reader.anchors(at)[0]\n if (firstLink && first.element > firstLink.element) return null\n return first.text.length <= AI_SITE_IMPORT_LABEL_MAX_CHARS ? clean(first.text, AI_SITE_IMPORT_LABEL_MAX_CHARS) : null\n }\n return null\n}\n\nconst EMAIL = /[\\w.+-]+@[\\w-]+(?:\\.[\\w-]+)+/\nconst PHONE = /(?:\\+?\\d[\\d\\s().-]{7,}\\d)/\nconst ADDRESS = /\\d+\\s+[\\p{L}\\d .'-]+(?:street|st\\.?|avenue|ave\\.?|road|rd\\.?|drive|dr\\.?|lane|ln\\.?|boulevard|blvd\\.?|way|court|ct\\.?|place|pl\\.?|suite|ste\\.?|highway|hwy\\.?|parkway|pkwy)\\b/iu\n\n/**\n * The source's footer (AGL-4106), as its home page builds it; `null` for a\n * page with no footer.\n */\nexport function siteImportFooterColumns(page: SiteImportFrameSource, stylesheets: readonly string[] = []): SiteImportFooter | null {\n if (!page.structure) return null\n const reader = frameReader(page, stylesheets)\n const footers = reader.elements.filter((node) => (node.tag === 'footer' || node.attrs.role === 'contentinfo') && node.region === 'footer')\n const footer = footers.find((node) => !footers.some((other) => other !== node && reader.contains(other.id, node.id)))\n if (!footer) return null\n const { blocks, structure } = page\n // The footer's words in order: its text blocks, its menu's words, and its links.\n type Leaf = { element: number; text: string; href: string | null }\n const leaves: Leaf[] = []\n structure.blockElement.forEach((element, index) => {\n const block = blocks[index]\n if (!block || block.region !== 'footer' || block.kind === 'image' || block.kind === 'link') return\n // A line that is only its link's words is that link.\n const next = blocks[index + 1]\n if (next?.kind === 'link' && next.inline && next.text === block.text) return\n if (reader.contains(footer.id, element)) leaves.push({ element, text: clean(block.text, 1_000), href: null })\n })\n for (const entry of reader.texts(footer.id)) leaves.push({ element: entry.element, text: clean(entry.text, 1_000), href: null })\n for (const anchor of reader.anchors(footer.id)) {\n // An icon link is named by its label: a profile's network.\n const label = anchor.label || reader.elements[anchor.element]?.attrs.label || ''\n if (anchor.href && label) leaves.push({ element: anchor.element, text: clean(label), href: anchor.href })\n }\n leaves.sort((a, b) => a.element - b.element)\n const within = (element: number) => leaves.filter((leaf) => reader.contains(element, leaf.element))\n // A column: a heading, then its links, nothing else.\n const columns: SiteImportFooter['columns'] = []\n const inColumns = new Set<Leaf>()\n for (const node of reader.elements) {\n if (!reader.contains(footer.id, node.id) || node.id === footer.id) continue\n const own = within(node.id)\n const [heading, ...rest] = own\n if (!heading || heading.href || heading.text.length > AI_SITE_IMPORT_LABEL_MAX_CHARS || rest.length < 2 || rest.some((leaf) => !leaf.href)) continue\n if (own.some((leaf) => inColumns.has(leaf))) continue\n own.forEach((leaf) => inColumns.add(leaf))\n columns.push({ heading: heading.text, links: rest.slice(0, SITE_IMPORT_NAV_LIMITS.footerLinks).map((leaf) => ({ label: leaf.text, href: leaf.href as string })) })\n }\n const social: SiteImportFooter['social'] = []\n const links: SiteImportFooterLink[] = []\n const text: string[] = []\n let copyright: string | null = null\n let address: string | null = null\n let phone: string | null = null\n let email: string | null = null\n for (const leaf of leaves) {\n if (inColumns.has(leaf)) continue\n if (leaf.href) {\n const network = siteImportSocialNetwork(leaf.href)\n if (network) {\n if (!social.some((entry) => entry.href === leaf.href)) social.push({ label: leaf.text, href: leaf.href, network })\n } else if (leaf.href.startsWith('mailto:')) email ??= leaf.href.slice('mailto:'.length)\n else if (leaf.href.startsWith('tel:')) phone ??= leaf.text\n else if (!links.some((entry) => entry.href === leaf.href && entry.label === leaf.text)) links.push({ label: leaf.text, href: leaf.href })\n continue\n }\n if (!copyright && /©|\\(c\\)\\s*\\d{4}|\\bcopyright\\b/i.test(leaf.text)) {\n copyright = leaf.text\n continue\n }\n if (!email && EMAIL.test(leaf.text) && leaf.text.length <= 120) email = (EMAIL.exec(leaf.text) as RegExpExecArray)[0]\n else if (!phone && PHONE.test(leaf.text) && leaf.text.length <= 40) phone = leaf.text\n else if (!address && ADDRESS.test(leaf.text) && leaf.text.length <= 200) address = leaf.text\n else if (!text.includes(leaf.text)) text.push(leaf.text)\n }\n // What the footer does not print, the site's own structured data says.\n const organization = page.organization\n return {\n columns,\n social,\n links,\n text,\n address: address ?? (organization?.address || null),\n phone: phone ?? (organization?.telephone || null),\n email: email ?? (organization?.email || null),\n copyright,\n }\n}\n"],"names":["AI_SITE_IMPORT_LABEL_MAX_CHARS","siteImportBackgroundOf","siteImportCssOf","siteImportHex","siteImportStylesOf","siteImportButtonStyleOf","SITE_IMPORT_NAV_LIMITS","items","entries","footerLinks","PHONE_MENU","PHONE_MENU_CLASS","SOCIAL_NETWORKS","siteImportSocialNetwork","href","host","URL","hostname","find","pattern","test","clean","text","max","line","String","replace","trim","length","slice","trimEnd","FrameReader","anchors","element","held","anchorMemo","get","node","elements","own","anchorAt","list","child","children","push","set","contains","ancestor","at","parent","texts","page","structure","filter","entry","first","background","depth","color","styles","of","a","buttonStyle","under","Map","anchor","frameReader","stylesheets","css","siteImportNavTree","reader","header","tag","region","attrs","role","nav","id","row","guard","brand","pathname","image","label","ctas","inRow","Set","opener","forEach","add","style","trigger","map","navEntry","has","some","cta","phoneMenu","class","controls","item","icon","undefined","icons","column","columnHeading","path","iconPath","viewBox","firstLink","EMAIL","PHONE","ADDRESS","siteImportFooterColumns","footers","footer","other","blocks","leaves","blockElement","index","block","kind","next","inline","sort","b","within","leaf","columns","inColumns","heading","rest","links","social","copyright","address","phone","email","network","startsWith","exec","includes","organization","telephone"],"mappings":";AAAA;;;;;;;;;;;;;;;CAeC,GAED;;;;;;;;;;;;;;;;;;;;;;2CAsB2C,GAE3C,SACEA,8BAA8B,QAQzB,6BAAyB;AAChC,SAASC,sBAAsB,EAAEC,eAAe,EAAEC,aAAa,EAAEC,kBAAkB,QAA+B,uBAAmB;AAErI,SAASC,uBAAuB,QAAQ,0BAAsB;AAK9D,sEAAsE,GACtE,OAAO,MAAMC,yBAAyB;IAAEC,OAAO;IAAIC,SAAS;IAAIC,aAAa;AAAI,EAAU;AAE3F,MAAMC,aAAa;AACnB,MAAMC,mBAAmB;AAEzB,MAAMC,kBAAmD;IACvD;QAAC;QAAqC;KAAI;IAC1C;QAAC;QAA2B;KAAW;IACvC;QAAC;QAA4B;KAAY;IACzC;QAAC;QAA2B;KAAW;IACvC;QAAC;QAA0B;KAAU;IACrC;QAAC;QAAyB;KAAS;IACnC;QAAC;QAAyB;KAAS;IACnC;QAAC;QAA4B;KAAY;IACzC;QAAC;QAA0B;KAAU;IACrC;QAAC;QAAuB;KAAO;IAC/B;QAAC;QAA8B;KAAc;IAC7C;QAAC;QAA2B;KAAW;IACvC;QAAC;QAAwB;KAAQ;IACjC;QAAC;QAAuB;KAAU;IAClC;QAAC;QAA8B;KAAW;IAC1C;QAAC;QAAiC;KAAU;IAC5C;QAAC;QAA2C;KAAW;CACxD;AAED,sFAAsF,GACtF,OAAO,SAASC,wBAAwBC,IAAY;IAClD,IAAI;;YAEKF;QADP,MAAMG,OAAO,IAAIC,IAAIF,MAAMG,QAAQ;QACnC,gBAAOL,wBAAAA,gBAAgBM,IAAI,CAAC,CAAC,CAACC,QAAQ,GAAKA,QAAQC,IAAI,CAACL,2BAAjDH,qBAAyD,CAAC,EAAE,mBAAI;IACzE,EAAE,eAAM;QACN,OAAO;IACT;AACF;AAEA,MAAMS,QAAQ,CAACC,MAAcC,MAAM,GAAG;IACpC,MAAMC,OAAOC,OAAOH,eAAAA,OAAQ,IAAII,OAAO,CAAC,QAAQ,KAAKC,IAAI;IACzD,OAAOH,KAAKI,MAAM,GAAGL,MAAM,GAAGC,KAAKK,KAAK,CAAC,GAAGN,MAAM,GAAGO,OAAO,GAAG,CAAC,CAAC,GAAGN;AACtE;AAEA,IAAA,AAAMO,cAAN,MAAMA;IAcJC,QAAQC,OAAe,EAAsB;QAC3C,MAAMC,OAAO,IAAI,CAACC,UAAU,CAACC,GAAG,CAACH;QACjC,IAAIC,MAAM,OAAOA;QACjB,MAAMG,OAAO,IAAI,CAACC,QAAQ,CAACL,QAAQ;QACnC,IAAI,CAACI,MAAM,OAAO,EAAE;QACpB,MAAME,MAAM,IAAI,CAACC,QAAQ,CAACJ,GAAG,CAACH;QAC9B,MAAMQ,OAAOF,MAAM;YAACA;SAAI,GAAG,EAAE;QAC7B,KAAK,MAAMG,SAASL,KAAKM,QAAQ,CAAEF,KAAKG,IAAI,IAAI,IAAI,CAACZ,OAAO,CAACU;QAC7D,IAAI,CAACP,UAAU,CAACU,GAAG,CAACZ,SAASQ;QAC7B,OAAOA;IACT;IAEAK,SAASC,QAAgB,EAAEd,OAAe,EAAW;;YACd;QAArC,IAAK,IAAIe,KAAKf,SAASe,MAAM,GAAGA,cAAK,oBAAA,IAAI,CAACV,QAAQ,CAACU,GAAG,qBAAjB,kBAAmBC,MAAM,mBAAI,CAAC,EAAG,IAAID,OAAOD,UAAU,OAAO;QAClG,OAAO;IACT;IAEA,+FAA+F,GAC/FG,MAAMjB,OAAe,EAA4C;YACjD;QAAd,MAAMiB,SAAQ,6BAAA,IAAI,CAACC,IAAI,CAACC,SAAS,CAACF,KAAK,YAAzB,6BAA6B,EAAE;QAC7C,OAAOA,MAAMG,MAAM,CAAC,CAACC,QAAU,IAAI,CAACR,QAAQ,CAACb,SAASqB,MAAMrB,OAAO;IACrE;IAEAsB,MAAMnC,IAA0C,EAAiC;QAC/E,OAAO,IAAI,CAACkB,QAAQ,CAACpB,IAAI,CAACE;IAC5B;IAEAoC,WAAWvB,OAAe,EAAiB;QACzC,IAAK,IAAIe,KAAKf,SAASwB,QAAQ,GAAGT,MAAM,KAAKS,QAAQ,GAAGA,SAAS,EAAG;;gBAGtC;YAF5B,MAAMC,QAAQzD,uBAAuB,IAAI,CAAC0D,MAAM,CAACC,EAAE,CAACZ,KAAKU,KAAK;YAC9D,IAAIA,SAASA,MAAMG,CAAC,IAAI,KAAK,OAAO1D,cAAcuD;YAClD,MAAMf,WAAqB,UAAC,oBAAA,IAAI,CAACL,QAAQ,CAACU,GAAG,qBAAjB,kBAAmBL,QAAQ,mBAAI,EAAE,EAAEU,MAAM,CAAC,CAACX;oBAAwC;uBAA9B,IAAI,CAACV,OAAO,CAACU,OAAOd,MAAM,MAAI,uBAAA,IAAI,CAACU,QAAQ,CAACI,MAAM,qBAApB,qBAAsBC,QAAQ,CAACf,MAAM;;YACpJoB,KAAKL,SAASf,MAAM,KAAK,IAAKe,QAAQ,CAAC,EAAE,GAAc,CAAC;QAC1D;QACA,OAAO;IACT;IAEAmB,YAAY7B,OAAe,EAAgC;;YAIhB;QAHzC,MAAMI,OAAO,IAAI,CAACC,QAAQ,CAACL,QAAQ;QACnC,IAAI,CAACI,MAAM,OAAO;QAClB,IAAI0B,QAAuB;QAC3B,IAAK,IAAIf,KAAKX,KAAKY,MAAM,EAAED,MAAM,GAAGA,cAAK,oBAAA,IAAI,CAACV,QAAQ,CAACU,GAAG,qBAAjB,kBAAmBC,MAAM,mBAAI,CAAC,EAAG;YACxE,MAAMS,QAAQzD,uBAAuB,IAAI,CAAC0D,MAAM,CAACC,EAAE,CAACZ,KAAKU,KAAK;YAC9D,IAAIA,SAASA,MAAMG,CAAC,IAAI,KAAK;gBAC3BE,QAAQ5D,cAAcuD;gBACtB;YACF;QACF;QACA,OAAOrD,wBAAwBgC,MAAM,IAAI,CAACsB,MAAM,CAACC,EAAE,CAAC3B,UAAU8B,gBAAAA,QAAS;IACzE;IAzDA,YACE,AAASZ,IAA2B,EACpC,AAASQ,MAAwB,CACjC;aAFSR,OAAAA;aACAQ,SAAAA;QANX,qDAAqD,QACpCxB,aAAa,IAAI6B;aACjBxB,WAAW,IAAIwB;QAM9B,IAAI,CAAC1B,QAAQ,GAAGa,KAAKC,SAAS,CAACd,QAAQ;QACvC,KAAK,MAAM2B,UAAUd,KAAKC,SAAS,CAACpB,OAAO,CAAE,IAAI,CAACQ,QAAQ,CAACK,GAAG,CAACoB,OAAOhC,OAAO,EAAEgC;IACjF;AAoDF;AAEA,SAASC,YAAYf,IAA2B,EAAEgB,WAA8B;IAC9E,MAAMC,MAAMlE,gBAAgB;WAAIiD,KAAKC,SAAS,CAACO,MAAM;WAAKQ;KAAY;IACtE,OAAO,IAAIpC,YAAYoB,MAAM/C,mBAAmB+C,KAAKC,SAAS,EAAEgB;AAClE;AAEA;;;CAGC,GACD,OAAO,SAASC,kBAAkBlB,IAA2B,EAAEgB,cAAiC,EAAE;QAI9FG,MAAAA,eAKAA,OAAAA;QAoBkBA;IA5BpB,IAAI,CAACnB,KAAKC,SAAS,EAAE,OAAO;IAC5B,MAAMkB,SAASJ,YAAYf,MAAMgB;IACjC,MAAMI,UACJD,QAAAA,gBAAAA,OAAOf,KAAK,CAAC,CAAClB,OAASA,KAAKmC,GAAG,KAAK,YAAYnC,KAAKoC,MAAM,KAAK,qBAAhEH,gBACAA,OAAOf,KAAK,CAAC,CAAClB,OAASA,KAAKqC,KAAK,CAACC,IAAI,KAAK,qBAD3CL,OAEAA,OAAOf,KAAK,CAAC,CAAClB,OAASA,KAAKmC,GAAG,KAAK,SAASnC,KAAKoC,MAAM,KAAK;IAC/D,IAAI,CAACF,QAAQ,OAAO;IACpB,MAAMK,OACJN,SAAAA,iBAAAA,OAAOf,KAAK,CAAC,CAAClB,OAASA,KAAKmC,GAAG,KAAK,SAASF,OAAOxB,QAAQ,CAACyB,OAAOM,EAAE,EAAExC,KAAKwC,EAAE,KAAKP,OAAOtC,OAAO,CAACK,KAAKwC,EAAE,EAAEjD,MAAM,IAAI,cAAtH0C,iBACAA,OAAOf,KAAK,CAAC,CAAClB,OAASA,KAAKmC,GAAG,KAAK,SAASnC,KAAKoC,MAAM,KAAK,YAAYH,OAAOtC,OAAO,CAACK,KAAKwC,EAAE,EAAEjD,MAAM,IAAI,cAD3G0C,QAEAC;IACF,8EAA8E;IAC9E,IAAIO,MAAMF,IAAIC,EAAE;IAChB,IAAK,IAAIE,QAAQ,GAAGA,QAAQ,IAAIA,SAAS,EAAG;;YACxBT;QAAlB,MAAM3B,WAAW,WAAC2B,wBAAAA,OAAOhC,QAAQ,CAACwC,IAAI,qBAApBR,sBAAsB3B,QAAQ,oBAAI,EAAE,EAAEU,MAAM,CAAC,CAACX,QAAU4B,OAAOtC,OAAO,CAACU,OAAOd,MAAM,IAAI0C,OAAOpB,KAAK,CAACR,OAAOd,MAAM;QACpI,IAAIe,SAASf,MAAM,KAAK,GAAG;QAC3BkD,MAAMnC,QAAQ,CAAC,EAAE;IACnB;IACA,MAAMqC,QAAQ,CAACf;QACb,IAAI;gBACaA;YAAf,OAAO,IAAIjD,KAAIiD,eAAAA,OAAOnD,IAAI,YAAXmD,eAAe,IAAIgB,QAAQ,KAAK,OAAQ,CAAA,CAAC,CAAChB,OAAOiB,KAAK,IAAI,CAACjB,OAAOkB,KAAK,AAAD;QACvF,EAAE,eAAM;YACN,OAAO;QACT;IACF;IACA,MAAM5E,QAA8B,EAAE;IACtC,MAAM6E,OAA2B,EAAE;IACnC,MAAMC,QAAQ,IAAIC;IAClB,KAAK,MAAM5C,mBAAS4B,uBAAAA,OAAOhC,QAAQ,CAACwC,IAAI,qBAApBR,qBAAsB3B,QAAQ,oBAAI,EAAE,CAAE;YAiBpC4C;QAhBpB,MAAMvD,UAAUsC,OAAOtC,OAAO,CAACU,OAAOW,MAAM,CAAC,CAACY,SAAW,CAACe,MAAMf,WAAYA,CAAAA,OAAOkB,KAAK,IAAIlB,OAAOnD,IAAI,AAAD;QACtG,MAAMoC,QAAQoB,OAAOpB,KAAK,CAACR;QAC3B,IAAI,CAACV,QAAQJ,MAAM,IAAI,CAACsB,MAAMtB,MAAM,EAAE;QACtCI,QAAQwD,OAAO,CAAC,CAACvB,SAAWoB,MAAMI,GAAG,CAACxB;QACtC,kFAAkF;QAClF,IAAIjC,QAAQJ,MAAM,KAAK,KAAK,CAACsB,MAAMtB,MAAM,EAAE;YACzC,MAAMqC,SAASjC,OAAO,CAAC,EAAE;YACzB,MAAM0D,QAAQpB,OAAOR,WAAW,CAACG,OAAOhC,OAAO;YAC/C,IAAIyD,SAASA,UAAU,SAAS;gBAC9BN,KAAKxC,IAAI,CAAC;oBAAEuC,OAAO9D,MAAM4C,OAAOkB,KAAK,EAAEnF,iCAAiC;oBAAIc,MAAMmD,OAAOnD,IAAI;oBAAE4E;gBAAM;gBACrG;YACF;QACF;QACA,mFAAmF;QACnF,MAAMC,UAAU3D,OAAO,CAAC,EAAE;QAC1B,MAAMuD,SAASrC,MAAMhC,IAAI,CAAC,CAACoC,QAAU,CAACqC,WAAWrC,MAAMrB,OAAO,GAAG0D,QAAQ1D,OAAO;QAChF,MAAMkD,QAAQ9D,OAAMkE,iBAAAA,0BAAAA,OAAQjE,IAAI,oBAAIqE,2BAAAA,QAASR,KAAK,YAA9BI,QAAkC,IAAIvF,iCAAiC;QAC3F,IAAI,CAACmF,OAAO;QACZ,MAAM3E,UAAU,AAAC+E,CAAAA,SAASvD,UAAUA,QAAQH,KAAK,CAAC,EAAC,EAAGA,KAAK,CAAC,GAAGvB,uBAAuBE,OAAO;QAC7FD,MAAMqC,IAAI,CAAC;YACTuC;YACArE,MAAMyE,SAAS,gBAAQI,2BAAAA,QAAS7E,IAAI,oBAAI;YACxCP,OAAOC,QAAQ6C,MAAM,CAAC,CAACY,SAAWA,OAAOnD,IAAI,EAAE8E,GAAG,CAAC,CAAC3B,SAAW4B,SAASvB,QAAQL,QAAQvB;QAC1F;QACA,IAAInC,MAAMqB,MAAM,IAAItB,uBAAuBC,KAAK,EAAE;IACpD;IACA,6CAA6C;IAC7C,KAAK,MAAM0D,UAAUK,OAAOtC,OAAO,CAACuC,OAAOM,EAAE,EAAG;QAC9C,IAAIQ,MAAMS,GAAG,CAAC7B,WAAWe,MAAMf,WAAW,CAACA,OAAOkB,KAAK,IAAIb,OAAOxB,QAAQ,CAACgC,KAAKb,OAAOhC,OAAO,GAAG;QACjG,MAAMyD,QAAQpB,OAAOR,WAAW,CAACG,OAAOhC,OAAO;QAC/C,IAAI,CAACyD,OAAO;QACZ,IAAI,CAACN,KAAKW,IAAI,CAAC,CAACC,MAAQA,IAAIb,KAAK,KAAKlB,OAAOkB,KAAK,IAAIa,IAAIlF,IAAI,KAAKmD,OAAOnD,IAAI,GAAGsE,KAAKxC,IAAI,CAAC;YAAEuC,OAAO9D,MAAM4C,OAAOkB,KAAK,EAAEnF,iCAAiC;YAAIc,MAAMmD,OAAOnD,IAAI;YAAE4E;QAAM;IACxL;IACA,MAAMO,YAAY3B,OAAOhC,QAAQ,CAACyD,IAAI,CAAC,CAAC1D;YAGSA,mBACxBA,mBAAiDA,oBAAmDA;QAH3H,IAAIA,KAAKoC,MAAM,KAAK,YAAY,CAACH,OAAOxB,QAAQ,CAACyB,OAAOM,EAAE,EAAExC,KAAKwC,EAAE,GAAG,OAAO;QAC7E,IAAIxC,KAAKmC,GAAG,KAAK,YAAYnC,KAAKqC,KAAK,CAACC,IAAI,KAAK,YAAYtC,KAAKmC,GAAG,KAAK,aAAanC,KAAKmC,GAAG,KAAK,KAAK,OAAO;QAChH,IAAInC,KAAKmC,GAAG,KAAK,OAAO,CAAC7D,iBAAiBS,IAAI,EAACiB,oBAAAA,KAAKqC,KAAK,CAACwB,KAAK,YAAhB7D,oBAAoB,KAAK,OAAO;QAC/E,OAAO3B,WAAWU,IAAI,EAACiB,oBAAAA,KAAKqC,KAAK,CAACS,KAAK,YAAhB9C,oBAAoB,OAAO1B,iBAAiBS,IAAI,EAACiB,qBAAAA,KAAKqC,KAAK,CAACwB,KAAK,YAAhB7D,qBAAoB,OAAO,mBAAmBjB,IAAI,EAACiB,uBAAAA,KAAKqC,KAAK,CAACyB,QAAQ,YAAnB9D,uBAAuB;IACpJ;IACA,IAAI,CAAC9B,MAAMqB,MAAM,IAAI,CAACwD,KAAKxD,MAAM,EAAE,OAAO;IAC1C,OAAO;QAAErB;QAAO6E;QAAMa;QAAWzC,YAAYc,OAAOd,UAAU,CAACe,OAAOM,EAAE;IAAE;AAC5E;AAEA,oEAAoE,GACpE,SAASgB,SAASvB,MAAmB,EAAEL,MAAwB,EAAEmC,IAAY;IAC3E,MAAMC,OAAOpC,OAAOoC,IAAI,KAAKC,YAAYhC,OAAOnB,IAAI,CAACC,SAAS,CAACmD,KAAK,CAACtC,OAAOoC,IAAI,CAAC,GAAGC;IACpF,MAAME,SAASC,cAAcnC,QAAQL,OAAOhC,OAAO,EAAEmE;IACrD,OAAO;QACLjB,OAAO9D,MAAM4C,OAAOkB,KAAK,EAAEnF,iCAAiC;OACxDiE,OAAO3C,IAAI,GAAG;QAAEA,MAAMD,MAAM4C,OAAO3C,IAAI;IAAE,IAAI,CAAC;QAClDR,MAAMmD,OAAOnD,IAAI;OACbuF,CAAAA,wBAAAA,KAAMK,IAAI,IAAG;QAAEC,UAAUN,KAAKK,IAAI;QAAEE,SAASP,KAAKO,OAAO;IAAC,IAAI,CAAC,GAC/DJ,SAAS;QAAEA;IAAO,IAAI,CAAC;AAE/B;AAEA;;;;CAIC,GACD,SAASC,cAAcnC,MAAmB,EAAErC,OAAe,EAAEmE,IAAY;;QAEzD9B,0BAAqEA;IADnF,MAAM/B,MAAM+B,OAAOpB,KAAK,CAACkD,MAAM/C,MAAM,CAAC,CAACC,QAAU,CAACgB,OAAOtC,OAAO,CAACoE,MAAML,IAAI,CAAC,CAAC9B,SAAWK,OAAOxB,QAAQ,CAACmB,OAAOhC,OAAO,EAAEqB,MAAMrB,OAAO;IACrI,IAAK,IAAIe,cAAKsB,2BAAAA,OAAOhC,QAAQ,CAACL,QAAQ,qBAAxBqC,yBAA0BrB,MAAM,mBAAI,CAAC,GAAGD,MAAM,KAAKA,OAAOoD,MAAMpD,eAAKsB,sBAAAA,OAAOhC,QAAQ,CAACU,GAAG,qBAAnBsB,oBAAqBrB,MAAM,oBAAI,CAAC,EAAG;QACpH,MAAMM,QAAQhB,IAAIrB,IAAI,CAAC,CAACoC,QAAUgB,OAAOxB,QAAQ,CAACE,IAAIM,MAAMrB,OAAO;QACnE,IAAI,CAACsB,OAAO;QACZ,MAAMsD,YAAYvC,OAAOtC,OAAO,CAACgB,GAAG,CAAC,EAAE;QACvC,IAAI6D,aAAatD,MAAMtB,OAAO,GAAG4E,UAAU5E,OAAO,EAAE,OAAO;QAC3D,OAAOsB,MAAMjC,IAAI,CAACM,MAAM,IAAI5B,iCAAiCqB,MAAMkC,MAAMjC,IAAI,EAAEtB,kCAAkC;IACnH;IACA,OAAO;AACT;AAEA,MAAM8G,QAAQ;AACd,MAAMC,QAAQ;AACd,MAAMC,UAAU;AAEhB;;;CAGC,GACD,OAAO,SAASC,wBAAwB9D,IAA2B,EAAEgB,cAAiC,EAAE;IACtG,IAAI,CAAChB,KAAKC,SAAS,EAAE,OAAO;IAC5B,MAAMkB,SAASJ,YAAYf,MAAMgB;IACjC,MAAM+C,UAAU5C,OAAOhC,QAAQ,CAACe,MAAM,CAAC,CAAChB,OAAS,AAACA,CAAAA,KAAKmC,GAAG,KAAK,YAAYnC,KAAKqC,KAAK,CAACC,IAAI,KAAK,aAAY,KAAMtC,KAAKoC,MAAM,KAAK;IACjI,MAAM0C,SAASD,QAAQhG,IAAI,CAAC,CAACmB,OAAS,CAAC6E,QAAQnB,IAAI,CAAC,CAACqB,QAAUA,UAAU/E,QAAQiC,OAAOxB,QAAQ,CAACsE,MAAMvC,EAAE,EAAExC,KAAKwC,EAAE;IAClH,IAAI,CAACsC,QAAQ,OAAO;IACpB,MAAM,EAAEE,MAAM,EAAEjE,SAAS,EAAE,GAAGD;IAG9B,MAAMmE,SAAiB,EAAE;IACzBlE,UAAUmE,YAAY,CAAC/B,OAAO,CAAC,CAACvD,SAASuF;QACvC,MAAMC,QAAQJ,MAAM,CAACG,MAAM;QAC3B,IAAI,CAACC,SAASA,MAAMhD,MAAM,KAAK,YAAYgD,MAAMC,IAAI,KAAK,WAAWD,MAAMC,IAAI,KAAK,QAAQ;QAC5F,qDAAqD;QACrD,MAAMC,OAAON,MAAM,CAACG,QAAQ,EAAE;QAC9B,IAAIG,CAAAA,wBAAAA,KAAMD,IAAI,MAAK,UAAUC,KAAKC,MAAM,IAAID,KAAKrG,IAAI,KAAKmG,MAAMnG,IAAI,EAAE;QACtE,IAAIgD,OAAOxB,QAAQ,CAACqE,OAAOtC,EAAE,EAAE5C,UAAUqF,OAAO1E,IAAI,CAAC;YAAEX;YAASX,MAAMD,MAAMoG,MAAMnG,IAAI,EAAE;YAAQR,MAAM;QAAK;IAC7G;IACA,KAAK,MAAMwC,SAASgB,OAAOpB,KAAK,CAACiE,OAAOtC,EAAE,EAAGyC,OAAO1E,IAAI,CAAC;QAAEX,SAASqB,MAAMrB,OAAO;QAAEX,MAAMD,MAAMiC,MAAMhC,IAAI,EAAE;QAAQR,MAAM;IAAK;IAC9H,KAAK,MAAMmD,UAAUK,OAAOtC,OAAO,CAACmF,OAAOtC,EAAE,EAAG;YAEhBP;QAD9B,2DAA2D;QAC3D,MAAMa,QAAQlB,OAAOkB,KAAK,MAAIb,kCAAAA,OAAOhC,QAAQ,CAAC2B,OAAOhC,OAAO,CAAC,qBAA/BqC,gCAAiCI,KAAK,CAACS,KAAK,KAAI;QAC9E,IAAIlB,OAAOnD,IAAI,IAAIqE,OAAOmC,OAAO1E,IAAI,CAAC;YAAEX,SAASgC,OAAOhC,OAAO;YAAEX,MAAMD,MAAM8D;YAAQrE,MAAMmD,OAAOnD,IAAI;QAAC;IACzG;IACAwG,OAAOO,IAAI,CAAC,CAAChE,GAAGiE,IAAMjE,EAAE5B,OAAO,GAAG6F,EAAE7F,OAAO;IAC3C,MAAM8F,SAAS,CAAC9F,UAAoBqF,OAAOjE,MAAM,CAAC,CAAC2E,OAAS1D,OAAOxB,QAAQ,CAACb,SAAS+F,KAAK/F,OAAO;IACjG,qDAAqD;IACrD,MAAMgG,UAAuC,EAAE;IAC/C,MAAMC,YAAY,IAAI5C;IACtB,KAAK,MAAMjD,QAAQiC,OAAOhC,QAAQ,CAAE;QAClC,IAAI,CAACgC,OAAOxB,QAAQ,CAACqE,OAAOtC,EAAE,EAAExC,KAAKwC,EAAE,KAAKxC,KAAKwC,EAAE,KAAKsC,OAAOtC,EAAE,EAAE;QACnE,MAAMtC,MAAMwF,OAAO1F,KAAKwC,EAAE;QAC1B,MAAM,CAACsD,SAAS,GAAGC,KAAK,GAAG7F;QAC3B,IAAI,CAAC4F,WAAWA,QAAQrH,IAAI,IAAIqH,QAAQ7G,IAAI,CAACM,MAAM,GAAG5B,kCAAkCoI,KAAKxG,MAAM,GAAG,KAAKwG,KAAKrC,IAAI,CAAC,CAACiC,OAAS,CAACA,KAAKlH,IAAI,GAAG;QAC5I,IAAIyB,IAAIwD,IAAI,CAAC,CAACiC,OAASE,UAAUpC,GAAG,CAACkC,QAAQ;QAC7CzF,IAAIiD,OAAO,CAAC,CAACwC,OAASE,UAAUzC,GAAG,CAACuC;QACpCC,QAAQrF,IAAI,CAAC;YAAEuF,SAASA,QAAQ7G,IAAI;YAAE+G,OAAOD,KAAKvG,KAAK,CAAC,GAAGvB,uBAAuBG,WAAW,EAAEmF,GAAG,CAAC,CAACoC,OAAU,CAAA;oBAAE7C,OAAO6C,KAAK1G,IAAI;oBAAER,MAAMkH,KAAKlH,IAAI;gBAAW,CAAA;QAAI;IAClK;IACA,MAAMwH,SAAqC,EAAE;IAC7C,MAAMD,QAAgC,EAAE;IACxC,MAAM/G,OAAiB,EAAE;IACzB,IAAIiH,YAA2B;IAC/B,IAAIC,UAAyB;IAC7B,IAAIC,QAAuB;IAC3B,IAAIC,QAAuB;IAC3B,KAAK,MAAMV,QAAQV,OAAQ;QACzB,IAAIY,UAAUpC,GAAG,CAACkC,OAAO;QACzB,IAAIA,KAAKlH,IAAI,EAAE;YACb,MAAM6H,UAAU9H,wBAAwBmH,KAAKlH,IAAI;YACjD,IAAI6H,SAAS;gBACX,IAAI,CAACL,OAAOvC,IAAI,CAAC,CAACzC,QAAUA,MAAMxC,IAAI,KAAKkH,KAAKlH,IAAI,GAAGwH,OAAO1F,IAAI,CAAC;oBAAEuC,OAAO6C,KAAK1G,IAAI;oBAAER,MAAMkH,KAAKlH,IAAI;oBAAE6H;gBAAQ;YAClH,OAAO,IAAIX,KAAKlH,IAAI,CAAC8H,UAAU,CAAC,YAAYF,gBAAAA,QAAAA,QAAUV,KAAKlH,IAAI,CAACe,KAAK,CAAC,UAAUD,MAAM;iBACjF,IAAIoG,KAAKlH,IAAI,CAAC8H,UAAU,CAAC,SAASH,gBAAAA,QAAAA,QAAUT,KAAK1G,IAAI;iBACrD,IAAI,CAAC+G,MAAMtC,IAAI,CAAC,CAACzC,QAAUA,MAAMxC,IAAI,KAAKkH,KAAKlH,IAAI,IAAIwC,MAAM6B,KAAK,KAAK6C,KAAK1G,IAAI,GAAG+G,MAAMzF,IAAI,CAAC;gBAAEuC,OAAO6C,KAAK1G,IAAI;gBAAER,MAAMkH,KAAKlH,IAAI;YAAC;YACvI;QACF;QACA,IAAI,CAACyH,aAAa,iCAAiCnH,IAAI,CAAC4G,KAAK1G,IAAI,GAAG;YAClEiH,YAAYP,KAAK1G,IAAI;YACrB;QACF;QACA,IAAI,CAACoH,SAAS5B,MAAM1F,IAAI,CAAC4G,KAAK1G,IAAI,KAAK0G,KAAK1G,IAAI,CAACM,MAAM,IAAI,KAAK8G,QAAQ,AAAC5B,MAAM+B,IAAI,CAACb,KAAK1G,IAAI,CAAqB,CAAC,EAAE;aAChH,IAAI,CAACmH,SAAS1B,MAAM3F,IAAI,CAAC4G,KAAK1G,IAAI,KAAK0G,KAAK1G,IAAI,CAACM,MAAM,IAAI,IAAI6G,QAAQT,KAAK1G,IAAI;aAChF,IAAI,CAACkH,WAAWxB,QAAQ5F,IAAI,CAAC4G,KAAK1G,IAAI,KAAK0G,KAAK1G,IAAI,CAACM,MAAM,IAAI,KAAK4G,UAAUR,KAAK1G,IAAI;aACvF,IAAI,CAACA,KAAKwH,QAAQ,CAACd,KAAK1G,IAAI,GAAGA,KAAKsB,IAAI,CAACoF,KAAK1G,IAAI;IACzD;IACA,uEAAuE;IACvE,MAAMyH,eAAe5F,KAAK4F,YAAY;IACtC,OAAO;QACLd;QACAK;QACAD;QACA/G;QACAkH,OAAO,EAAEA,kBAAAA,UAAYO,CAAAA,gCAAAA,aAAcP,OAAO,KAAI;QAC9CC,KAAK,EAAEA,gBAAAA,QAAUM,CAAAA,gCAAAA,aAAcC,SAAS,KAAI;QAC5CN,KAAK,EAAEA,gBAAAA,QAAUK,CAAAA,gCAAAA,aAAcL,KAAK,KAAI;QACxCH;IACF;AACF"}
|
|
@@ -44,8 +44,15 @@ export declare function siteImportPageImages(page: Pick<SiteImportPage, 'blocks'
|
|
|
44
44
|
export declare function siteImportPageSections(page: Pick<SiteImportPage, 'title' | 'blocks'>): string[];
|
|
45
45
|
/** The page's text in order, as a prompt shows it inside its fence. */
|
|
46
46
|
export declare function siteImportPageText(page: Pick<SiteImportPage, 'title' | 'description' | 'blocks'>): string;
|
|
47
|
-
/**
|
|
47
|
+
/**
|
|
48
|
+
* The page as a wave reads it: its main region's blocks whole, within the
|
|
49
|
+
* read's budget, and how the source lays them out (AGL-4106) over the blocks
|
|
50
|
+
* kept, its icons' drawings dropped first when it runs past its own budget.
|
|
51
|
+
*/
|
|
48
52
|
export declare function siteImportPageRead(page: SiteImportPage, at: string): AiSiteImportPageRead;
|
|
53
|
+
/** The most characters of a source's title and description a read keeps: far past what any listing shows. */
|
|
54
|
+
export declare const SITE_IMPORT_META_TITLE_MAX_CHARS = 300;
|
|
55
|
+
export declare const SITE_IMPORT_META_DESCRIPTION_MAX_CHARS = 1000;
|
|
49
56
|
/**
|
|
50
57
|
* How the source's header draws its brand: its logo alone (aglyn.com), its
|
|
51
58
|
* logo with its name beside it, or its name alone. `text` is the name as the
|
|
@@ -36,7 +36,7 @@ import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
|
36
36
|
* its own facts (`siteImportSiteFacts`), for the header and footer the import
|
|
37
37
|
* builds.
|
|
38
38
|
*=========================================*/ import { neutralizeUntrustedText } from "@aglyn/shared-util-tools/untrusted-text";
|
|
39
|
-
import { AI_SITE_IMPORT_BLOCKED_STATUSES, AI_SITE_IMPORT_IMAGES_PER_PAGE, AI_SITE_IMPORT_LABEL_MAX_CHARS, AI_SITE_IMPORT_MAX_SECTIONS, AI_SITE_IMPORT_PAGE_TEXT_MAX_CHARS, AI_SITE_IMPORT_READ_MAX_CHARS, aiSiteImportCapBlocks } from "../model/ai-site-import.js";
|
|
39
|
+
import { AI_SITE_IMPORT_BLOCKED_STATUSES, AI_SITE_IMPORT_IMAGES_PER_PAGE, AI_SITE_IMPORT_LABEL_MAX_CHARS, AI_SITE_IMPORT_MAX_SECTIONS, AI_SITE_IMPORT_PAGE_TEXT_MAX_CHARS, AI_SITE_IMPORT_READ_MAX_CHARS, aiSiteImportCapBlocks, AI_SITE_IMPORT_LAYOUT_MAX_CHARS, siteImportLayoutChars, siteImportLayoutWithin, siteImportLayoutWithoutDrawings } from "../model/ai-site-import.js";
|
|
40
40
|
import { siteImportLogoCandidates } from "./site-import-images.js";
|
|
41
41
|
/** A line of untrusted text safe to show outside a fence: neutralized, one line, cut short. */ export function siteImportLabel(text, max = AI_SITE_IMPORT_LABEL_MAX_CHARS) {
|
|
42
42
|
const line = neutralizeUntrustedText(text).replace(/\s+/g, ' ').trim();
|
|
@@ -135,9 +135,16 @@ const wordsIn = (text)=>{
|
|
|
135
135
|
const text = lines.join('\n');
|
|
136
136
|
return text.length > AI_SITE_IMPORT_PAGE_TEXT_MAX_CHARS ? text.slice(0, AI_SITE_IMPORT_PAGE_TEXT_MAX_CHARS) : text;
|
|
137
137
|
}
|
|
138
|
-
/**
|
|
138
|
+
/**
|
|
139
|
+
* The page as a wave reads it: its main region's blocks whole, within the
|
|
140
|
+
* read's budget, and how the source lays them out (AGL-4106) over the blocks
|
|
141
|
+
* kept, its icons' drawings dropped first when it runs past its own budget.
|
|
142
|
+
*/ export function siteImportPageRead(page, at) {
|
|
139
143
|
const main = page.blocks.filter((block)=>block.region === 'main');
|
|
140
144
|
const capped = aiSiteImportCapBlocks(main, AI_SITE_IMPORT_READ_MAX_CHARS);
|
|
145
|
+
let layout = page.layout ? siteImportLayoutWithin(page.layout, capped.blocks.length) : null;
|
|
146
|
+
if (layout && siteImportLayoutChars(layout) > AI_SITE_IMPORT_LAYOUT_MAX_CHARS) layout = siteImportLayoutWithoutDrawings(layout);
|
|
147
|
+
if (layout && siteImportLayoutChars(layout) > AI_SITE_IMPORT_LAYOUT_MAX_CHARS) layout = null;
|
|
141
148
|
return _extends({
|
|
142
149
|
at,
|
|
143
150
|
title: siteImportPageTitle(page),
|
|
@@ -153,8 +160,21 @@ const wordsIn = (text)=>{
|
|
|
153
160
|
blocks: main
|
|
154
161
|
}),
|
|
155
162
|
forms: page.forms.kept
|
|
163
|
+
}, layout ? {
|
|
164
|
+
layout
|
|
165
|
+
} : {}, {
|
|
166
|
+
// The source's own title and description, for the page's listing (AGL-4107).
|
|
167
|
+
meta: {
|
|
168
|
+
title: siteImportMetaText(page.title, SITE_IMPORT_META_TITLE_MAX_CHARS),
|
|
169
|
+
description: siteImportMetaText(page.description, SITE_IMPORT_META_DESCRIPTION_MAX_CHARS)
|
|
170
|
+
}
|
|
156
171
|
});
|
|
157
172
|
}
|
|
173
|
+
/** The most characters of a source's title and description a read keeps: far past what any listing shows. */ export const SITE_IMPORT_META_TITLE_MAX_CHARS = 300;
|
|
174
|
+
export const SITE_IMPORT_META_DESCRIPTION_MAX_CHARS = 1000;
|
|
175
|
+
/** A title or a description as the source writes it, one line, neutralized; a longer one is kept to its ceiling. */ function siteImportMetaText(text, max) {
|
|
176
|
+
return neutralizeUntrustedText(String(text != null ? text : '')).replace(/\s+/g, ' ').trim().slice(0, max);
|
|
177
|
+
}
|
|
158
178
|
/** The site's own name: its head's, its structured data's, else its home page's title before a separator. */ export function siteImportSiteName(home) {
|
|
159
179
|
var _home_title;
|
|
160
180
|
var _home_organization;
|
|
@@ -252,6 +272,8 @@ const SOCIAL_HOSTS = /(?:^|\.)(?:twitter\.com|x\.com|facebook\.com|instagram\.co
|
|
|
252
272
|
return status !== undefined && AI_SITE_IMPORT_BLOCKED_STATUSES.has(status) ? 'blocked' : 'unreadable';
|
|
253
273
|
case 'not-reached':
|
|
254
274
|
return 'not-reached';
|
|
275
|
+
case 'not-html':
|
|
276
|
+
return 'file';
|
|
255
277
|
default:
|
|
256
278
|
return 'unreadable';
|
|
257
279
|
}
|