@aglyn/plugins-ai 1.0.0-beta.263 → 1.0.0-beta.266
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/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/components/ai-site-start-card.component.d.ts +12 -0
- package/src/lib/components/ai-site-start-card.component.js +256 -236
- package/src/lib/components/ai-site-start-card.component.js.map +1 -1
- package/src/lib/components/ai-site-start-steps.component.d.ts +108 -0
- package/src/lib/components/ai-site-start-steps.component.js +451 -0
- package/src/lib/components/ai-site-start-steps.component.js.map +1 -0
- package/src/lib/components/ai-site-start-tracking.d.ts +114 -0
- package/src/lib/components/ai-site-start-tracking.js +277 -0
- package/src/lib/components/ai-site-start-tracking.js.map +1 -0
- package/src/lib/components/ai-site-style-previews.d.ts +26 -0
- package/src/lib/components/ai-site-style-previews.js +115 -0
- package/src/lib/components/ai-site-style-previews.js.map +1 -0
- package/src/lib/insights/ai-insight-digest.d.ts +9 -0
- package/src/lib/insights/ai-insight-digest.js +35 -11
- package/src/lib/insights/ai-insight-digest.js.map +1 -1
- package/src/lib/jobs/ai-job-page-language.d.ts +9 -0
- package/src/lib/jobs/ai-job-page-language.js +20 -2
- package/src/lib/jobs/ai-job-page-language.js.map +1 -1
- package/src/lib/jobs/ai-job-page-sections.js +8 -2
- package/src/lib/jobs/ai-job-page-sections.js.map +1 -1
- package/src/lib/jobs/ai-job-plan-step.d.ts +43 -8
- package/src/lib/jobs/ai-job-plan-step.js +99 -18
- package/src/lib/jobs/ai-job-plan-step.js.map +1 -1
- package/src/lib/jobs/ai-job-site-datasets.d.ts +93 -2
- package/src/lib/jobs/ai-job-site-datasets.js +308 -21
- package/src/lib/jobs/ai-job-site-datasets.js.map +1 -1
- package/src/lib/jobs/ai-layout-stock-photos.d.ts +191 -13
- package/src/lib/jobs/ai-layout-stock-photos.js +1709 -69
- package/src/lib/jobs/ai-layout-stock-photos.js.map +1 -1
- package/src/lib/jobs/fixtures/ai-site-foreign-datasets-recording.d.ts +58 -0
- package/src/lib/jobs/fixtures/ai-site-foreign-datasets-recording.js +678 -0
- package/src/lib/jobs/fixtures/ai-site-foreign-datasets-recording.js.map +1 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-agl3856-beta265-recordings.d.ts +33 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-agl3856-beta265-recordings.js +270 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-agl3856-beta265-recordings.js.map +1 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-agl3856-people-recordings.d.ts +33 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-agl3856-people-recordings.js +277 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-agl3856-people-recordings.js.map +1 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-juniper-records-recordings.d.ts +23 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-juniper-records-recordings.js +163 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-juniper-records-recordings.js.map +1 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-learning-center-recordings.d.ts +23 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-learning-center-recordings.js +53 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-learning-center-recordings.js.map +1 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-synchronicity-recordings.d.ts +21 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-synchronicity-recordings.js +119 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-synchronicity-recordings.js.map +1 -0
- package/src/lib/layout-language/ai-layout-compiler.js +54 -15
- package/src/lib/layout-language/ai-layout-compiler.js.map +1 -1
- package/src/lib/layout-language/ai-layout-design.d.ts +6 -3
- package/src/lib/layout-language/ai-layout-design.js +14 -13
- package/src/lib/layout-language/ai-layout-design.js.map +1 -1
- package/src/lib/layout-language/ai-layout-listings.d.ts +55 -0
- package/src/lib/layout-language/ai-layout-listings.js +103 -5
- package/src/lib/layout-language/ai-layout-listings.js.map +1 -1
- package/src/lib/layout-language/ai-layout-pictures.d.ts +42 -8
- package/src/lib/layout-language/ai-layout-pictures.js +91 -6
- package/src/lib/layout-language/ai-layout-pictures.js.map +1 -1
- package/src/lib/model/ai-site-home-arcs.d.ts +75 -0
- package/src/lib/model/ai-site-home-arcs.js +741 -0
- package/src/lib/model/ai-site-home-arcs.js.map +1 -0
- package/src/lib/model/ai-site-inventory.d.ts +18 -0
- package/src/lib/model/ai-site-inventory.js.map +1 -1
- package/src/lib/model/ai-site-job.d.ts +9 -6
- package/src/lib/model/ai-site-job.js +8 -5
- package/src/lib/model/ai-site-job.js.map +1 -1
- package/src/lib/model/ai-site-kinds.js +24 -24
- package/src/lib/model/ai-site-kinds.js.map +1 -1
- package/src/lib/model/ai-site-start-steps.d.ts +67 -0
- package/src/lib/model/ai-site-start-steps.js +127 -0
- package/src/lib/model/ai-site-start-steps.js.map +1 -0
- package/src/lib/model/ai-site-start.js +4 -1
- package/src/lib/model/ai-site-start.js.map +1 -1
- package/src/lib/runtime/ai-doctrine-validators.d.ts +32 -0
- package/src/lib/runtime/ai-doctrine-validators.js +119 -2
- package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
- package/src/lib/runtime/ai-doctrine.d.ts +6 -1
- package/src/lib/runtime/ai-doctrine.js +9 -5
- package/src/lib/runtime/ai-doctrine.js.map +1 -1
- package/src/lib/runtime/ai-palette.generated.js +5 -2
- package/src/lib/runtime/ai-palette.generated.js.map +1 -1
- package/src/lib/runtime/site-inventory.js +6 -0
- package/src/lib/runtime/site-inventory.js.map +1 -1
- package/src/lib/server/ai-insight-digest-route.js +2 -2
- package/src/lib/server/ai-insight-digest-route.js.map +1 -1
- package/src/lib/usage/assist-free-taste.js +3 -0
- package/src/lib/usage/assist-free-taste.js.map +1 -1
|
@@ -132,6 +132,14 @@ import { aiStockBusinessNoun } from "./ai-stock-business-nouns.js";
|
|
|
132
132
|
* ("wedding" of "a wedding and portrait photographer") is not the craft;
|
|
133
133
|
* and a hero whose second tag names the world too leads one whose first
|
|
134
134
|
* alone does.
|
|
135
|
+
* 9. **A world from the name** (the beta.261 Synchronicity Gym start,
|
|
136
|
+
* AGL-3856: its business type "a local service business" named no world,
|
|
137
|
+
* so its hero searched nothing and was left a black scrim, and its
|
|
138
|
+
* closing band's "Ready to train" took a subway train). A business type
|
|
139
|
+
* that names no world takes the craft its name, else its audience, names
|
|
140
|
+
* where this module lists it ("gym" of "Synchronicity Gym"); and a
|
|
141
|
+
* training word's other senses (a railway's train, an elevator's lift, a
|
|
142
|
+
* newspaper's press, a boat's row) never fill a gym's picture.
|
|
135
143
|
*
|
|
136
144
|
* A slot the library could not fill because it never answered — down, or
|
|
137
145
|
* out of time before its first answer — or because the site's library
|
|
@@ -1208,7 +1216,8 @@ const words = (list)=>new Set(list.trim().split(/\s+/));
|
|
|
1208
1216
|
'boxer',
|
|
1209
1217
|
'beach',
|
|
1210
1218
|
'circus',
|
|
1211
|
-
// Another sport's ball, never "medicine ball": a basketball court covered a deadlift night (AGL-3864)
|
|
1219
|
+
// Another sport's ball, never "medicine ball": a basketball court covered a deadlift night (AGL-3864),
|
|
1220
|
+
// a cricket ball on a wooden court another (AGL-3856).
|
|
1212
1221
|
'basketball',
|
|
1213
1222
|
'football',
|
|
1214
1223
|
'soccer',
|
|
@@ -1217,7 +1226,58 @@ const words = (list)=>new Set(list.trim().split(/\s+/));
|
|
|
1217
1226
|
'tennis',
|
|
1218
1227
|
'golf',
|
|
1219
1228
|
'rugby',
|
|
1220
|
-
'hockey'
|
|
1229
|
+
'hockey',
|
|
1230
|
+
'cricket',
|
|
1231
|
+
'squash',
|
|
1232
|
+
'badminton',
|
|
1233
|
+
'shuttlecock',
|
|
1234
|
+
'bowling',
|
|
1235
|
+
'billiards',
|
|
1236
|
+
'snooker',
|
|
1237
|
+
'pool',
|
|
1238
|
+
'lacrosse',
|
|
1239
|
+
'softball',
|
|
1240
|
+
'handball',
|
|
1241
|
+
'pingpong',
|
|
1242
|
+
'pong'
|
|
1243
|
+
],
|
|
1244
|
+
// A training word's other senses (AGL-3856): "train" is a railway's, "lift"
|
|
1245
|
+
// an elevator's or a ski lift's, "press" a newspaper's, "row" a boat's. The
|
|
1246
|
+
// beta.261 Synchronicity Gym's closing band, "Ready to train", took a
|
|
1247
|
+
// subway train at a station.
|
|
1248
|
+
shun: [
|
|
1249
|
+
'railway',
|
|
1250
|
+
'railroad',
|
|
1251
|
+
'rail',
|
|
1252
|
+
'locomotive',
|
|
1253
|
+
'subway',
|
|
1254
|
+
'metro',
|
|
1255
|
+
'tram',
|
|
1256
|
+
'tramway',
|
|
1257
|
+
'commuter',
|
|
1258
|
+
'freight',
|
|
1259
|
+
'transport',
|
|
1260
|
+
'transportation',
|
|
1261
|
+
'traffic',
|
|
1262
|
+
'vehicle',
|
|
1263
|
+
'bus',
|
|
1264
|
+
'truck',
|
|
1265
|
+
'airplane',
|
|
1266
|
+
'aircraft',
|
|
1267
|
+
'elevator',
|
|
1268
|
+
'escalator',
|
|
1269
|
+
'chairlift',
|
|
1270
|
+
'forklift',
|
|
1271
|
+
'crane',
|
|
1272
|
+
'newspaper',
|
|
1273
|
+
'journalism',
|
|
1274
|
+
'journalist',
|
|
1275
|
+
'printing',
|
|
1276
|
+
'boat',
|
|
1277
|
+
'ship',
|
|
1278
|
+
'canoe',
|
|
1279
|
+
'kayak',
|
|
1280
|
+
'sailboat'
|
|
1221
1281
|
]
|
|
1222
1282
|
},
|
|
1223
1283
|
{
|
|
@@ -1230,6 +1290,20 @@ const words = (list)=>new Set(list.trim().split(/\s+/));
|
|
|
1230
1290
|
'photo',
|
|
1231
1291
|
'photograph'
|
|
1232
1292
|
],
|
|
1293
|
+
scenes: true,
|
|
1294
|
+
maker: [
|
|
1295
|
+
'photographer',
|
|
1296
|
+
'photographers',
|
|
1297
|
+
'camera',
|
|
1298
|
+
'cameras',
|
|
1299
|
+
'lens',
|
|
1300
|
+
'lenses',
|
|
1301
|
+
'tripod',
|
|
1302
|
+
'dslr',
|
|
1303
|
+
'slr',
|
|
1304
|
+
'journalist',
|
|
1305
|
+
'paparazzi'
|
|
1306
|
+
],
|
|
1233
1307
|
kin: [
|
|
1234
1308
|
'photographer',
|
|
1235
1309
|
'photography',
|
|
@@ -1415,7 +1489,7 @@ const words = (list)=>new Set(list.trim().split(/\s+/));
|
|
|
1415
1489
|
});
|
|
1416
1490
|
}
|
|
1417
1491
|
if (!broad.length) broad.push(business, craft);
|
|
1418
|
-
return {
|
|
1492
|
+
return _extends({
|
|
1419
1493
|
business,
|
|
1420
1494
|
craft,
|
|
1421
1495
|
domain: [
|
|
@@ -1426,7 +1500,20 @@ const words = (list)=>new Set(list.trim().split(/\s+/));
|
|
|
1426
1500
|
],
|
|
1427
1501
|
making: [
|
|
1428
1502
|
...new Set(makingSearches)
|
|
1429
|
-
]
|
|
1503
|
+
]
|
|
1504
|
+
}, groups.some((group)=>group.scenes) ? {
|
|
1505
|
+
scenes: true
|
|
1506
|
+
} : {}, groups.some((group)=>{
|
|
1507
|
+
var _group_maker;
|
|
1508
|
+
return (_group_maker = group.maker) == null ? void 0 : _group_maker.length;
|
|
1509
|
+
}) ? {
|
|
1510
|
+
maker: [
|
|
1511
|
+
...new Set(groups.flatMap((group)=>{
|
|
1512
|
+
var _group_maker;
|
|
1513
|
+
return (_group_maker = group.maker) != null ? _group_maker : [];
|
|
1514
|
+
}).map(aiStockStem))
|
|
1515
|
+
]
|
|
1516
|
+
} : {}, {
|
|
1430
1517
|
named: [
|
|
1431
1518
|
...new Set(stemList(businessType))
|
|
1432
1519
|
],
|
|
@@ -1440,7 +1527,7 @@ const words = (list)=>new Set(list.trim().split(/\s+/));
|
|
|
1440
1527
|
shunLead: [
|
|
1441
1528
|
...shunLead
|
|
1442
1529
|
]
|
|
1443
|
-
};
|
|
1530
|
+
});
|
|
1444
1531
|
}
|
|
1445
1532
|
/** A phrase's content words without its filler; all filler, and nothing is left. */ function subjectOf(list) {
|
|
1446
1533
|
return list.filter((word)=>!FILLER.has(word));
|
|
@@ -1487,6 +1574,560 @@ const clip = (query)=>query.replace(/\s+/g, ' ').trim().slice(0, STOCK_PHOTO_QUE
|
|
|
1487
1574
|
if (aiStorefrontSays('story', sectionName) || saysMadeByHand(slot.alt) || saysMadeByHand(sectionName)) return 'story';
|
|
1488
1575
|
return undefined;
|
|
1489
1576
|
}
|
|
1577
|
+
/**
|
|
1578
|
+
* The people a picture of a moment shows (AGL-3856): what its words name, the
|
|
1579
|
+
* search that asks for them, the words a photo of them leads with, those of
|
|
1580
|
+
* which it must name one anywhere, and what it never carries unless the
|
|
1581
|
+
* words name it. A family session is never a bride.
|
|
1582
|
+
*/ const PEOPLE_SCENES = [
|
|
1583
|
+
{
|
|
1584
|
+
words: [
|
|
1585
|
+
'family',
|
|
1586
|
+
'families',
|
|
1587
|
+
'kid',
|
|
1588
|
+
'kids',
|
|
1589
|
+
'child',
|
|
1590
|
+
'children',
|
|
1591
|
+
'newborn',
|
|
1592
|
+
'baby',
|
|
1593
|
+
'parent',
|
|
1594
|
+
'parents',
|
|
1595
|
+
'mother',
|
|
1596
|
+
'father'
|
|
1597
|
+
],
|
|
1598
|
+
query: ()=>'family',
|
|
1599
|
+
subject: 'family',
|
|
1600
|
+
lead: [
|
|
1601
|
+
'family',
|
|
1602
|
+
'families',
|
|
1603
|
+
'child',
|
|
1604
|
+
'children',
|
|
1605
|
+
'kid',
|
|
1606
|
+
'kids',
|
|
1607
|
+
'mother',
|
|
1608
|
+
'father',
|
|
1609
|
+
'parents',
|
|
1610
|
+
'mom',
|
|
1611
|
+
'dad',
|
|
1612
|
+
'daughter',
|
|
1613
|
+
'son',
|
|
1614
|
+
'baby',
|
|
1615
|
+
'toddler'
|
|
1616
|
+
],
|
|
1617
|
+
people: [
|
|
1618
|
+
'family',
|
|
1619
|
+
'child',
|
|
1620
|
+
'children',
|
|
1621
|
+
'kid',
|
|
1622
|
+
'kids',
|
|
1623
|
+
'mother',
|
|
1624
|
+
'father',
|
|
1625
|
+
'parent',
|
|
1626
|
+
'parents',
|
|
1627
|
+
'daughter',
|
|
1628
|
+
'son',
|
|
1629
|
+
'baby',
|
|
1630
|
+
'toddler',
|
|
1631
|
+
'mom',
|
|
1632
|
+
'dad'
|
|
1633
|
+
],
|
|
1634
|
+
shun: [
|
|
1635
|
+
'bride',
|
|
1636
|
+
'groom',
|
|
1637
|
+
'bridal',
|
|
1638
|
+
'wedding',
|
|
1639
|
+
'newlywed',
|
|
1640
|
+
'newlyweds',
|
|
1641
|
+
'marriage'
|
|
1642
|
+
]
|
|
1643
|
+
},
|
|
1644
|
+
{
|
|
1645
|
+
words: [
|
|
1646
|
+
'wedding',
|
|
1647
|
+
'weddings',
|
|
1648
|
+
'elopement',
|
|
1649
|
+
'elopements',
|
|
1650
|
+
'elope',
|
|
1651
|
+
'eloping',
|
|
1652
|
+
'bride',
|
|
1653
|
+
'brides',
|
|
1654
|
+
'bridal',
|
|
1655
|
+
'groom',
|
|
1656
|
+
'engagement',
|
|
1657
|
+
'engaged',
|
|
1658
|
+
'couple',
|
|
1659
|
+
'couples',
|
|
1660
|
+
'newlywed',
|
|
1661
|
+
'newlyweds',
|
|
1662
|
+
'honeymoon',
|
|
1663
|
+
'ceremony',
|
|
1664
|
+
'ceremonies',
|
|
1665
|
+
'reception',
|
|
1666
|
+
'receptions',
|
|
1667
|
+
'vows'
|
|
1668
|
+
],
|
|
1669
|
+
query: (named)=>[
|
|
1670
|
+
'wedding',
|
|
1671
|
+
'weddings',
|
|
1672
|
+
'bride',
|
|
1673
|
+
'bridal',
|
|
1674
|
+
'groom'
|
|
1675
|
+
].some((word)=>named.has(word)) ? 'wedding couple' : 'couple',
|
|
1676
|
+
subject: 'couple',
|
|
1677
|
+
lead: [
|
|
1678
|
+
'couple',
|
|
1679
|
+
'couples',
|
|
1680
|
+
'bride',
|
|
1681
|
+
'groom',
|
|
1682
|
+
'wedding',
|
|
1683
|
+
'newlyweds',
|
|
1684
|
+
'lovers',
|
|
1685
|
+
'elopement',
|
|
1686
|
+
'bride and groom'
|
|
1687
|
+
],
|
|
1688
|
+
people: [
|
|
1689
|
+
'couple',
|
|
1690
|
+
'bride',
|
|
1691
|
+
'groom',
|
|
1692
|
+
'newlywed',
|
|
1693
|
+
'newlyweds',
|
|
1694
|
+
'lovers',
|
|
1695
|
+
'husband',
|
|
1696
|
+
'wife',
|
|
1697
|
+
'man',
|
|
1698
|
+
'woman'
|
|
1699
|
+
],
|
|
1700
|
+
shun: []
|
|
1701
|
+
}
|
|
1702
|
+
];
|
|
1703
|
+
/**
|
|
1704
|
+
* A portrait session (AGL-3856), read for a record's cover alone: "Milestone
|
|
1705
|
+
* Portraits" is a person's portrait, never a cat's. A page's "portrait of the
|
|
1706
|
+
* photographer" keeps its own words.
|
|
1707
|
+
*/ const PORTRAIT_SCENE = {
|
|
1708
|
+
words: [
|
|
1709
|
+
'portrait',
|
|
1710
|
+
'portraits',
|
|
1711
|
+
'headshot',
|
|
1712
|
+
'headshots'
|
|
1713
|
+
],
|
|
1714
|
+
query: ()=>'portrait',
|
|
1715
|
+
subject: 'portrait',
|
|
1716
|
+
lead: [
|
|
1717
|
+
'portrait',
|
|
1718
|
+
'woman',
|
|
1719
|
+
'man',
|
|
1720
|
+
'couple',
|
|
1721
|
+
'person',
|
|
1722
|
+
'people'
|
|
1723
|
+
],
|
|
1724
|
+
people: [
|
|
1725
|
+
'woman',
|
|
1726
|
+
'women',
|
|
1727
|
+
'man',
|
|
1728
|
+
'men',
|
|
1729
|
+
'couple',
|
|
1730
|
+
'person',
|
|
1731
|
+
'people',
|
|
1732
|
+
'bride',
|
|
1733
|
+
'groom',
|
|
1734
|
+
'mother',
|
|
1735
|
+
'father',
|
|
1736
|
+
'family'
|
|
1737
|
+
],
|
|
1738
|
+
shun: []
|
|
1739
|
+
};
|
|
1740
|
+
/**
|
|
1741
|
+
* Words a photo of a child is tagged with (AGL-3856), as stems. Pixabay tags
|
|
1742
|
+
* a schoolgirl "girl", "schoolgirl" or "school girl" far more often than
|
|
1743
|
+
* "child", so a site for adults that names a school let children in: the
|
|
1744
|
+
* beta.264 Brightpath Adult Learning Center's "Meet your teachers" showed a
|
|
1745
|
+
* teen graduate, a schoolgirl and kids.
|
|
1746
|
+
*/ const CHILD_STEMS = [
|
|
1747
|
+
...new Set(('child children childhood kid kids baby babies toddler toddlers infant infants newborn boy boys girl girls ' + 'schoolgirl schoolgirls schoolboy schoolboys schoolchild schoolchildren schoolkid schoolkids pupil pupils ' + 'teen teens teenager teenagers teenage preteen youth kindergarten preschool').split(' ').map(aiStockStem))
|
|
1748
|
+
];
|
|
1749
|
+
/** Words of which a photo of one of the business's people names one: an adult, at work or in their role. */ const ADULT_PEOPLE_STEMS = [
|
|
1750
|
+
...new Set(('man men woman women adult adults businessman businesswoman businessmen professional ' + 'professionals staff worker workers employee employees team colleague colleagues coworker coworkers ' + 'teacher teachers instructor instructors tutor tutors lecturer professor educator faculty coach coaches ' + 'trainer trainers chef chefs cook baker barista doctor doctors dentist nurse nurses hygienist therapist ' + 'physician stylist hairdresser barber artist musician photographer entrepreneur founder owner manager ' + 'leader speaker volunteer volunteers mechanic electrician plumber carpenter craftsman technician').split(' ').map(aiStockStem))
|
|
1751
|
+
];
|
|
1752
|
+
/**
|
|
1753
|
+
* The roles a list of the business's people names (AGL-3856): the words that
|
|
1754
|
+
* name each, the words a photo of someone in it is tagged with, and the word
|
|
1755
|
+
* a search for them leads with, where the libraries know one. A teacher is
|
|
1756
|
+
* shown teaching, a coach coaching, never a student or a whistle. A team or
|
|
1757
|
+
* a staff is anyone at work: its words are preferred, never required.
|
|
1758
|
+
*
|
|
1759
|
+
* A role whose photos are often of someone merely tagged with it carries the
|
|
1760
|
+
* words of it being done, one of which the photo's first tags must name
|
|
1761
|
+
* (`scene`), and the words of something else being done, which none of them
|
|
1762
|
+
* may (`refuse`): the beta.265 Northgate Adult Learning Center's "Meet your
|
|
1763
|
+
* teachers" placed a woman painting at an easel ("artist, teacher of
|
|
1764
|
+
* drawing"), a model in a traditional dress ("woman, model, show, teacher")
|
|
1765
|
+
* and an elderly man's portrait ("teacher, professor, man, senior").
|
|
1766
|
+
*/ const PEOPLE_ROLES = [
|
|
1767
|
+
{
|
|
1768
|
+
words: [
|
|
1769
|
+
'instructor',
|
|
1770
|
+
'teacher',
|
|
1771
|
+
'tutor',
|
|
1772
|
+
'educator',
|
|
1773
|
+
'faculty',
|
|
1774
|
+
'professor',
|
|
1775
|
+
'lecturer',
|
|
1776
|
+
'mentor'
|
|
1777
|
+
],
|
|
1778
|
+
kin: [
|
|
1779
|
+
'teacher',
|
|
1780
|
+
'instructor',
|
|
1781
|
+
'tutor',
|
|
1782
|
+
'educator',
|
|
1783
|
+
'professor',
|
|
1784
|
+
'lecturer',
|
|
1785
|
+
'teaching',
|
|
1786
|
+
'lecture',
|
|
1787
|
+
'faculty'
|
|
1788
|
+
],
|
|
1789
|
+
query: 'teacher',
|
|
1790
|
+
scene: [
|
|
1791
|
+
'teaching',
|
|
1792
|
+
'teach',
|
|
1793
|
+
'classroom',
|
|
1794
|
+
'class',
|
|
1795
|
+
'lecture',
|
|
1796
|
+
'lesson',
|
|
1797
|
+
'lessons',
|
|
1798
|
+
'tutor',
|
|
1799
|
+
'tutoring',
|
|
1800
|
+
'blackboard',
|
|
1801
|
+
'chalkboard',
|
|
1802
|
+
'whiteboard',
|
|
1803
|
+
'board',
|
|
1804
|
+
'seminar',
|
|
1805
|
+
'student',
|
|
1806
|
+
'students',
|
|
1807
|
+
'learner',
|
|
1808
|
+
'learners',
|
|
1809
|
+
'presentation'
|
|
1810
|
+
],
|
|
1811
|
+
refuse: [
|
|
1812
|
+
'model',
|
|
1813
|
+
'modeling',
|
|
1814
|
+
'fashion',
|
|
1815
|
+
'pose',
|
|
1816
|
+
'posing',
|
|
1817
|
+
'beauty',
|
|
1818
|
+
'glamour',
|
|
1819
|
+
'cosplay',
|
|
1820
|
+
'artist',
|
|
1821
|
+
'artists',
|
|
1822
|
+
'painter',
|
|
1823
|
+
'painting',
|
|
1824
|
+
'paint',
|
|
1825
|
+
'easel',
|
|
1826
|
+
'canvas',
|
|
1827
|
+
'draw',
|
|
1828
|
+
'draws',
|
|
1829
|
+
'drawing',
|
|
1830
|
+
'calligraphy'
|
|
1831
|
+
]
|
|
1832
|
+
},
|
|
1833
|
+
{
|
|
1834
|
+
words: [
|
|
1835
|
+
'coach',
|
|
1836
|
+
'trainer'
|
|
1837
|
+
],
|
|
1838
|
+
kin: [
|
|
1839
|
+
'coach',
|
|
1840
|
+
'coaching',
|
|
1841
|
+
'trainer',
|
|
1842
|
+
'instructor'
|
|
1843
|
+
],
|
|
1844
|
+
query: 'coach'
|
|
1845
|
+
},
|
|
1846
|
+
{
|
|
1847
|
+
words: [
|
|
1848
|
+
'chef',
|
|
1849
|
+
'cook',
|
|
1850
|
+
'baker'
|
|
1851
|
+
],
|
|
1852
|
+
kin: [
|
|
1853
|
+
'chef',
|
|
1854
|
+
'cook',
|
|
1855
|
+
'cooking',
|
|
1856
|
+
'baker',
|
|
1857
|
+
'baking'
|
|
1858
|
+
],
|
|
1859
|
+
query: 'chef'
|
|
1860
|
+
},
|
|
1861
|
+
{
|
|
1862
|
+
words: [
|
|
1863
|
+
'stylist',
|
|
1864
|
+
'barber',
|
|
1865
|
+
'hairdresser',
|
|
1866
|
+
'colorist'
|
|
1867
|
+
],
|
|
1868
|
+
kin: [
|
|
1869
|
+
'stylist',
|
|
1870
|
+
'barber',
|
|
1871
|
+
'hairdresser',
|
|
1872
|
+
'hairstylist',
|
|
1873
|
+
'haircut'
|
|
1874
|
+
],
|
|
1875
|
+
query: 'hairdresser'
|
|
1876
|
+
},
|
|
1877
|
+
{
|
|
1878
|
+
words: [
|
|
1879
|
+
'doctor',
|
|
1880
|
+
'dentist',
|
|
1881
|
+
'hygienist',
|
|
1882
|
+
'nurse',
|
|
1883
|
+
'therapist',
|
|
1884
|
+
'practitioner',
|
|
1885
|
+
'physician'
|
|
1886
|
+
],
|
|
1887
|
+
kin: [
|
|
1888
|
+
'doctor',
|
|
1889
|
+
'dentist',
|
|
1890
|
+
'hygienist',
|
|
1891
|
+
'nurse',
|
|
1892
|
+
'therapist',
|
|
1893
|
+
'physician',
|
|
1894
|
+
'medical'
|
|
1895
|
+
]
|
|
1896
|
+
},
|
|
1897
|
+
{
|
|
1898
|
+
words: [
|
|
1899
|
+
'staff',
|
|
1900
|
+
'team',
|
|
1901
|
+
'crew',
|
|
1902
|
+
'member',
|
|
1903
|
+
'manager',
|
|
1904
|
+
'founder',
|
|
1905
|
+
'owner',
|
|
1906
|
+
'partner',
|
|
1907
|
+
'associate',
|
|
1908
|
+
'employee'
|
|
1909
|
+
],
|
|
1910
|
+
kin: [
|
|
1911
|
+
'staff',
|
|
1912
|
+
'team',
|
|
1913
|
+
'worker',
|
|
1914
|
+
'employee',
|
|
1915
|
+
'colleague',
|
|
1916
|
+
'coworker',
|
|
1917
|
+
'professional',
|
|
1918
|
+
'businessman',
|
|
1919
|
+
'businesswoman',
|
|
1920
|
+
'owner',
|
|
1921
|
+
'manager'
|
|
1922
|
+
],
|
|
1923
|
+
loose: true
|
|
1924
|
+
}
|
|
1925
|
+
];
|
|
1926
|
+
/** How many of a photo's first tags show a role being done: "teacher, presentation, education, man, students" (AGL-3856). */ export const AI_STOCK_ROLE_TAGS_READ = 6;
|
|
1927
|
+
/**
|
|
1928
|
+
* The role a person's words say, else the first of the other texts that names
|
|
1929
|
+
* one (their list's name): "Online learning coach" of the "Instructors" list
|
|
1930
|
+
* is a coach. `null` where none names a listed role.
|
|
1931
|
+
*/ export function aiStockPeopleRole(...texts) {
|
|
1932
|
+
for (const text of texts){
|
|
1933
|
+
var _role_query;
|
|
1934
|
+
const named = new Set(stemList(text));
|
|
1935
|
+
const role = PEOPLE_ROLES.find((entry)=>entry.words.some((word)=>named.has(aiStockStem(word))));
|
|
1936
|
+
if (role) return _extends({
|
|
1937
|
+
kin: [
|
|
1938
|
+
...new Set(role.kin.map(aiStockStem))
|
|
1939
|
+
],
|
|
1940
|
+
query: (_role_query = role.query) != null ? _role_query : '',
|
|
1941
|
+
required: !role.loose
|
|
1942
|
+
}, role.scene ? {
|
|
1943
|
+
scene: [
|
|
1944
|
+
...new Set(role.scene.map(aiStockStem))
|
|
1945
|
+
]
|
|
1946
|
+
} : {}, role.refuse ? {
|
|
1947
|
+
refuse: [
|
|
1948
|
+
...new Set(role.refuse.map(aiStockStem))
|
|
1949
|
+
]
|
|
1950
|
+
} : {});
|
|
1951
|
+
}
|
|
1952
|
+
return null;
|
|
1953
|
+
}
|
|
1954
|
+
/** A role's refusals, less what the person's own words name: a "Drawing instructor" may be drawing. */ export function aiStockRoleRefusals(role, words) {
|
|
1955
|
+
var _role_refuse;
|
|
1956
|
+
const named = new Set(stemList(words));
|
|
1957
|
+
return ((_role_refuse = role.refuse) != null ? _role_refuse : []).filter((stem)=>!named.has(stem));
|
|
1958
|
+
}
|
|
1959
|
+
/** A site's audience in its own words, when it is adults: "Adults finishing a high school diploma". */ const ADULT_AUDIENCE = /\b(?:adults?|grown[- ]?ups?|seniors?|retirees?|older (?:adults|people)|professionals|employees|workforce|job ?seekers)\b/i;
|
|
1960
|
+
/** Words that say a site is for children too, whatever else it says. */ const YOUNG_AUDIENCE = /\b(?:kids?|child(?:ren)?|teens?|teenagers?|youth|young people|famil(?:y|ies)|boys?|girls?|bab(?:y|ies)|toddlers?|k-12|elementary|middle school|preschool|kindergarten|pupils?)\b/i;
|
|
1961
|
+
/**
|
|
1962
|
+
* Whether a site is for adults alone (AGL-3856), as its audience, its name
|
|
1963
|
+
* or its business type says: "Adults finishing a high school diploma",
|
|
1964
|
+
* "Brightpath Adult Learning Center". Then no photo of a child is placed
|
|
1965
|
+
* anywhere on it, whatever else its words name ("school").
|
|
1966
|
+
*/ export function aiStockAdultsOnly(texts) {
|
|
1967
|
+
const said = texts.filter(Boolean).join(' . ');
|
|
1968
|
+
return ADULT_AUDIENCE.test(said) && !YOUNG_AUDIENCE.test(said);
|
|
1969
|
+
}
|
|
1970
|
+
/** Where a picture of a moment is set, by the words that name it, and the word a search and a photo say it with. */ const SCENE_SETTINGS = [
|
|
1971
|
+
{
|
|
1972
|
+
words: [
|
|
1973
|
+
'mountain',
|
|
1974
|
+
'mountains',
|
|
1975
|
+
'ridgetop',
|
|
1976
|
+
'ridge',
|
|
1977
|
+
'summit',
|
|
1978
|
+
'peak',
|
|
1979
|
+
'alpine',
|
|
1980
|
+
'overlook',
|
|
1981
|
+
'parkway'
|
|
1982
|
+
],
|
|
1983
|
+
said: 'mountains'
|
|
1984
|
+
},
|
|
1985
|
+
{
|
|
1986
|
+
words: [
|
|
1987
|
+
'fall',
|
|
1988
|
+
'autumn',
|
|
1989
|
+
'foliage'
|
|
1990
|
+
],
|
|
1991
|
+
said: 'autumn'
|
|
1992
|
+
},
|
|
1993
|
+
{
|
|
1994
|
+
words: [
|
|
1995
|
+
'winter',
|
|
1996
|
+
'snow',
|
|
1997
|
+
'snowy'
|
|
1998
|
+
],
|
|
1999
|
+
said: 'winter'
|
|
2000
|
+
},
|
|
2001
|
+
{
|
|
2002
|
+
words: [
|
|
2003
|
+
'beach',
|
|
2004
|
+
'coast',
|
|
2005
|
+
'seaside',
|
|
2006
|
+
'shore',
|
|
2007
|
+
'ocean'
|
|
2008
|
+
],
|
|
2009
|
+
said: 'beach'
|
|
2010
|
+
},
|
|
2011
|
+
{
|
|
2012
|
+
words: [
|
|
2013
|
+
'forest',
|
|
2014
|
+
'woods',
|
|
2015
|
+
'woodland'
|
|
2016
|
+
],
|
|
2017
|
+
said: 'forest'
|
|
2018
|
+
},
|
|
2019
|
+
{
|
|
2020
|
+
words: [
|
|
2021
|
+
'spring',
|
|
2022
|
+
'blossom',
|
|
2023
|
+
'blossoms'
|
|
2024
|
+
],
|
|
2025
|
+
said: 'spring'
|
|
2026
|
+
},
|
|
2027
|
+
{
|
|
2028
|
+
words: [
|
|
2029
|
+
'city',
|
|
2030
|
+
'urban',
|
|
2031
|
+
'downtown'
|
|
2032
|
+
],
|
|
2033
|
+
said: 'city'
|
|
2034
|
+
}
|
|
2035
|
+
];
|
|
2036
|
+
/**
|
|
2037
|
+
* The scene a picture's words name (AGL-3856): the people at a moment, and
|
|
2038
|
+
* where — "Ridgetop Elopement" a couple in the mountains, "Mountain Wedding
|
|
2039
|
+
* Day" a wedding couple in the mountains, "Family Session in the Fall" a
|
|
2040
|
+
* family in autumn — read from every word, never only the first clause.
|
|
2041
|
+
* `null` where it names no people.
|
|
2042
|
+
*/ export function aiStockScene(text) {
|
|
2043
|
+
const named = sceneWords(text);
|
|
2044
|
+
const scene = PEOPLE_SCENES.find((entry)=>entry.words.some((word)=>named.has(word)));
|
|
2045
|
+
if (!scene) return null;
|
|
2046
|
+
const setting = aiStockSetting(text);
|
|
2047
|
+
const query = scene.query(named);
|
|
2048
|
+
return {
|
|
2049
|
+
queries: [
|
|
2050
|
+
...new Set([
|
|
2051
|
+
setting ? `${query} ${setting}` : '',
|
|
2052
|
+
query
|
|
2053
|
+
].filter(Boolean))
|
|
2054
|
+
],
|
|
2055
|
+
subject: [
|
|
2056
|
+
scene.subject,
|
|
2057
|
+
setting
|
|
2058
|
+
].filter(Boolean).join(' '),
|
|
2059
|
+
object: scene.subject,
|
|
2060
|
+
lead: scene.lead.map((phrase)=>stemList(phrase).join(' ')),
|
|
2061
|
+
people: scene.people.map(aiStockStem),
|
|
2062
|
+
shun: scene.shun.filter((word)=>!named.has(word)).map(aiStockStem),
|
|
2063
|
+
named: [
|
|
2064
|
+
...new Set(stemList(text))
|
|
2065
|
+
]
|
|
2066
|
+
};
|
|
2067
|
+
}
|
|
2068
|
+
/** A text's lowercase words, letters only. */ const sceneWords = (text)=>new Set(text.toLowerCase().split(/[^a-zÀ-ɏ]+/).filter(Boolean));
|
|
2069
|
+
/** Where a text's words set a picture ({@link SCENE_SETTINGS}): "mountains" of "Couples planning mountain weddings"; empty for none. */ export function aiStockSetting(text) {
|
|
2070
|
+
var _ref;
|
|
2071
|
+
var _SCENE_SETTINGS_find;
|
|
2072
|
+
const named = sceneWords(text);
|
|
2073
|
+
return (_ref = (_SCENE_SETTINGS_find = SCENE_SETTINGS.find((entry)=>entry.words.some((word)=>named.has(word)))) == null ? void 0 : _SCENE_SETTINGS_find.said) != null ? _ref : '';
|
|
2074
|
+
}
|
|
2075
|
+
/**
|
|
2076
|
+
* The moment a portfolio's record shows (AGL-3856): read from the record's
|
|
2077
|
+
* own name first, then from what it is filed under, then as a portrait, and
|
|
2078
|
+
* set where its name, its category or else the site's own words say. A
|
|
2079
|
+
* photographer's "Getting Ready" filed under "Weddings and elopements" is a
|
|
2080
|
+
* wedding couple in the mountains; "Milestone Portraits" filed under "Family
|
|
2081
|
+
* portraits" a family. Its searches end on the people alone ("couple"), so a
|
|
2082
|
+
* moment no photo shows in its setting still shows its people. `null` where
|
|
2083
|
+
* neither names people at a moment nor a portrait.
|
|
2084
|
+
*/ export function aiStockRecordScene(name, category = '', siteSetting = '') {
|
|
2085
|
+
var _texts_find, _PEOPLE_SCENES_find, _texts_map_find;
|
|
2086
|
+
const texts = [
|
|
2087
|
+
name,
|
|
2088
|
+
category
|
|
2089
|
+
].filter((text)=>text.trim());
|
|
2090
|
+
const source = (_texts_find = texts.find((text)=>PEOPLE_SCENES.some((entry)=>entry.words.some((word)=>sceneWords(text).has(word))))) != null ? _texts_find : texts.find((text)=>PORTRAIT_SCENE.words.some((word)=>sceneWords(text).has(word)));
|
|
2091
|
+
if (!source) return null;
|
|
2092
|
+
const named = sceneWords(source);
|
|
2093
|
+
const scene = (_PEOPLE_SCENES_find = PEOPLE_SCENES.find((entry)=>entry.words.some((word)=>named.has(word)))) != null ? _PEOPLE_SCENES_find : PORTRAIT_SCENE;
|
|
2094
|
+
const setting = (_texts_map_find = texts.map(aiStockSetting).find(Boolean)) != null ? _texts_map_find : siteSetting;
|
|
2095
|
+
const query = scene.query(named);
|
|
2096
|
+
const all = sceneWords(texts.join(' '));
|
|
2097
|
+
return {
|
|
2098
|
+
queries: [
|
|
2099
|
+
...new Set([
|
|
2100
|
+
setting ? `${query} ${setting}` : '',
|
|
2101
|
+
query,
|
|
2102
|
+
scene.subject
|
|
2103
|
+
].filter(Boolean))
|
|
2104
|
+
],
|
|
2105
|
+
subject: [
|
|
2106
|
+
scene.subject,
|
|
2107
|
+
setting
|
|
2108
|
+
].filter(Boolean).join(' '),
|
|
2109
|
+
object: scene.subject,
|
|
2110
|
+
// A record's cover leads with its people: a wedding's table decor is not "Overlook Ceremonies".
|
|
2111
|
+
lead: scene.lead.filter((phrase)=>!RECORD_SCENE_UNPEOPLED.has(phrase)).map((phrase)=>stemList(phrase).join(' ')),
|
|
2112
|
+
people: scene.people.map(aiStockStem),
|
|
2113
|
+
shun: scene.shun.filter((word)=>!all.has(word)).map(aiStockStem),
|
|
2114
|
+
named: [
|
|
2115
|
+
...new Set(stemList(texts.join(' ')))
|
|
2116
|
+
]
|
|
2117
|
+
};
|
|
2118
|
+
}
|
|
2119
|
+
/**
|
|
2120
|
+
* A scene's lead words a record's cover never leads with alone
|
|
2121
|
+
* ({@link aiStockRecordScene}): the moment with no one in it (a wedding's table
|
|
2122
|
+
* decor for "Overlook Ceremonies"), or children with no family (a ski class
|
|
2123
|
+
* for "Milestone Portraits").
|
|
2124
|
+
*/ const RECORD_SCENE_UNPEOPLED = words('wedding elopement child children kid kids baby toddler');
|
|
2125
|
+
/**
|
|
2126
|
+
* What a portfolio's record is never OF, among its first tags, unless its own
|
|
2127
|
+
* words name it (AGL-3856): an animal, a toy or a figurine. The beta.265
|
|
2128
|
+
* Laurel Ridge "Gallery collections" covers were a toy frog with a camera, a
|
|
2129
|
+
* toy duck and a cat's portrait.
|
|
2130
|
+
*/ const RECORD_ODD_LEADS = words('animal animals cat cats kitten dog dogs puppy pet pets bird birds frog frogs duck ducks horse horses cow rabbit bunny ' + 'toy toys figurine figurines lego doll dolls teddy puppet miniature');
|
|
1490
2131
|
/** An object's kin phrases as stems, or none. */ const kinStems = (entry)=>entry ? [
|
|
1491
2132
|
...new Set(entry.kin.map((phrase)=>stemList(phrase).join(' ')))
|
|
1492
2133
|
] : [];
|
|
@@ -1708,8 +2349,14 @@ const clip = (query)=>query.replace(/\s+/g, ' ').trim().slice(0, STOCK_PHOTO_QUE
|
|
|
1708
2349
|
}
|
|
1709
2350
|
return searches;
|
|
1710
2351
|
}
|
|
1711
|
-
/**
|
|
1712
|
-
|
|
2352
|
+
/**
|
|
2353
|
+
* The searches a slot tries, in order, most specific first, each distinct.
|
|
2354
|
+
* A portrait of one of the business's people ({@link AiLayoutPictureSlot.people})
|
|
2355
|
+
* asks first for the role its words or its list's name (`sectionName`) say,
|
|
2356
|
+
* in the site's world: "teacher classroom" for a "Math instructor" (AGL-3856).
|
|
2357
|
+
*/ export function aiStockSearchesFor(slot, terms, subject, part, sectionName = '') {
|
|
2358
|
+
var _terms_core, _terms_making, _ref, _ref1, _terms_setting, _ref2, _terms_maker, _ref3;
|
|
2359
|
+
var _aiStockPeopleRole, _slot_record, _slot_record1;
|
|
1713
2360
|
const orientation = aiStockOrientation(slot.aspect);
|
|
1714
2361
|
const size = slot.role === 'hero' ? {
|
|
1715
2362
|
minWidth: 1600
|
|
@@ -1719,6 +2366,11 @@ const clip = (query)=>query.replace(/\s+/g, ' ').trim().slice(0, STOCK_PHOTO_QUE
|
|
|
1719
2366
|
minWidth: 900
|
|
1720
2367
|
};
|
|
1721
2368
|
const domain = new Set(terms.domain);
|
|
2369
|
+
// A portfolio's record of a craft whose work is people at a moment shows the
|
|
2370
|
+
// moment, never the maker: the beta.265 Laurel Ridge "Gallery collections"
|
|
2371
|
+
// searched "photographer mountain elopements" and placed a press
|
|
2372
|
+
// photographer, a toy frog with a camera and a cat (AGL-3856).
|
|
2373
|
+
const work = aiStockShowsTheWork(slot, terms);
|
|
1722
2374
|
const object = aiStockObjectWord(subject);
|
|
1723
2375
|
const subjectStems = stemList(subject);
|
|
1724
2376
|
const kin = object ? objectKin(object, [
|
|
@@ -1738,21 +2390,28 @@ const clip = (query)=>query.replace(/\s+/g, ' ').trim().slice(0, STOCK_PHOTO_QUE
|
|
|
1738
2390
|
// A collection tile is product-like: held to the shop's category and its
|
|
1739
2391
|
// own object among the first tags, and searched by its object's kin, never
|
|
1740
2392
|
// the category's world alone (AGL-3660).
|
|
1741
|
-
const collection = part === 'collection' && Boolean(terms.craft);
|
|
1742
|
-
const broad = kin && collection ? [] : terms.broad.map((query)=>({
|
|
2393
|
+
const collection = part === 'collection' && Boolean(terms.craft) && !work;
|
|
2394
|
+
const broad = kin && collection || work ? [] : terms.broad.map((query)=>({
|
|
1743
2395
|
query,
|
|
1744
2396
|
broad: true,
|
|
1745
2397
|
category: collection
|
|
1746
2398
|
}));
|
|
1747
2399
|
// The maker at work, held to the craft: never "hands" (AGL-3660).
|
|
1748
|
-
const making = ((_terms_making = terms.making) != null ? _terms_making : []).map((query)=>({
|
|
2400
|
+
const making = (work ? [] : (_terms_making = terms.making) != null ? _terms_making : []).map((query)=>({
|
|
1749
2401
|
query,
|
|
1750
2402
|
broad: true,
|
|
1751
2403
|
category: true
|
|
1752
2404
|
}));
|
|
2405
|
+
// A portrait in its role, in the site's world, where the libraries know a word for it.
|
|
2406
|
+
const roleWord = slot.people ? (_ref = (_aiStockPeopleRole = aiStockPeopleRole(slot.alt, sectionName)) == null ? void 0 : _aiStockPeopleRole.query) != null ? _ref : '' : '';
|
|
2407
|
+
const role = roleWord ? stemList(terms.business).includes(roleWord) ? terms.business : `${roleWord} ${terms.business}`.trim() : '';
|
|
2408
|
+
// A scene's people are read from every word of the picture's, never its first clause alone.
|
|
2409
|
+
const scene = work ? aiStockRecordScene(slot.alt, (_ref1 = (_slot_record = slot.record) == null ? void 0 : _slot_record.category) != null ? _ref1 : '', (_terms_setting = terms.setting) != null ? _terms_setting : '') : terms.scenes && slot.role !== 'hero' && part !== 'collection' ? aiStockScene(slot.alt) : null;
|
|
2410
|
+
// The work's own subject is never qualified by the craft: "getting ready", never "photographer getting ready".
|
|
2411
|
+
const own = work ? (phrase)=>phrase : qualify;
|
|
1753
2412
|
const thing = subject ? [
|
|
1754
2413
|
{
|
|
1755
|
-
query:
|
|
2414
|
+
query: own(subject),
|
|
1756
2415
|
held: true
|
|
1757
2416
|
},
|
|
1758
2417
|
...kin ? kin.queries.map((query)=>({
|
|
@@ -1760,11 +2419,21 @@ const clip = (query)=>query.replace(/\s+/g, ' ').trim().slice(0, STOCK_PHOTO_QUE
|
|
|
1760
2419
|
held: true
|
|
1761
2420
|
})) : [
|
|
1762
2421
|
{
|
|
1763
|
-
query:
|
|
2422
|
+
query: own(object),
|
|
1764
2423
|
held: true
|
|
1765
2424
|
}
|
|
1766
2425
|
]
|
|
1767
2426
|
] : [];
|
|
2427
|
+
// Never the maker, the camera, an animal or a toy, unless the record names it.
|
|
2428
|
+
const recordNamed = work ? new Set(stemList(`${slot.alt} ${(_ref2 = (_slot_record1 = slot.record) == null ? void 0 : _slot_record1.category) != null ? _ref2 : ''}`)) : null;
|
|
2429
|
+
const refuseLead = recordNamed ? [
|
|
2430
|
+
...new Set([
|
|
2431
|
+
...(_terms_maker = terms.maker) != null ? _terms_maker : [],
|
|
2432
|
+
...[
|
|
2433
|
+
...RECORD_ODD_LEADS
|
|
2434
|
+
].map(aiStockStem)
|
|
2435
|
+
])
|
|
2436
|
+
].filter((stem)=>!recordNamed.has(stem)) : [];
|
|
1768
2437
|
const queries = slot.role === 'hero' ? [
|
|
1769
2438
|
{
|
|
1770
2439
|
query: terms.business,
|
|
@@ -1772,6 +2441,13 @@ const clip = (query)=>query.replace(/\s+/g, ' ').trim().slice(0, STOCK_PHOTO_QUE
|
|
|
1772
2441
|
},
|
|
1773
2442
|
...broad
|
|
1774
2443
|
] : slot.role === 'about' ? [
|
|
2444
|
+
...role ? [
|
|
2445
|
+
{
|
|
2446
|
+
query: role,
|
|
2447
|
+
broad: true,
|
|
2448
|
+
people: true
|
|
2449
|
+
}
|
|
2450
|
+
] : [],
|
|
1775
2451
|
...part === 'story' ? making : [],
|
|
1776
2452
|
{
|
|
1777
2453
|
query: `${terms.business} ${subject}`,
|
|
@@ -1787,11 +2463,35 @@ const clip = (query)=>query.replace(/\s+/g, ' ').trim().slice(0, STOCK_PHOTO_QUE
|
|
|
1787
2463
|
...broad
|
|
1788
2464
|
] : [
|
|
1789
2465
|
...part === 'story' ? making : [],
|
|
1790
|
-
...thing,
|
|
2466
|
+
...scene ? [] : thing,
|
|
1791
2467
|
...broad
|
|
1792
2468
|
];
|
|
1793
2469
|
const seen = new Set();
|
|
1794
2470
|
const searches = [];
|
|
2471
|
+
// A picture of people at a moment asks for them, and where, before anything
|
|
2472
|
+
// else, and takes only a photo leading with them (AGL-3856): never the
|
|
2473
|
+
// roses of a "Mountain Wedding Day", nor a bride for a family session.
|
|
2474
|
+
for (const query of (_ref3 = scene == null ? void 0 : scene.queries) != null ? _ref3 : []){
|
|
2475
|
+
seen.add(`${query}|false`);
|
|
2476
|
+
searches.push(_extends({
|
|
2477
|
+
query,
|
|
2478
|
+
orientation
|
|
2479
|
+
}, size, {
|
|
2480
|
+
strict: true,
|
|
2481
|
+
subject: scene.subject,
|
|
2482
|
+
object: scene.object,
|
|
2483
|
+
kin: scene.lead,
|
|
2484
|
+
lead: true,
|
|
2485
|
+
needs: scene.people,
|
|
2486
|
+
allow: scene.named
|
|
2487
|
+
}, scene.shun.length ? {
|
|
2488
|
+
refuse: scene.shun
|
|
2489
|
+
} : {}, refuseLead.length ? {
|
|
2490
|
+
refuseLead
|
|
2491
|
+
} : {}, work ? {
|
|
2492
|
+
worldless: true
|
|
2493
|
+
} : {}));
|
|
2494
|
+
}
|
|
1795
2495
|
for (const raw of queries){
|
|
1796
2496
|
const query = clip(raw.query);
|
|
1797
2497
|
const key = `${query}|${raw.people === true}`;
|
|
@@ -1820,10 +2520,23 @@ const clip = (query)=>query.replace(/\s+/g, ' ').trim().slice(0, STOCK_PHOTO_QUE
|
|
|
1820
2520
|
category: core
|
|
1821
2521
|
} : {}, slot.role === 'hero' ? {
|
|
1822
2522
|
first: true
|
|
2523
|
+
} : {}, refuseLead.length ? {
|
|
2524
|
+
refuseLead
|
|
2525
|
+
} : {}, work ? {
|
|
2526
|
+
worldless: true
|
|
1823
2527
|
} : {}));
|
|
1824
2528
|
}
|
|
1825
2529
|
return searches;
|
|
1826
2530
|
}
|
|
2531
|
+
/**
|
|
2532
|
+
* Whether a slot is a portfolio's record of a craft whose work is people at a
|
|
2533
|
+
* moment (AGL-3856): a photographer's "Mountain Elopements" shows an
|
|
2534
|
+
* elopement, never the photographer. Searched by the moment its words and its
|
|
2535
|
+
* category name, held to no world of the craft, and never led by the craft's
|
|
2536
|
+
* own words, an animal or a toy unless the record names them.
|
|
2537
|
+
*/ export function aiStockShowsTheWork(slot, terms) {
|
|
2538
|
+
return Boolean(slot.record) && Boolean(terms.scenes) && slot.role !== 'hero' && !slot.people && !slot.product;
|
|
2539
|
+
}
|
|
1827
2540
|
/** The words of a photo's page address at its library, its id dropped: "robin-bird-songbird-garden-winter". */ function pageSlug(pageUrl) {
|
|
1828
2541
|
try {
|
|
1829
2542
|
var _ref;
|
|
@@ -1896,7 +2609,8 @@ const clip = (query)=>query.replace(/\s+/g, ' ').trim().slice(0, STOCK_PHOTO_QUE
|
|
|
1896
2609
|
* the subject's two-word phrase 3, each of its other words 1, and the
|
|
1897
2610
|
* site's world 1, plus a quarter when it is among the first tags.
|
|
1898
2611
|
*/ export function aiStockRelevance(photo, judgement) {
|
|
1899
|
-
var _judgement_rivals, _judgement_subject, _judgement_named, _judgement_shun, _judgement_shunLead, _judgement_leadAny, _judgement_kin, _judgement_requires, _judgement_category, _judgement_object, _judgement_domain;
|
|
2612
|
+
var _judgement_rivals, _judgement_subject, _judgement_named, _judgement_shun, _judgement_refuse, _judgement_refuseLead, _judgement_needs, _judgement_shunLead, _judgement_leadAny, _judgement_kin, _judgement_requires, _judgement_category, _judgement_object, _judgement_domain, _judgement_roles;
|
|
2613
|
+
var _judgement_roleScene, _judgement_roleRefuse;
|
|
1900
2614
|
const hit = hitWords(photo);
|
|
1901
2615
|
if (hit.rejected) return 0;
|
|
1902
2616
|
if (aiStockSensitive(photo, judgement.named)) return 0;
|
|
@@ -1910,6 +2624,23 @@ const clip = (query)=>query.replace(/\s+/g, ' ').trim().slice(0, STOCK_PHOTO_QUE
|
|
|
1910
2624
|
]);
|
|
1911
2625
|
// A dispenser is not a soapmaker's, nor a pump a bar's (AGL-3660).
|
|
1912
2626
|
if (((_judgement_shun = judgement.shun) != null ? _judgement_shun : []).some((word)=>hit.all.has(word) && !owned.has(word))) return 0;
|
|
2627
|
+
// A family session is never a bride, however the photographer's brief names weddings.
|
|
2628
|
+
if (((_judgement_refuse = judgement.refuse) != null ? _judgement_refuse : []).some((word)=>hit.all.has(word))) return 0;
|
|
2629
|
+
// A teacher's portrait is never OF a child, whatever else it names.
|
|
2630
|
+
if (((_judgement_refuseLead = judgement.refuseLead) != null ? _judgement_refuseLead : []).some((word)=>hit.lead.has(word))) return 0;
|
|
2631
|
+
// A teacher is shown teaching: a classroom, a lecture or students among the
|
|
2632
|
+
// first tags, and never a model, a pose or a painting (AGL-3856).
|
|
2633
|
+
if (((_judgement_roleScene = judgement.roleScene) == null ? void 0 : _judgement_roleScene.length) || ((_judgement_roleRefuse = judgement.roleRefuse) == null ? void 0 : _judgement_roleRefuse.length)) {
|
|
2634
|
+
var _photo_tags, _photo_tags1, _judgement_roleRefuse1;
|
|
2635
|
+
var _judgement_roleScene1;
|
|
2636
|
+
const first = new Set(stemList(((_photo_tags = photo.tags) != null ? _photo_tags : []).slice(0, AI_STOCK_ROLE_TAGS_READ).join(' ')));
|
|
2637
|
+
if (((_judgement_roleScene1 = judgement.roleScene) == null ? void 0 : _judgement_roleScene1.length) && !judgement.roleScene.some((stem)=>first.has(stem))) return 0;
|
|
2638
|
+
const read = new Set(stemList(((_photo_tags1 = photo.tags) != null ? _photo_tags1 : []).slice(0, ALT_TAGS_READ).join(' ')));
|
|
2639
|
+
if (((_judgement_roleRefuse1 = judgement.roleRefuse) != null ? _judgement_roleRefuse1 : []).some((stem)=>read.has(stem))) return 0;
|
|
2640
|
+
}
|
|
2641
|
+
// A wedding day shows its people: a close-up of roses tagged "wedding" never fills it.
|
|
2642
|
+
const needs = (_judgement_needs = judgement.needs) != null ? _judgement_needs : [];
|
|
2643
|
+
if (needs.length && !needs.some((word)=>hit.all.has(word))) return 0;
|
|
1913
2644
|
// What the photo is OF: a typewriter's terms of service are not a law firm's.
|
|
1914
2645
|
if (((_judgement_shunLead = judgement.shunLead) != null ? _judgement_shunLead : []).some((word)=>hit.lead.has(word) && !owned.has(word))) return 0;
|
|
1915
2646
|
if (judgement.product) {
|
|
@@ -1988,6 +2719,10 @@ const clip = (query)=>query.replace(/\s+/g, ' ').trim().slice(0, STOCK_PHOTO_QUE
|
|
|
1988
2719
|
if (phrased) score += 3;
|
|
1989
2720
|
for (const word of new Set(subject))if (word !== object && hit.all.has(word)) score += 1;
|
|
1990
2721
|
if (world) score += worldLead ? 1.25 : 1;
|
|
2722
|
+
// A portrait in its role: a teacher at the board over a woman in a classroom.
|
|
2723
|
+
const roles = (_judgement_roles = judgement.roles) != null ? _judgement_roles : [];
|
|
2724
|
+
if (roles.some((word)=>hit.lead.has(word))) score += 4;
|
|
2725
|
+
else if (roles.some((word)=>hit.all.has(word))) score += 2;
|
|
1991
2726
|
// A hero is most of all what its first tags say: "law office, lawyer,
|
|
1992
2727
|
// attorney" over "lawyers, personal injury, accident" (AGL-3856): a second
|
|
1993
2728
|
// of them naming its world says it is of that world, not of something else.
|
|
@@ -2022,6 +2757,36 @@ const clip = (query)=>query.replace(/\s+/g, ' ').trim().slice(0, STOCK_PHOTO_QUE
|
|
|
2022
2757
|
}
|
|
2023
2758
|
return ranked;
|
|
2024
2759
|
}
|
|
2760
|
+
/**
|
|
2761
|
+
* Whether two hits are of one shoot (AGL-3856): one contributor's photos a few
|
|
2762
|
+
* uploads apart, as Pixabay numbers them — the beta.265 Granite Peak Barbell
|
|
2763
|
+
* coaches were four of one model, 728223 to 728229 — else one page address.
|
|
2764
|
+
*/ export function aiStockSameShoot(a, b) {
|
|
2765
|
+
const near = Number(a.id) && Number(b.id) ? Math.abs(Number(a.id) - Number(b.id)) < 100 : false;
|
|
2766
|
+
if (a.photographer && a.photographer === b.photographer && near) return true;
|
|
2767
|
+
const slug = pageSlug(a.pageUrl);
|
|
2768
|
+
return Boolean(slug) && slug === pageSlug(b.pageUrl);
|
|
2769
|
+
}
|
|
2770
|
+
/**
|
|
2771
|
+
* A search's ranked hits with those of a shoot no slot shows yet first among
|
|
2772
|
+
* the best (AGL-3856): a team's three cards are three people where the
|
|
2773
|
+
* library has them, never one model three times while another fits as well.
|
|
2774
|
+
* Every other order is kept.
|
|
2775
|
+
*/ export function aiStockFreshShootsFirst(ranked, shown, judgement) {
|
|
2776
|
+
if (!shown.length || ranked.length < 2) return [
|
|
2777
|
+
...ranked
|
|
2778
|
+
];
|
|
2779
|
+
const best = aiStockRelevance(ranked[0], judgement);
|
|
2780
|
+
const top = ranked.filter((photo)=>aiStockRelevance(photo, judgement) === best);
|
|
2781
|
+
const fresh = top.filter((photo)=>!shown.some((other)=>aiStockSameShoot(photo, other)));
|
|
2782
|
+
if (!fresh.length || fresh[0] === ranked[0]) return [
|
|
2783
|
+
...ranked
|
|
2784
|
+
];
|
|
2785
|
+
return [
|
|
2786
|
+
...fresh,
|
|
2787
|
+
...ranked.filter((photo)=>!fresh.includes(photo))
|
|
2788
|
+
];
|
|
2789
|
+
}
|
|
2025
2790
|
/** The pick among a search's hits: the first {@link aiStockRank} would try, or `null`. */ export function aiStockPick(photos, used, seed, judgement = {}) {
|
|
2026
2791
|
var _aiStockRank_;
|
|
2027
2792
|
return (_aiStockRank_ = aiStockRank(photos, used, seed, judgement)[0]) != null ? _aiStockRank_ : null;
|
|
@@ -2068,61 +2833,284 @@ function jobPhotos(key) {
|
|
|
2068
2833
|
/** The longest description of a photo written from the library's own words. */ const AI_STOCK_ALT_MAX = 160;
|
|
2069
2834
|
/**
|
|
2070
2835
|
* Words a tag uses for how a photo looks or feels, never a thing it shows
|
|
2071
|
-
* (AGL-3856): "extravagant" of "wedding dress, bride, extravagant"
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2836
|
+
* (AGL-3856): "extravagant" of "wedding dress, bride, extravagant", "moody"
|
|
2837
|
+
* of "forest, human, photographer", a season or a people's name.
|
|
2838
|
+
*/ const DESCRIBING = words('love romance romantic luxury luxurious fancy extravagant happiness happy beauty emotion emotions mood lifestyle ' + 'concept aesthetic wallpaper background retro vintage antique old young cute sexy fun funny sad positive negative healthy fit ' + 'strong powerful homemade tasty yummy sweet sweetness nice dark bw monochrome macro sporty active married bridal ' + 'female male adult good bad poc bipoc moody sunny rainy foggy misty snowy cloudy stormy dreamy shiny trendy messy ' + 'casual blonde blond brunette muscular athletic clever intelligent concentrated curious lonely sleepy heavy ' + 'nature outdoors outdoor indoors indoor scenery scenic autumn fall winter spring summer ' + 'physical teenage traveling ' + // Adjectives a tag gives alone, never a thing: "students, multicultural" (AGL-3856).
|
|
2839
|
+
'multicultural multiethnic multiracial diverse international cultural traditional modern classic classical ' + 'domestic wild tropical rural urban ' + // How the photo was made, never what it shows: "cutout, perspective", "men, silhouettes".
|
|
2840
|
+
'silhouette silhouettes cutout perspective bokeh closeup ' + // A holiday names when, never a thing: "tet market" is a market.
|
|
2841
|
+
'tet christmas easter halloween diwali ramadan hanukkah thanksgiving ' + 'indian asian african american european caucasian vietnamese japanese chinese korean thai latino latina hispanic ' + 'japan vietnam viet nam thailand india china korea germany france italy spain england britain uk usa america ' + 'canada mexico brazil australia africa europe asia');
|
|
2842
|
+
/**
|
|
2843
|
+
* Words a tag uses for what a person or a thing is like, said before it
|
|
2844
|
+
* rather than as a thing of its own (AGL-3856): "a confident lawyer", "an
|
|
2845
|
+
* elderly couple", "an analog camera", never "a lawyer, law and a confident".
|
|
2846
|
+
*/ const QUALITIES = words('confident professional successful serious smart busy creative proud skilled experienced determined focused ' + 'senior junior expert friendly cheerful smiling elderly analog analogue digital mirrorless');
|
|
2847
|
+
/** What a tag names that takes no article: "chalk" of "board, chalk". */ const MASS_NOUNS = words('food coffee tea bread wood water wax soap clay money fabric yarn wool furniture jewelry jewellery ' + 'pottery glass paper music art hair makeup chalk paint ink dust stone marble concrete steel metal ' + 'skincare equipment decor fashion wildlife weather traffic wine beer chocolate rice sugar flour milk ' + 'cheese honey oil ice fire light sunlight dough sourdough pastry dessert people foliage greenery faculty staff ' + 'clothing bedding frosting icing flooring');
|
|
2848
|
+
/** Nouns plural without an "-s": said without an article, "women in a classroom". */ const PLURAL_NOUNS = words('men women people children feet teeth mice geese sheep fish');
|
|
2849
|
+
/**
|
|
2850
|
+
* What a tag names that no photo shows as a thing of its own (AGL-3856): an
|
|
2851
|
+
* idea ("law" of "lawyer, law", "feedback" of "board, chalk, feedback"), a
|
|
2852
|
+
* doing ("read", "lift", "fitness", "a workout"), a word for the photo itself
|
|
2853
|
+
* ("photo", "spot"). Left out, never said with an article: "A bookwork and a
|
|
2854
|
+
* read", "A kettlebell and fitness", "A board and training" said nothing.
|
|
2855
|
+
*/ const ABSTRACT_NOUNS = words('law justice advocacy business feedback review news return communication communicate discussion dialog conversation ' + 'success career work job satisfaction quality concept idea strategy marketing education technology health support ' + 'confirmation opinion rating evaluation planning project presentation optimization information internet finance ' + 'management leadership teamwork motivation growth strength service services help advice consulting consultation ' + 'marriage photography architecture travel transport transportation industry company corporate social e-business ' + 'fitness workout workouts exercise exercises crossfit sport sports skill skills knowledge exam exams lecture ' + 'session lesson course hobby leisure power energy wellness talent formation participation ' + 'press show day days night year years time holiday month months week weeks morning evening afternoon ' + 'kiss hug embrace ' + 'photo photos picture pictures image images shot shots shoot shoots spot spots ' + 'read write learn study lift move remove focus relax think sit smile laugh eat swim teach enjoy celebrate explore ' + 'hike can');
|
|
2856
|
+
/** Words ending as an idea does that are things: "a station", "a city", "a ship". */ const CONCRETE_ABSTRACT_ENDINGS = words('station mansion lotion potion ornament instrument apartment monument pavement basement garment compartment ' + 'equipment cement city university facility ship warship spaceship airship');
|
|
2857
|
+
/** Whether a word is an idea or a doing by how it ends: "participation", "unity", "empowerment". */ const saysAnIdea = (word)=>/(?:tion|sion|ment|ness|ity|ship|ism)s?$/.test(word) && !CONCRETE_ABSTRACT_ENDINGS.has(aiStockStem(word));
|
|
2858
|
+
/**
|
|
2859
|
+
* Makers' names a tag gives, never what a photo shows to someone who cannot
|
|
2860
|
+
* see it (AGL-3856): "A photo-camera and a sony".
|
|
2861
|
+
*/ const BRANDS = words('sony canon cannon nikon fujifilm fuji leica olympus panasonic kodak polaroid hasselblad pentax gopro dji ' + 'apple iphone ipad macbook imac samsung huawei android google microsoft windows dell lenovo nintendo playstation ' + 'xbox nike adidas puma reebok peloton bmw mercedes audi porsche ferrari lamborghini tesla ford toyota honda ' + 'volkswagen vw jeep chevrolet coca-cola pepsi starbucks mcdonalds ikea lego barbie disney altman ' + 'petri zenit praktica yashica minolta mamiya rolleiflex voigtlander contax ricoh konica lomo lomography holga zeiss');
|
|
2862
|
+
/** Stopwords a description still says, as the people a photo shows: "a family", "a person". */ const SAID_PEOPLE = words('people person family');
|
|
2863
|
+
/** Words ending in "-ing" that are things, and take an article: "a wedding", "a ring". */ const COUNTED_ING = words('wedding ring earring building painting drawing ceiling string swing king sibling wing dumpling pudding sapling');
|
|
2864
|
+
/** "-ing" words that are things, and take no article: "clothing". */ const UNCOUNTED_ING = words('clothing bedding frosting icing flooring stuffing seasoning filling');
|
|
2865
|
+
/** People a photo shows; the generic ones give way to a role ("man" to "businessman"). */ const PEOPLE_NOUNS = words('man men woman women person people human humans boy boys girl girls guy guys lady ladies child children kid kids ' + 'baby family couple bride groom schoolgirl schoolboy teen teenager student students graduate athlete athletes ' + 'businessman businesswoman businessmen lawyer attorney doctor dentist nurse chef cook baker barista ' + 'trainer coach teacher teachers instructor lecturer professor tutor worker workers entrepreneur model ' + 'photographer potter artist florist mother father parent parents grandmother grandfather grandparents ' + 'faculty staff team participants volunteer volunteers musician dancer leader manager owner founder ' + 'journalist journalists reporter reporters master masters lovers newlywed newlyweds husband wife fiance fiancee spouse');
|
|
2866
|
+
/** People said by no role: a model is whoever posed (AGL-3856), "a woman" of "woman, model, show, teacher". */ const GENERIC_PEOPLE = words('man men woman women person people human humans guy guys lady ladies adult boy boys girl girls model models');
|
|
2867
|
+
/**
|
|
2868
|
+
* A body's parts, never a thing a photo is described by (AGL-3856): "A girl
|
|
2869
|
+
* with a hand" said nothing, and "A body in a gym" is a gym.
|
|
2870
|
+
*/ const BODY_PARTS = words('hand hands finger fingers arm arms leg legs foot feet body bodies face faces eye eyes skin head heads lip lips mouth nose ' + 'ear ears neck shoulder shoulders knee knees');
|
|
2871
|
+
/**
|
|
2872
|
+
* Words for a kind of thing, said only where nothing names the thing itself
|
|
2873
|
+
* (AGL-3856): "A domestic cat and an animal" is a cat.
|
|
2874
|
+
*/ const KINDS_OF_THING = words('animal animals pet pets creature creatures mammal mammals object objects thing things item items');
|
|
2875
|
+
/** How many of a photo's first tags a person leads its description from, where it names a thing too: "A camera" of "petri, camera, film, photography, photographer" (AGL-3856). */ const ALT_PERSON_TAGS_READ = 4;
|
|
2876
|
+
/** How many of a photo's first tags a role is said from, over a man or a woman before it: "A man" of "bed, book, man, …, student" (AGL-3856). */ const ALT_ROLE_TAGS_READ = 6;
|
|
2877
|
+
/** What a person in a photo wears, said "in": "a bride in a wedding dress". */ const CLOTHING = words('dress gown suit uniform costume coat jacket robe apron');
|
|
2878
|
+
/**
|
|
2879
|
+
* Places a photo is set in, each with how a phrase says it is there: "a
|
|
2880
|
+
* train at a station", "a lawyer in court", "a couple at a wedding".
|
|
2881
|
+
*/ const PLACES = {
|
|
2882
|
+
station: 'at',
|
|
2883
|
+
market: 'at',
|
|
2884
|
+
desk: 'at',
|
|
2885
|
+
table: 'at',
|
|
2886
|
+
counter: 'at',
|
|
2887
|
+
school: 'at',
|
|
2888
|
+
college: 'at',
|
|
2889
|
+
university: 'at',
|
|
2890
|
+
wedding: 'at',
|
|
2891
|
+
sunset: 'at',
|
|
2892
|
+
sunrise: 'at',
|
|
2893
|
+
dusk: 'at',
|
|
2894
|
+
dawn: 'at',
|
|
2895
|
+
beach: 'on',
|
|
2896
|
+
bed: 'on',
|
|
2897
|
+
sofa: 'on',
|
|
2898
|
+
couch: 'on',
|
|
2899
|
+
bench: 'on',
|
|
2900
|
+
trail: 'on',
|
|
2901
|
+
path: 'on',
|
|
2902
|
+
bridge: 'on',
|
|
2903
|
+
street: 'on',
|
|
2904
|
+
court: 'in',
|
|
2905
|
+
courtroom: 'in',
|
|
2906
|
+
office: 'in',
|
|
2907
|
+
kitchen: 'in',
|
|
2908
|
+
gym: 'in',
|
|
2909
|
+
studio: 'in',
|
|
2910
|
+
classroom: 'in',
|
|
2911
|
+
class: 'in',
|
|
2912
|
+
hall: 'in',
|
|
2913
|
+
room: 'in',
|
|
2914
|
+
garden: 'in',
|
|
2915
|
+
park: 'in',
|
|
2916
|
+
shop: 'in',
|
|
2917
|
+
store: 'in',
|
|
2918
|
+
bakery: 'in',
|
|
2919
|
+
cafe: 'in',
|
|
2920
|
+
restaurant: 'in',
|
|
2921
|
+
workshop: 'in',
|
|
2922
|
+
library: 'in',
|
|
2923
|
+
salon: 'in',
|
|
2924
|
+
clinic: 'in',
|
|
2925
|
+
forest: 'in',
|
|
2926
|
+
city: 'in',
|
|
2927
|
+
church: 'in',
|
|
2928
|
+
field: 'in',
|
|
2929
|
+
meadow: 'in',
|
|
2930
|
+
valley: 'in',
|
|
2931
|
+
mountain: 'in',
|
|
2932
|
+
hill: 'on',
|
|
2933
|
+
cliff: 'on',
|
|
2934
|
+
river: 'by',
|
|
2935
|
+
lake: 'by',
|
|
2936
|
+
waterfall: 'at'
|
|
2937
|
+
};
|
|
2938
|
+
/** Places that are things too, a photo's subject where nothing else is: "a desk in a classroom". */ const OBJECT_PLACES = words('desk table counter bridge bed sofa couch bench');
|
|
2939
|
+
/** Places said without an article: "in court", "at sunset". */ const BARE_PLACES = words('court sunset sunrise dusk dawn');
|
|
2940
|
+
/** Places said in words of their own: a mountain tag is "in the mountains". */ const PLACES_SAID = {
|
|
2941
|
+
mountain: 'the mountains'
|
|
2942
|
+
};
|
|
2943
|
+
/** How many of a photo's first tags its description reads: the things and people it is OF. */ const ALT_TAGS_READ = 8;
|
|
2944
|
+
/** How far a photo's tags are read for where it is set, said last: "in a gym" of a kettlebell's seventh tag. */ const ALT_PLACE_TAGS_READ = 10;
|
|
2945
|
+
/** A tag's words, a word for the photo itself taken off a compound: "photo-camera" is a camera. */ function tagWords(tag) {
|
|
2946
|
+
return tag.toLowerCase().replace(/[^a-zÀ-ɏ\s'-]/g, ' ').replace(/\b(?:photo|picture|image)-(?=[a-z])/g, '').split(/\s+/).filter(Boolean);
|
|
2947
|
+
}
|
|
2075
2948
|
/**
|
|
2076
2949
|
* A tag as a phrase of an English description (AGL-3856): "a wedding dress",
|
|
2077
|
-
* "an attorney", "books", "
|
|
2078
|
-
* thing ("extravagant", "
|
|
2950
|
+
* "an attorney", "books", "chalk", "Columbus"; `null` for a tag that names no
|
|
2951
|
+
* thing ("extravagant", "feedback", "training", "sony") or is too long to be
|
|
2952
|
+
* one.
|
|
2079
2953
|
*/ function describedTag(tag, proper) {
|
|
2080
|
-
const describing = (word)=>FILLER.has(word) || DESCRIBING.has(word) || /(?:ous|ful)$/.test(word) || word.length > 4 && /[^e]ed$/.test(word);
|
|
2081
|
-
const all = tag
|
|
2082
|
-
// "Beautiful flowers" shows flowers; "roasted" shows nothing
|
|
2083
|
-
|
|
2954
|
+
const describing = (word)=>FILLER.has(word) || DESCRIBING.has(word) || QUALITIES.has(word) || /(?:ous|ful)$/.test(word) || word.length > 4 && /[^e]ed$/.test(word);
|
|
2955
|
+
const all = tagWords(tag);
|
|
2956
|
+
// "Beautiful flowers" shows flowers; "roasted" shows nothing; "photo model"
|
|
2957
|
+
// is a model and "traveling couples" are couples: a word before the last
|
|
2958
|
+
// that is a doing says what they do, never what they are.
|
|
2959
|
+
const list = all.filter((word, index)=>!describing(word) && !(all.length > 1 && /^(?:photo|picture|image)$/.test(word)) && !(index < all.length - 1 && index > 0 && word.endsWith('ing') && !COUNTED_ING.has(word)));
|
|
2084
2960
|
if (!list.length || all.length > 3) return null;
|
|
2085
2961
|
// A phrase of no thing ("up close") or a doing ("take pictures", "to lift", "work out") is not what it shows.
|
|
2086
|
-
if (list.every((word)=>STOPWORDS.has(word) && word
|
|
2087
|
-
if (/^(?:to|take|taking|make|making|get|do|go|lie)$/.test(list[0])) return null;
|
|
2962
|
+
if (list.every((word)=>STOPWORDS.has(word) && !SAID_PEOPLE.has(word))) return null;
|
|
2963
|
+
if (/^(?:to|take|taking|make|making|get|do|go|lie|in|on|at|for|with|by|of)$/.test(list[0])) return null;
|
|
2088
2964
|
if (/^(?:out|up|off|down|away|on|in)$/.test(list[list.length - 1])) return null;
|
|
2965
|
+
if (list.some((word)=>BRANDS.has(word))) return null;
|
|
2089
2966
|
if (list.every((word)=>proper.has(word))) return list.map((word)=>`${word.charAt(0).toUpperCase()}${word.slice(1)}`).join(' ');
|
|
2090
|
-
const phrase = list.join(' ');
|
|
2091
2967
|
const last = list[list.length - 1];
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2968
|
+
// An idea or a doing, never a thing: "law", "feedback", "customer satisfaction", "participation", "weightlifting".
|
|
2969
|
+
if (ABSTRACT_NOUNS.has(last) || ABSTRACT_NOUNS.has(aiStockStem(last)) || saysAnIdea(last)) return null;
|
|
2970
|
+
const doing = last.endsWith('ing') && !COUNTED_ING.has(aiStockStem(last)) && !UNCOUNTED_ING.has(last);
|
|
2971
|
+
if (doing) return null;
|
|
2972
|
+
const phrase = list.join(' ');
|
|
2973
|
+
const plural = PLURAL_NOUNS.has(last) || aiStockStem(last) !== last && !/^(?:lens|canvas|atlas|gas|bus)$/.test(last);
|
|
2974
|
+
if (plural || MASS_NOUNS.has(last) || STOPWORDS.has(list[0]) && !SAID_PEOPLE.has(list[0])) return phrase;
|
|
2975
|
+
return `${articleFor(phrase)} ${phrase}`;
|
|
2976
|
+
}
|
|
2977
|
+
/** "a" or "an" before a phrase. */ const articleFor = (phrase)=>/^(?:[aeio]|u(?!ni|s[eu]))/.test(phrase) ? 'an' : 'a';
|
|
2978
|
+
/** Whether two words name one thing, one inside the other: "law" and "lawyer", "cam" and "camera". */ function nearSynonyms(a, b) {
|
|
2979
|
+
const [short, long] = a.length <= b.length ? [
|
|
2980
|
+
a,
|
|
2981
|
+
b
|
|
2982
|
+
] : [
|
|
2983
|
+
b,
|
|
2984
|
+
a
|
|
2985
|
+
];
|
|
2986
|
+
return short.length >= 3 && short !== long && (long.startsWith(short) || long.endsWith(short));
|
|
2096
2987
|
}
|
|
2097
2988
|
/**
|
|
2098
|
-
* A photo described from its own tags (AGL-3856):
|
|
2099
|
-
*
|
|
2100
|
-
*
|
|
2101
|
-
*
|
|
2102
|
-
*
|
|
2989
|
+
* A photo described from its own tags (AGL-3856): a short English phrase of
|
|
2990
|
+
* concrete things, never the library's comma-separated list: "A train at a
|
|
2991
|
+
* station", "A confident lawyer in court", "A portrait of a businessman", "A
|
|
2992
|
+
* photographer with a camera on a beach", "A kettlebell in a gym", "An analog
|
|
2993
|
+
* camera". It says one person at most (a role over a man or a woman, an adult
|
|
2994
|
+
* over a girl), what they hold or wear, or one or two things, and where:
|
|
2995
|
+
*
|
|
2996
|
+
* - an idea or a doing ("law", "feedback", "training", "a read",
|
|
2997
|
+
* "participation"), a maker's name ("sony"), a word for the photo itself
|
|
2998
|
+
* ("photo", "spot") and a look ("moody", "analog" alone, a season) are left
|
|
2999
|
+
* out; a quality ("elderly", "analog") is said before the one thing it
|
|
3000
|
+
* qualifies;
|
|
3001
|
+
* - of two words for one thing the first tag's stays ("book" over
|
|
3002
|
+
* "bookwork", "camera" over "photo-camera"), unless the later phrase holds
|
|
3003
|
+
* it whole ("wedding dress" over "wedding");
|
|
3004
|
+
* - a second thing comes only from the first two tags and where no place is
|
|
3005
|
+
* said, what a person holds only from the first three, where it is set
|
|
3006
|
+
* from the first ten;
|
|
3007
|
+
* - no word is said twice: never "an elderly couple and an elderly".
|
|
3008
|
+
*
|
|
3009
|
+
* `null` where no tag names a thing.
|
|
2103
3010
|
*/ export function aiStockTagsAlt(tags, proper = new Set()) {
|
|
2104
|
-
const
|
|
2105
|
-
|
|
2106
|
-
|
|
3011
|
+
const parts = [];
|
|
3012
|
+
let quality = '';
|
|
3013
|
+
let portrait = false;
|
|
3014
|
+
for (const [index, tag] of tags.slice(0, ALT_PLACE_TAGS_READ).entries()){
|
|
3015
|
+
var _ref;
|
|
3016
|
+
var _bare_split_pop;
|
|
3017
|
+
const lower = tag.toLowerCase().trim();
|
|
3018
|
+
const reading = index < ALT_TAGS_READ;
|
|
3019
|
+
if (reading && (lower === 'portrait' || lower === 'portraits')) {
|
|
3020
|
+
portrait = true;
|
|
3021
|
+
continue;
|
|
3022
|
+
}
|
|
3023
|
+
if (QUALITIES.has(lower)) {
|
|
3024
|
+
if (reading) quality || (quality = lower);
|
|
3025
|
+
continue;
|
|
3026
|
+
}
|
|
2107
3027
|
const said = describedTag(tag, proper);
|
|
2108
3028
|
if (!said) continue;
|
|
2109
|
-
const
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
3029
|
+
const bare = said.replace(/^an?\s+/, '');
|
|
3030
|
+
const stems = stemList(bare);
|
|
3031
|
+
const head = (_ref = (_bare_split_pop = bare.split(/\s+/).pop()) == null ? void 0 : _bare_split_pop.toLowerCase()) != null ? _ref : '';
|
|
3032
|
+
if (!head) continue;
|
|
3033
|
+
const headStem = aiStockStem(head);
|
|
3034
|
+
const kind = PEOPLE_NOUNS.has(head) || PEOPLE_NOUNS.has(headStem) ? 'person' : PLACES[headStem] ? 'place' : 'thing';
|
|
3035
|
+
// Past the first tags only where it is set is read.
|
|
3036
|
+
if (!reading && kind !== 'place') continue;
|
|
3037
|
+
// A hand or a body is never what a photo is described by.
|
|
3038
|
+
if (kind === 'thing' && (BODY_PARTS.has(head) || BODY_PARTS.has(headStem))) continue;
|
|
3039
|
+
if (parts.some((part)=>stems.every((stem)=>part.stems.includes(stem)))) continue;
|
|
3040
|
+
const part = {
|
|
3041
|
+
said,
|
|
2114
3042
|
stems,
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
3043
|
+
head: headStem,
|
|
3044
|
+
kind,
|
|
3045
|
+
index
|
|
3046
|
+
};
|
|
3047
|
+
// A longer phrase of the same thing takes its place: "elderly couple" for "couple".
|
|
3048
|
+
const holds = parts.findIndex((held)=>held.head === headStem && held.stems.every((stem)=>stems.includes(stem)));
|
|
3049
|
+
if (holds >= 0) {
|
|
3050
|
+
parts.splice(holds, 1, _extends({}, part, {
|
|
3051
|
+
index: parts[holds].index
|
|
3052
|
+
}));
|
|
3053
|
+
continue;
|
|
3054
|
+
}
|
|
3055
|
+
// Two words for one thing ("book" and "bookwork", "cam" and "camera"): the
|
|
3056
|
+
// first tag's stays; a person's are told apart by their role below.
|
|
3057
|
+
if (kind !== 'person' && parts.some((held)=>held.kind !== 'person' && nearSynonyms(held.head, headStem))) continue;
|
|
3058
|
+
parts.push(part);
|
|
3059
|
+
}
|
|
3060
|
+
// One person: a role among the first tags over a man, a woman, a girl
|
|
3061
|
+
// ("businessman" over "man"), else the first; a role tagged later is not
|
|
3062
|
+
// what the photo shows ("bed, book, man, …, student" is a man), nor is one
|
|
3063
|
+
// a model posed as ("woman, model, show, teacher" is a woman).
|
|
3064
|
+
const people = parts.filter((part)=>part.kind === 'person');
|
|
3065
|
+
const posed = people.some((part)=>part.head === 'model');
|
|
3066
|
+
let places = parts.filter((part)=>part.kind === 'place');
|
|
3067
|
+
let things = parts.filter((part)=>part.kind === 'thing');
|
|
3068
|
+
// A kind of thing only where nothing names the thing: "a cat", never "a cat and an animal".
|
|
3069
|
+
if (things.some((part)=>!KINDS_OF_THING.has(part.head))) things = things.filter((part)=>!KINDS_OF_THING.has(part.head));
|
|
3070
|
+
// A person tagged after the things a photo is of is not who it shows:
|
|
3071
|
+
// "petri, camera, film, photography, photographer" is a camera, and "men,
|
|
3072
|
+
// silhouettes, camera, nature, photographer" men with a camera.
|
|
3073
|
+
const leads = (part)=>portrait || part.index < ALT_PERSON_TAGS_READ || !things.length;
|
|
3074
|
+
const role = posed ? undefined : people.find((part)=>!GENERIC_PEOPLE.has(part.head) && part.index < ALT_ROLE_TAGS_READ && leads(part));
|
|
3075
|
+
const person = role != null ? role : people.find(leads);
|
|
3076
|
+
// Nothing but places: a desk is the thing in a classroom; "a school in a classroom" says one place.
|
|
3077
|
+
if (!person && !things.length && places.length > 1) {
|
|
3078
|
+
const object = places.find((part)=>OBJECT_PLACES.has(part.head));
|
|
3079
|
+
if (object) {
|
|
3080
|
+
things = [
|
|
3081
|
+
object
|
|
3082
|
+
];
|
|
3083
|
+
places = places.filter((part)=>part !== object);
|
|
3084
|
+
} else places = places.slice(0, 1);
|
|
3085
|
+
}
|
|
3086
|
+
const place = places[0];
|
|
3087
|
+
if (!person && !things.length && !place) return null;
|
|
3088
|
+
/** Every word said so far, so none is said twice. */ const said = new Set();
|
|
3089
|
+
const fresh = (part)=>{
|
|
3090
|
+
const list = stemList(part.said.replace(/^an?\s+/, ''));
|
|
3091
|
+
if (list.some((stem)=>said.has(stem))) return false;
|
|
3092
|
+
for (const stem of list)said.add(stem);
|
|
3093
|
+
return true;
|
|
3094
|
+
};
|
|
3095
|
+
/** A quality before a single counted word: "a confident lawyer", "an analog camera". */ const qualified = (part)=>quality && /^an? \S+$/.test(part.said) && !said.has(aiStockStem(quality)) ? `${articleFor(quality)} ${quality} ${part.said.replace(/^an?\s+/, '')}` : part.said;
|
|
3096
|
+
let phrase = '';
|
|
3097
|
+
if (person && fresh(person)) {
|
|
3098
|
+
phrase = portrait ? `a portrait of ${person.said}` : qualified(person);
|
|
3099
|
+
const held = things.find((part)=>part.index <= 2);
|
|
3100
|
+
if (!portrait && held && fresh(held)) phrase += ` ${CLOTHING.has(held.head) ? 'in' : 'with'} ${held.said}`;
|
|
3101
|
+
} else if (things[0] && fresh(things[0])) {
|
|
3102
|
+
phrase = qualified(things[0]);
|
|
3103
|
+
const second = things[1];
|
|
3104
|
+
if (second && second.index <= 1 && !place && fresh(second)) phrase += ` and ${second.said}`;
|
|
3105
|
+
}
|
|
3106
|
+
if (place && fresh(place)) {
|
|
3107
|
+
var _PLACES_place_head, _PLACES_SAID_place_head;
|
|
3108
|
+
const preposition = (_PLACES_place_head = PLACES[place.head]) != null ? _PLACES_place_head : 'in';
|
|
3109
|
+
const where = (_PLACES_SAID_place_head = PLACES_SAID[place.head]) != null ? _PLACES_SAID_place_head : BARE_PLACES.has(place.head) && /^an? \S+$/.test(place.said) ? place.head : place.said;
|
|
3110
|
+
phrase = phrase ? `${phrase} ${preposition} ${where}` : where;
|
|
2121
3111
|
}
|
|
2122
|
-
if (!
|
|
2123
|
-
const
|
|
2124
|
-
const joined = said.length > 1 ? `${said.slice(0, -1).join(', ')} and ${said[said.length - 1]}` : said[0];
|
|
2125
|
-
const out = `${joined.charAt(0).toUpperCase()}${joined.slice(1)}`;
|
|
3112
|
+
if (!phrase) return null;
|
|
3113
|
+
const out = `${phrase.charAt(0).toUpperCase()}${phrase.slice(1)}`;
|
|
2126
3114
|
return out.length > AI_STOCK_ALT_MAX ? out.slice(0, AI_STOCK_ALT_MAX).replace(/[\s,]+\S*$/, '') : out;
|
|
2127
3115
|
}
|
|
2128
3116
|
/** The thing a description names: its subject's object, a trailing "speaking calmly" or "dusted" dropped. */ function describedObject(text, sectionName) {
|
|
@@ -2155,12 +3143,13 @@ function jobPhotos(key) {
|
|
|
2155
3143
|
* description, and are never kept. Otherwise the photo is described by the
|
|
2156
3144
|
* library's own words: its description where it writes one (a Pexels hit's
|
|
2157
3145
|
* `alt`), else its first tags as an English phrase ({@link aiStockTagsAlt}),
|
|
2158
|
-
* never the library's tag list
|
|
3146
|
+
* never the library's tag list, and where no tag names a thing, the slot's
|
|
3147
|
+
* own subject: its words, else its section's name. The libraries write in English, and no model
|
|
2159
3148
|
* translates them, so a site in another language takes the platform's own
|
|
2160
3149
|
* words for an illustrative photo in its language (`aiLayoutLabel`), true of
|
|
2161
3150
|
* any photo, as does a hit whose words name no thing.
|
|
2162
3151
|
*/ export function aiStockPhotoAlt(photo, slot, input = {}) {
|
|
2163
|
-
var _photo_alt, _aiStockTagsAlt, _photo_tags;
|
|
3152
|
+
var _photo_alt, _input_sectionName, _aiStockTagsAlt, _photo_tags;
|
|
2164
3153
|
if (aiLayoutSpeaksOtherThanEnglish(input.language)) return aiLayoutLabel('illustrativePhoto', input.language);
|
|
2165
3154
|
if (!slot.standIn) {
|
|
2166
3155
|
var _photo_tags1, _ref;
|
|
@@ -2177,7 +3166,13 @@ function jobPhotos(key) {
|
|
|
2177
3166
|
}
|
|
2178
3167
|
const own = ((_photo_alt = photo.alt) != null ? _photo_alt : '').replace(/\s+/g, ' ').trim();
|
|
2179
3168
|
if (own) return own.length > AI_STOCK_ALT_MAX ? `${own.slice(0, AI_STOCK_ALT_MAX).replace(/\s+\S*$/, '')}…` : own;
|
|
2180
|
-
|
|
3169
|
+
// No tag names a thing: the slot's own subject, as its words or its section's name say it, never a tag list.
|
|
3170
|
+
const subject = [
|
|
3171
|
+
slot.alt,
|
|
3172
|
+
(_input_sectionName = input.sectionName) != null ? _input_sectionName : ''
|
|
3173
|
+
].map((text)=>text.replace(/\s+/g, ' ').trim()).find((text)=>/[A-Za-zÀ-ɏ]/.test(text));
|
|
3174
|
+
const fallback = subject ? `${subject.charAt(0).toUpperCase()}${subject.slice(1)}`.slice(0, AI_STOCK_ALT_MAX) : '';
|
|
3175
|
+
return (_aiStockTagsAlt = aiStockTagsAlt((_photo_tags = photo.tags) != null ? _photo_tags : [], input.proper)) != null ? _aiStockTagsAlt : fallback || aiLayoutLabel('illustrativePhoto', input.language);
|
|
2181
3176
|
}
|
|
2182
3177
|
/** The names a business type writes with a capital, lowercased: "columbus", "ohio" (AGL-3856). */ export function aiStockProperWords(businessType) {
|
|
2183
3178
|
const found = new Set();
|
|
@@ -2190,19 +3185,33 @@ const EXTENSIONS = {
|
|
|
2190
3185
|
'image/webp': 'webp'
|
|
2191
3186
|
};
|
|
2192
3187
|
/**
|
|
2193
|
-
* A job's language and English photo words (AGL-3760),
|
|
2194
|
-
*
|
|
3188
|
+
* A job's language and English photo words (AGL-3760), the English name of
|
|
3189
|
+
* its kind of site (AGL-3771), and the business's name and audience as the
|
|
3190
|
+
* person typed them (AGL-3856), as a stock photo source takes them.
|
|
2195
3191
|
*/ export function aiStockPhotoWordsOf(job) {
|
|
2196
3192
|
const plan = job.plan;
|
|
2197
3193
|
const kind = job.inputs ? aiSiteKindOfInputs(job.inputs) : null;
|
|
2198
3194
|
// "Restaurant & café" searches as "restaurant cafe".
|
|
2199
3195
|
const kindWords = kind ? kind.label.normalize('NFD').replace(/[\u0300-\u036f]/g, '').replace(/&/g, ' ').replace(/\s+/g, ' ').trim().toLowerCase() : '';
|
|
3196
|
+
const said = (key)=>{
|
|
3197
|
+
var _job_inputs;
|
|
3198
|
+
const value = (_job_inputs = job.inputs) == null ? void 0 : _job_inputs[key];
|
|
3199
|
+
return typeof value === 'string' ? value.replace(/\s+/g, ' ').trim() : '';
|
|
3200
|
+
};
|
|
3201
|
+
const name = said('businessName');
|
|
3202
|
+
const audience = said('audience');
|
|
2200
3203
|
return _extends({}, (plan == null ? void 0 : plan.language) ? {
|
|
2201
3204
|
language: plan.language
|
|
2202
3205
|
} : {}, (plan == null ? void 0 : plan.photoWords) ? {
|
|
2203
3206
|
photoWords: plan.photoWords
|
|
2204
3207
|
} : {}, kindWords ? {
|
|
2205
3208
|
kindWords
|
|
3209
|
+
} : {}, kind ? {
|
|
3210
|
+
kind: kind.id
|
|
3211
|
+
} : {}, name ? {
|
|
3212
|
+
name
|
|
3213
|
+
} : {}, audience ? {
|
|
3214
|
+
audience
|
|
2206
3215
|
} : {});
|
|
2207
3216
|
}
|
|
2208
3217
|
/**
|
|
@@ -2229,9 +3238,580 @@ const EXTENSIONS = {
|
|
|
2229
3238
|
* its business named in English, else its plan's photo words' subject, else
|
|
2230
3239
|
* its kind of site), every other site by its business type. One reading for
|
|
2231
3240
|
* every slot of every source — hero, background, gallery, card, product,
|
|
2232
|
-
* post — so no slot searches by words another slot is held against.
|
|
3241
|
+
* post — so no slot searches by words another slot is held against. A site for
|
|
3242
|
+
* adults alone ({@link aiStockAdultsOnly}) is told so, whatever its world.
|
|
2233
3243
|
*/ export function aiStockSourceTerms(input) {
|
|
2234
|
-
|
|
3244
|
+
var _input_audience;
|
|
3245
|
+
const terms = sourceWorldTerms(input);
|
|
3246
|
+
// Where the site's own words set it: a photographer "in the Blue Ridge" for "mountain weddings" (AGL-3856).
|
|
3247
|
+
const setting = aiStockSetting([
|
|
3248
|
+
input.business,
|
|
3249
|
+
(_input_audience = input.audience) != null ? _input_audience : ''
|
|
3250
|
+
].join(' . '));
|
|
3251
|
+
return _extends({}, terms, aiStockAdultsOnly([
|
|
3252
|
+
input.audience,
|
|
3253
|
+
input.name,
|
|
3254
|
+
input.business
|
|
3255
|
+
]) ? {
|
|
3256
|
+
adults: true
|
|
3257
|
+
} : {}, setting ? {
|
|
3258
|
+
setting
|
|
3259
|
+
} : {});
|
|
3260
|
+
}
|
|
3261
|
+
/** What a source's photos are searched by: its world, as {@link aiStockSourceTerms} reads it. */ function sourceWorldTerms(input) {
|
|
3262
|
+
var _input_name, _input_audience, _input_name1, _input_audience1, _input_kind;
|
|
3263
|
+
const english = aiStockSourceSubject(input);
|
|
3264
|
+
const terms = aiStockSiteTerms(english || input.business);
|
|
3265
|
+
if (english || terms.domain.length && !aiStockIsAbbreviation(input.business)) return terms;
|
|
3266
|
+
// A business type that names no world ("a local service business"), or
|
|
3267
|
+
// one the libraries do not know ("LMS"), is searched by the craft its
|
|
3268
|
+
// name, else its audience, names (AGL-3856): the beta.261 Synchronicity Gym
|
|
3269
|
+
// hero searched nothing at all and was left a black scrim, and its closing
|
|
3270
|
+
// band, held to no world, took a subway train for "Ready to train". Then by
|
|
3271
|
+
// the kind of site its name says ("Adult Hybrid Learning Center"), and
|
|
3272
|
+
// last by the kind of site the person picked: the beta.262 Adult Hybrid
|
|
3273
|
+
// Learning Center searched "lms" and shipped no photo at all.
|
|
3274
|
+
const craft = aiStockListedCraft((_input_name = input.name) != null ? _input_name : '') || aiStockListedCraft((_input_audience = input.audience) != null ? _input_audience : '');
|
|
3275
|
+
const kind = craft ? '' : aiStockNamedKind((_input_name1 = input.name) != null ? _input_name1 : '') || aiStockNamedKind((_input_audience1 = input.audience) != null ? _input_audience1 : '') || ((_input_kind = input.kind) != null ? _input_kind : '');
|
|
3276
|
+
const found = craft ? aiStockSiteTerms(craft) : aiStockKindTerms(kind);
|
|
3277
|
+
if (!found) return terms;
|
|
3278
|
+
return _extends({}, found, {
|
|
3279
|
+
named: [
|
|
3280
|
+
...new Set([
|
|
3281
|
+
...terms.named,
|
|
3282
|
+
...found.named
|
|
3283
|
+
])
|
|
3284
|
+
]
|
|
3285
|
+
});
|
|
3286
|
+
}
|
|
3287
|
+
/**
|
|
3288
|
+
* Whether a business type is only an abbreviation the libraries do not know
|
|
3289
|
+
* (AGL-3856): "LMS", "CRM", "B2B". Its letters are no world to search.
|
|
3290
|
+
*/ export function aiStockIsAbbreviation(businessType) {
|
|
3291
|
+
const list = businessType.trim().split(/\s+/).filter(Boolean);
|
|
3292
|
+
return list.length > 0 && list.length <= 2 && list.every((word)=>/^[A-Z0-9&/.-]{2,6}s?$/.test(word) && /[A-Z]/.test(word));
|
|
3293
|
+
}
|
|
3294
|
+
/**
|
|
3295
|
+
* What a picture of each kind of site the guided start offers shows, where
|
|
3296
|
+
* nothing else says (AGL-3856): the words it is searched by, the words a
|
|
3297
|
+
* photo of it is tagged with, and its broad searches. A kind whose subject
|
|
3298
|
+
* is a craft {@link CRAFT_KIN} lists takes that craft's own.
|
|
3299
|
+
*/ const KIND_SUBJECTS = {
|
|
3300
|
+
business: {
|
|
3301
|
+
subject: 'small business',
|
|
3302
|
+
kin: [
|
|
3303
|
+
'business',
|
|
3304
|
+
'office',
|
|
3305
|
+
'team',
|
|
3306
|
+
'meeting',
|
|
3307
|
+
'workplace',
|
|
3308
|
+
'coworkers',
|
|
3309
|
+
'entrepreneur',
|
|
3310
|
+
'shop',
|
|
3311
|
+
'service',
|
|
3312
|
+
'customer'
|
|
3313
|
+
],
|
|
3314
|
+
broad: [
|
|
3315
|
+
'small business owner',
|
|
3316
|
+
'team meeting office'
|
|
3317
|
+
]
|
|
3318
|
+
},
|
|
3319
|
+
trades: {
|
|
3320
|
+
subject: 'contractor',
|
|
3321
|
+
kin: [
|
|
3322
|
+
'contractor',
|
|
3323
|
+
'construction',
|
|
3324
|
+
'repair',
|
|
3325
|
+
'renovation',
|
|
3326
|
+
'tools',
|
|
3327
|
+
'worker',
|
|
3328
|
+
'handyman',
|
|
3329
|
+
'plumber',
|
|
3330
|
+
'electrician',
|
|
3331
|
+
'house',
|
|
3332
|
+
'roof'
|
|
3333
|
+
],
|
|
3334
|
+
broad: [
|
|
3335
|
+
'home repair contractor',
|
|
3336
|
+
'construction worker tools'
|
|
3337
|
+
]
|
|
3338
|
+
},
|
|
3339
|
+
professional: {
|
|
3340
|
+
subject: 'consultant',
|
|
3341
|
+
kin: [
|
|
3342
|
+
'consultant',
|
|
3343
|
+
'consulting',
|
|
3344
|
+
'office',
|
|
3345
|
+
'meeting',
|
|
3346
|
+
'client',
|
|
3347
|
+
'advisor',
|
|
3348
|
+
'finance',
|
|
3349
|
+
'accountant',
|
|
3350
|
+
'lawyer',
|
|
3351
|
+
'desk',
|
|
3352
|
+
'business'
|
|
3353
|
+
],
|
|
3354
|
+
broad: [
|
|
3355
|
+
'consultant client meeting',
|
|
3356
|
+
'professional office'
|
|
3357
|
+
]
|
|
3358
|
+
},
|
|
3359
|
+
wellness: {
|
|
3360
|
+
subject: 'wellness',
|
|
3361
|
+
kin: [
|
|
3362
|
+
'wellness',
|
|
3363
|
+
'health',
|
|
3364
|
+
'therapy',
|
|
3365
|
+
'therapist',
|
|
3366
|
+
'massage',
|
|
3367
|
+
'spa',
|
|
3368
|
+
'relaxation',
|
|
3369
|
+
'clinic',
|
|
3370
|
+
'healthcare'
|
|
3371
|
+
],
|
|
3372
|
+
broad: [
|
|
3373
|
+
'wellness therapy',
|
|
3374
|
+
'massage therapy'
|
|
3375
|
+
]
|
|
3376
|
+
},
|
|
3377
|
+
restaurant: {
|
|
3378
|
+
subject: 'restaurant',
|
|
3379
|
+
kin: [
|
|
3380
|
+
'restaurant',
|
|
3381
|
+
'dining',
|
|
3382
|
+
'food',
|
|
3383
|
+
'dish',
|
|
3384
|
+
'chef',
|
|
3385
|
+
'meal',
|
|
3386
|
+
'cafe',
|
|
3387
|
+
'cuisine',
|
|
3388
|
+
'dinner',
|
|
3389
|
+
'lunch'
|
|
3390
|
+
],
|
|
3391
|
+
broad: [
|
|
3392
|
+
'restaurant dining',
|
|
3393
|
+
'restaurant food'
|
|
3394
|
+
]
|
|
3395
|
+
},
|
|
3396
|
+
store: {
|
|
3397
|
+
subject: 'boutique',
|
|
3398
|
+
kin: [
|
|
3399
|
+
'boutique',
|
|
3400
|
+
'shop',
|
|
3401
|
+
'store',
|
|
3402
|
+
'shopping',
|
|
3403
|
+
'retail',
|
|
3404
|
+
'products',
|
|
3405
|
+
'display',
|
|
3406
|
+
'shelf'
|
|
3407
|
+
],
|
|
3408
|
+
broad: [
|
|
3409
|
+
'boutique shop',
|
|
3410
|
+
'retail store display'
|
|
3411
|
+
]
|
|
3412
|
+
},
|
|
3413
|
+
portfolio: {
|
|
3414
|
+
subject: 'artist',
|
|
3415
|
+
kin: [
|
|
3416
|
+
'art',
|
|
3417
|
+
'artist',
|
|
3418
|
+
'design',
|
|
3419
|
+
'creative',
|
|
3420
|
+
'studio',
|
|
3421
|
+
'workspace',
|
|
3422
|
+
'painting',
|
|
3423
|
+
'drawing'
|
|
3424
|
+
],
|
|
3425
|
+
broad: [
|
|
3426
|
+
'artist studio',
|
|
3427
|
+
'creative workspace'
|
|
3428
|
+
]
|
|
3429
|
+
},
|
|
3430
|
+
studio: {
|
|
3431
|
+
subject: 'design studio',
|
|
3432
|
+
kin: [
|
|
3433
|
+
'design',
|
|
3434
|
+
'designer',
|
|
3435
|
+
'studio',
|
|
3436
|
+
'creative',
|
|
3437
|
+
'agency',
|
|
3438
|
+
'team',
|
|
3439
|
+
'workspace',
|
|
3440
|
+
'office'
|
|
3441
|
+
],
|
|
3442
|
+
broad: [
|
|
3443
|
+
'design studio team',
|
|
3444
|
+
'creative agency office'
|
|
3445
|
+
]
|
|
3446
|
+
},
|
|
3447
|
+
photography: {
|
|
3448
|
+
subject: 'photographer'
|
|
3449
|
+
},
|
|
3450
|
+
blog: {
|
|
3451
|
+
subject: 'writer',
|
|
3452
|
+
kin: [
|
|
3453
|
+
'writer',
|
|
3454
|
+
'writing',
|
|
3455
|
+
'notebook',
|
|
3456
|
+
'laptop',
|
|
3457
|
+
'desk',
|
|
3458
|
+
'journal',
|
|
3459
|
+
'reading',
|
|
3460
|
+
'book'
|
|
3461
|
+
],
|
|
3462
|
+
broad: [
|
|
3463
|
+
'writer desk laptop',
|
|
3464
|
+
'writing notebook'
|
|
3465
|
+
]
|
|
3466
|
+
},
|
|
3467
|
+
events: {
|
|
3468
|
+
subject: 'event',
|
|
3469
|
+
kin: [
|
|
3470
|
+
'event',
|
|
3471
|
+
'celebration',
|
|
3472
|
+
'party',
|
|
3473
|
+
'wedding',
|
|
3474
|
+
'venue',
|
|
3475
|
+
'reception',
|
|
3476
|
+
'gathering',
|
|
3477
|
+
'decoration',
|
|
3478
|
+
'banquet'
|
|
3479
|
+
],
|
|
3480
|
+
broad: [
|
|
3481
|
+
'event celebration',
|
|
3482
|
+
'event venue decoration'
|
|
3483
|
+
]
|
|
3484
|
+
},
|
|
3485
|
+
fitness: {
|
|
3486
|
+
subject: 'gym'
|
|
3487
|
+
},
|
|
3488
|
+
yoga: {
|
|
3489
|
+
subject: 'yoga',
|
|
3490
|
+
kin: [
|
|
3491
|
+
'yoga',
|
|
3492
|
+
'meditation',
|
|
3493
|
+
'mindfulness',
|
|
3494
|
+
'pose',
|
|
3495
|
+
'mat',
|
|
3496
|
+
'relaxation',
|
|
3497
|
+
'stretching'
|
|
3498
|
+
],
|
|
3499
|
+
broad: [
|
|
3500
|
+
'yoga class',
|
|
3501
|
+
'meditation'
|
|
3502
|
+
]
|
|
3503
|
+
},
|
|
3504
|
+
beauty: {
|
|
3505
|
+
subject: 'salon',
|
|
3506
|
+
kin: [
|
|
3507
|
+
'salon',
|
|
3508
|
+
'beauty',
|
|
3509
|
+
'hair',
|
|
3510
|
+
'hairdresser',
|
|
3511
|
+
'makeup',
|
|
3512
|
+
'nails',
|
|
3513
|
+
'manicure',
|
|
3514
|
+
'stylist',
|
|
3515
|
+
'spa',
|
|
3516
|
+
'cosmetics'
|
|
3517
|
+
],
|
|
3518
|
+
broad: [
|
|
3519
|
+
'beauty salon',
|
|
3520
|
+
'hair salon'
|
|
3521
|
+
]
|
|
3522
|
+
},
|
|
3523
|
+
realestate: {
|
|
3524
|
+
subject: 'real estate',
|
|
3525
|
+
kin: [
|
|
3526
|
+
'house',
|
|
3527
|
+
'home',
|
|
3528
|
+
'estate',
|
|
3529
|
+
'property',
|
|
3530
|
+
'interior',
|
|
3531
|
+
'apartment',
|
|
3532
|
+
'realtor',
|
|
3533
|
+
'architecture',
|
|
3534
|
+
'living'
|
|
3535
|
+
],
|
|
3536
|
+
broad: [
|
|
3537
|
+
'house exterior',
|
|
3538
|
+
'home interior'
|
|
3539
|
+
]
|
|
3540
|
+
},
|
|
3541
|
+
education: {
|
|
3542
|
+
subject: 'classroom',
|
|
3543
|
+
kin: [
|
|
3544
|
+
'classroom',
|
|
3545
|
+
'student',
|
|
3546
|
+
'students',
|
|
3547
|
+
'education',
|
|
3548
|
+
'learning',
|
|
3549
|
+
'e-learning',
|
|
3550
|
+
'teacher',
|
|
3551
|
+
'teaching',
|
|
3552
|
+
'class',
|
|
3553
|
+
'lecture',
|
|
3554
|
+
'study',
|
|
3555
|
+
'studying',
|
|
3556
|
+
'course',
|
|
3557
|
+
'school',
|
|
3558
|
+
'seminar',
|
|
3559
|
+
'training',
|
|
3560
|
+
'book',
|
|
3561
|
+
'books',
|
|
3562
|
+
'laptop'
|
|
3563
|
+
],
|
|
3564
|
+
broad: [
|
|
3565
|
+
'adult students classroom',
|
|
3566
|
+
'online learning',
|
|
3567
|
+
'adult education'
|
|
3568
|
+
]
|
|
3569
|
+
},
|
|
3570
|
+
nonprofit: {
|
|
3571
|
+
subject: 'volunteers',
|
|
3572
|
+
kin: [
|
|
3573
|
+
'volunteer',
|
|
3574
|
+
'volunteers',
|
|
3575
|
+
'charity',
|
|
3576
|
+
'helping',
|
|
3577
|
+
'donation',
|
|
3578
|
+
'nonprofit',
|
|
3579
|
+
'team',
|
|
3580
|
+
'people'
|
|
3581
|
+
],
|
|
3582
|
+
broad: [
|
|
3583
|
+
'charity volunteers',
|
|
3584
|
+
'volunteers helping'
|
|
3585
|
+
]
|
|
3586
|
+
},
|
|
3587
|
+
music: {
|
|
3588
|
+
subject: 'musician',
|
|
3589
|
+
kin: [
|
|
3590
|
+
'music',
|
|
3591
|
+
'musician',
|
|
3592
|
+
'band',
|
|
3593
|
+
'concert',
|
|
3594
|
+
'guitar',
|
|
3595
|
+
'stage',
|
|
3596
|
+
'singer',
|
|
3597
|
+
'instrument',
|
|
3598
|
+
'piano',
|
|
3599
|
+
'drums'
|
|
3600
|
+
],
|
|
3601
|
+
broad: [
|
|
3602
|
+
'live music band',
|
|
3603
|
+
'musician guitar'
|
|
3604
|
+
]
|
|
3605
|
+
},
|
|
3606
|
+
personal: {
|
|
3607
|
+
subject: 'workspace',
|
|
3608
|
+
kin: [
|
|
3609
|
+
'workspace',
|
|
3610
|
+
'desk',
|
|
3611
|
+
'laptop',
|
|
3612
|
+
'office',
|
|
3613
|
+
'professional',
|
|
3614
|
+
'notebook',
|
|
3615
|
+
'coffee'
|
|
3616
|
+
],
|
|
3617
|
+
broad: [
|
|
3618
|
+
'workspace desk laptop',
|
|
3619
|
+
'professional workspace'
|
|
3620
|
+
]
|
|
3621
|
+
},
|
|
3622
|
+
landing: {
|
|
3623
|
+
subject: 'startup team',
|
|
3624
|
+
kin: [
|
|
3625
|
+
'startup',
|
|
3626
|
+
'team',
|
|
3627
|
+
'technology',
|
|
3628
|
+
'laptop',
|
|
3629
|
+
'office',
|
|
3630
|
+
'workspace',
|
|
3631
|
+
'business',
|
|
3632
|
+
'meeting'
|
|
3633
|
+
],
|
|
3634
|
+
broad: [
|
|
3635
|
+
'startup team office',
|
|
3636
|
+
'modern workspace'
|
|
3637
|
+
]
|
|
3638
|
+
},
|
|
3639
|
+
'coming-soon': {
|
|
3640
|
+
subject: 'workspace',
|
|
3641
|
+
kin: [
|
|
3642
|
+
'workspace',
|
|
3643
|
+
'desk',
|
|
3644
|
+
'laptop',
|
|
3645
|
+
'office',
|
|
3646
|
+
'notebook',
|
|
3647
|
+
'coffee'
|
|
3648
|
+
],
|
|
3649
|
+
broad: [
|
|
3650
|
+
'modern workspace',
|
|
3651
|
+
'workspace desk laptop'
|
|
3652
|
+
]
|
|
3653
|
+
}
|
|
3654
|
+
};
|
|
3655
|
+
/** Words a business's name or audience uses for a kind of site (AGL-3856): "Learning Center" is a school's. */ const NAMED_KINDS = [
|
|
3656
|
+
{
|
|
3657
|
+
kind: 'education',
|
|
3658
|
+
words: [
|
|
3659
|
+
'learning',
|
|
3660
|
+
'academy',
|
|
3661
|
+
'school',
|
|
3662
|
+
'tutoring',
|
|
3663
|
+
'tutor',
|
|
3664
|
+
'tutors',
|
|
3665
|
+
'college',
|
|
3666
|
+
'institute',
|
|
3667
|
+
'university',
|
|
3668
|
+
'courses',
|
|
3669
|
+
'classes',
|
|
3670
|
+
'lessons',
|
|
3671
|
+
'education',
|
|
3672
|
+
'students',
|
|
3673
|
+
'learners'
|
|
3674
|
+
]
|
|
3675
|
+
},
|
|
3676
|
+
{
|
|
3677
|
+
kind: 'beauty',
|
|
3678
|
+
words: [
|
|
3679
|
+
'salon',
|
|
3680
|
+
'barber',
|
|
3681
|
+
'barbershop',
|
|
3682
|
+
'nails',
|
|
3683
|
+
'spa',
|
|
3684
|
+
'lashes',
|
|
3685
|
+
'brows'
|
|
3686
|
+
]
|
|
3687
|
+
},
|
|
3688
|
+
{
|
|
3689
|
+
kind: 'realestate',
|
|
3690
|
+
words: [
|
|
3691
|
+
'realty',
|
|
3692
|
+
'realtor',
|
|
3693
|
+
'realtors',
|
|
3694
|
+
'properties',
|
|
3695
|
+
'homes',
|
|
3696
|
+
'estate'
|
|
3697
|
+
]
|
|
3698
|
+
},
|
|
3699
|
+
{
|
|
3700
|
+
kind: 'restaurant',
|
|
3701
|
+
words: [
|
|
3702
|
+
'restaurant',
|
|
3703
|
+
'bistro',
|
|
3704
|
+
'kitchen',
|
|
3705
|
+
'grill',
|
|
3706
|
+
'diner',
|
|
3707
|
+
'eatery',
|
|
3708
|
+
'cafe',
|
|
3709
|
+
'café',
|
|
3710
|
+
'tavern',
|
|
3711
|
+
'pizzeria'
|
|
3712
|
+
]
|
|
3713
|
+
},
|
|
3714
|
+
{
|
|
3715
|
+
kind: 'trades',
|
|
3716
|
+
words: [
|
|
3717
|
+
'plumbing',
|
|
3718
|
+
'roofing',
|
|
3719
|
+
'hvac',
|
|
3720
|
+
'electric',
|
|
3721
|
+
'electrical',
|
|
3722
|
+
'landscaping',
|
|
3723
|
+
'construction',
|
|
3724
|
+
'contracting',
|
|
3725
|
+
'remodeling',
|
|
3726
|
+
'painting',
|
|
3727
|
+
'handyman'
|
|
3728
|
+
]
|
|
3729
|
+
},
|
|
3730
|
+
{
|
|
3731
|
+
kind: 'nonprofit',
|
|
3732
|
+
words: [
|
|
3733
|
+
'foundation',
|
|
3734
|
+
'charity',
|
|
3735
|
+
'church',
|
|
3736
|
+
'ministry',
|
|
3737
|
+
'nonprofit',
|
|
3738
|
+
'volunteers'
|
|
3739
|
+
]
|
|
3740
|
+
},
|
|
3741
|
+
{
|
|
3742
|
+
kind: 'yoga',
|
|
3743
|
+
words: [
|
|
3744
|
+
'yoga',
|
|
3745
|
+
'meditation',
|
|
3746
|
+
'mindfulness'
|
|
3747
|
+
]
|
|
3748
|
+
},
|
|
3749
|
+
{
|
|
3750
|
+
kind: 'music',
|
|
3751
|
+
words: [
|
|
3752
|
+
'band',
|
|
3753
|
+
'music',
|
|
3754
|
+
'orchestra',
|
|
3755
|
+
'choir',
|
|
3756
|
+
'records'
|
|
3757
|
+
]
|
|
3758
|
+
},
|
|
3759
|
+
{
|
|
3760
|
+
kind: 'wellness',
|
|
3761
|
+
words: [
|
|
3762
|
+
'wellness',
|
|
3763
|
+
'therapy',
|
|
3764
|
+
'massage',
|
|
3765
|
+
'clinic',
|
|
3766
|
+
'chiropractic',
|
|
3767
|
+
'counseling'
|
|
3768
|
+
]
|
|
3769
|
+
}
|
|
3770
|
+
];
|
|
3771
|
+
/** The kind of site a business's name or audience names ({@link NAMED_KINDS}), or empty. */ export function aiStockNamedKind(text) {
|
|
3772
|
+
var _ref;
|
|
3773
|
+
var _NAMED_KINDS_find;
|
|
3774
|
+
const named = new Set(text.toLowerCase().split(/[^a-zÀ-ɏ]+/).filter(Boolean));
|
|
3775
|
+
return (_ref = (_NAMED_KINDS_find = NAMED_KINDS.find((entry)=>entry.words.some((word)=>named.has(word)))) == null ? void 0 : _NAMED_KINDS_find.kind) != null ? _ref : '';
|
|
3776
|
+
}
|
|
3777
|
+
/** What a kind of site's pictures are searched and judged by ({@link KIND_SUBJECTS}); `null` for a kind not listed. */ export function aiStockKindTerms(kind) {
|
|
3778
|
+
var _entry_broad;
|
|
3779
|
+
var _entry_kin;
|
|
3780
|
+
const entry = KIND_SUBJECTS[kind];
|
|
3781
|
+
if (!entry) return null;
|
|
3782
|
+
const base = aiStockSiteTerms(entry.subject);
|
|
3783
|
+
if (!((_entry_kin = entry.kin) == null ? void 0 : _entry_kin.length)) return base;
|
|
3784
|
+
const kin = entry.kin.map(aiStockStem);
|
|
3785
|
+
return _extends({}, base, {
|
|
3786
|
+
domain: [
|
|
3787
|
+
...new Set([
|
|
3788
|
+
...base.domain,
|
|
3789
|
+
...kin
|
|
3790
|
+
])
|
|
3791
|
+
],
|
|
3792
|
+
broad: [
|
|
3793
|
+
...(_entry_broad = entry.broad) != null ? _entry_broad : base.broad
|
|
3794
|
+
],
|
|
3795
|
+
// A hero names one of the kind's own words, never only the subject's.
|
|
3796
|
+
core: [
|
|
3797
|
+
...new Set([
|
|
3798
|
+
...base.core,
|
|
3799
|
+
...kin
|
|
3800
|
+
])
|
|
3801
|
+
]
|
|
3802
|
+
});
|
|
3803
|
+
}
|
|
3804
|
+
/**
|
|
3805
|
+
* The first word of a text that names a craft {@link CRAFT_KIN} lists, as
|
|
3806
|
+
* its table writes it: "gym" of "Synchronicity Gym", "law" of "Hartwell
|
|
3807
|
+
* Reyes Family Law". Empty where it names none, so a name that is only a
|
|
3808
|
+
* name ("Hillside") never becomes a world.
|
|
3809
|
+
*/ export function aiStockListedCraft(text) {
|
|
3810
|
+
for (const stem of stemList(text))for (const group of CRAFT_KIN){
|
|
3811
|
+
const word = group.match.find((match)=>aiStockStem(match) === stem);
|
|
3812
|
+
if (word) return word;
|
|
3813
|
+
}
|
|
3814
|
+
return '';
|
|
2235
3815
|
}
|
|
2236
3816
|
/**
|
|
2237
3817
|
* What a site in another language is searched by ({@link aiStockSourceTerms}),
|
|
@@ -2286,6 +3866,7 @@ const EXTENSIONS = {
|
|
|
2286
3866
|
const avoid = new Set((_input_avoid = input.avoid) != null ? _input_avoid : []);
|
|
2287
3867
|
/** Placed elsewhere in the job: by another page in this process, or one the caller read. */ const elsewhere = (key)=>avoid.has(key) || (job == null ? void 0 : job.has(key)) === true && job.get(key) !== scope;
|
|
2288
3868
|
/** Source keys this page has claimed for a slot, or turned down. */ const claimed = new Set();
|
|
3869
|
+
/** The photos this page's slots have claimed, first choices, by slot order. */ const shown = [];
|
|
2289
3870
|
/** Asset srcs this page has placed. */ const placedSrcs = new Set();
|
|
2290
3871
|
const answers = new Map();
|
|
2291
3872
|
let searches = 0;
|
|
@@ -2308,6 +3889,12 @@ const EXTENSIONS = {
|
|
|
2308
3889
|
delete request.leadWorld;
|
|
2309
3890
|
delete request.first;
|
|
2310
3891
|
delete request.category;
|
|
3892
|
+
delete request.strict;
|
|
3893
|
+
delete request.refuse;
|
|
3894
|
+
delete request.needs;
|
|
3895
|
+
delete request.allow;
|
|
3896
|
+
delete request.refuseLead;
|
|
3897
|
+
delete request.worldless;
|
|
2311
3898
|
const key = JSON.stringify([
|
|
2312
3899
|
request.query,
|
|
2313
3900
|
request.orientation,
|
|
@@ -2414,14 +4001,51 @@ const EXTENSIONS = {
|
|
|
2414
4001
|
}
|
|
2415
4002
|
return null;
|
|
2416
4003
|
};
|
|
2417
|
-
/**
|
|
4004
|
+
/**
|
|
4005
|
+
* How a search's hits are judged for a slot. On a site for adults alone no
|
|
4006
|
+
* hit names a child; a portrait of one of the business's people names an
|
|
4007
|
+
* adult, never leads with a child, and is preferred in its role (AGL-3856).
|
|
4008
|
+
*/ const judgementFor = (slot, request, subject)=>{
|
|
4009
|
+
var _request_refuse, _input_sectionNames_slot_sectionIndex, _request_refuseLead;
|
|
4010
|
+
var _request_needs, _role_scene, _role_refuse;
|
|
4011
|
+
const refuse = [
|
|
4012
|
+
...(_request_refuse = request.refuse) != null ? _request_refuse : [],
|
|
4013
|
+
...terms.adults ? CHILD_STEMS : []
|
|
4014
|
+
];
|
|
4015
|
+
const role = slot.people ? aiStockPeopleRole(slot.alt, (_input_sectionNames_slot_sectionIndex = input.sectionNames[slot.sectionIndex]) != null ? _input_sectionNames_slot_sectionIndex : '') : null;
|
|
4016
|
+
return _extends({}, judgementOf(slot, request, subject), refuse.length ? {
|
|
4017
|
+
refuse: [
|
|
4018
|
+
...new Set(refuse)
|
|
4019
|
+
]
|
|
4020
|
+
} : {}, slot.people ? _extends({
|
|
4021
|
+
needs: ((_request_needs = request.needs) == null ? void 0 : _request_needs.length) ? request.needs : (role == null ? void 0 : role.required) ? role.kin : ADULT_PEOPLE_STEMS,
|
|
4022
|
+
refuseLead: [
|
|
4023
|
+
...new Set([
|
|
4024
|
+
...CHILD_STEMS,
|
|
4025
|
+
...(_request_refuseLead = request.refuseLead) != null ? _request_refuseLead : []
|
|
4026
|
+
])
|
|
4027
|
+
]
|
|
4028
|
+
}, role ? {
|
|
4029
|
+
roles: role.kin
|
|
4030
|
+
} : {}, (role == null ? void 0 : (_role_scene = role.scene) == null ? void 0 : _role_scene.length) ? {
|
|
4031
|
+
roleScene: role.scene
|
|
4032
|
+
} : {}, (role == null ? void 0 : (_role_refuse = role.refuse) == null ? void 0 : _role_refuse.length) ? {
|
|
4033
|
+
roleRefuse: aiStockRoleRefusals(role, slot.alt)
|
|
4034
|
+
} : {}) : {});
|
|
4035
|
+
};
|
|
4036
|
+
/** A search's own judgement, as its words set it. */ const judgementOf = (slot, request, subject)=>{
|
|
2418
4037
|
var _request_subject, _request_shun;
|
|
2419
|
-
var _request_shun1, _request_leadAny;
|
|
4038
|
+
var _request_allow, _request_shun1, _request_leadAny, _request_refuse, _request_needs, _request_refuseLead;
|
|
2420
4039
|
return _extends({
|
|
2421
4040
|
subject: (_request_subject = request.subject) != null ? _request_subject : request.broad ? '' : subject,
|
|
2422
4041
|
domain: terms.domain,
|
|
2423
|
-
strict: slot.role === 'gallery' && !request.broad || !!request.requires,
|
|
2424
|
-
named:
|
|
4042
|
+
strict: slot.role === 'gallery' && !request.broad || !!request.requires || !!request.strict,
|
|
4043
|
+
named: ((_request_allow = request.allow) == null ? void 0 : _request_allow.length) ? [
|
|
4044
|
+
...new Set([
|
|
4045
|
+
...terms.named,
|
|
4046
|
+
...request.allow
|
|
4047
|
+
])
|
|
4048
|
+
] : terms.named,
|
|
2425
4049
|
rivals: terms.rivals
|
|
2426
4050
|
}, terms.shun.length || ((_request_shun1 = request.shun) == null ? void 0 : _request_shun1.length) ? {
|
|
2427
4051
|
shun: [
|
|
@@ -2450,6 +4074,15 @@ const EXTENSIONS = {
|
|
|
2450
4074
|
leadWorld: true
|
|
2451
4075
|
} : {}, request.first ? {
|
|
2452
4076
|
first: true
|
|
4077
|
+
} : {}, ((_request_refuse = request.refuse) == null ? void 0 : _request_refuse.length) ? {
|
|
4078
|
+
refuse: request.refuse
|
|
4079
|
+
} : {}, ((_request_needs = request.needs) == null ? void 0 : _request_needs.length) ? {
|
|
4080
|
+
needs: request.needs
|
|
4081
|
+
} : {}, ((_request_refuseLead = request.refuseLead) == null ? void 0 : _request_refuseLead.length) ? {
|
|
4082
|
+
refuseLead: request.refuseLead
|
|
4083
|
+
} : {}, request.worldless ? {
|
|
4084
|
+
domain: [],
|
|
4085
|
+
rivals: []
|
|
2453
4086
|
} : {});
|
|
2454
4087
|
};
|
|
2455
4088
|
/**
|
|
@@ -2457,7 +4090,8 @@ const EXTENSIONS = {
|
|
|
2457
4090
|
* filled from (AGL-3660): its broad searches, any hit naming the craft
|
|
2458
4091
|
* itself. Never a desk, a laptop or a living room for a soap shop.
|
|
2459
4092
|
*/ const craftSearches = (slot)=>{
|
|
2460
|
-
|
|
4093
|
+
// A portfolio's record of the work is never filled by the craft's own photos (AGL-3856).
|
|
4094
|
+
if (!terms.core.length || aiStockShowsTheWork(slot, terms)) return [];
|
|
2461
4095
|
const orientation = aiStockOrientation(slot.aspect);
|
|
2462
4096
|
const size = slot.role === 'hero' ? {
|
|
2463
4097
|
minWidth: 1600
|
|
@@ -2495,7 +4129,7 @@ const EXTENSIONS = {
|
|
|
2495
4129
|
...slot.product ? aiStockProductSearches(slot, terms.craft, terms.domain, {
|
|
2496
4130
|
broad: terms.broad,
|
|
2497
4131
|
core: terms.core
|
|
2498
|
-
}) : aiStockSearchesFor(slot, terms, subject, aiStockSlotPart(slot, sectionName)),
|
|
4132
|
+
}) : aiStockSearchesFor(slot, terms, subject, aiStockSlotPart(slot, sectionName), sectionName),
|
|
2499
4133
|
...craftSearches(slot)
|
|
2500
4134
|
];
|
|
2501
4135
|
/** The slot's accepted hits for a search; `null` once the page's searches are spent. */ const accepted = async (request, repeat)=>{
|
|
@@ -2509,7 +4143,10 @@ const EXTENSIONS = {
|
|
|
2509
4143
|
if (elsewhere(key)) blocked.add(key);
|
|
2510
4144
|
}
|
|
2511
4145
|
}
|
|
2512
|
-
|
|
4146
|
+
const judgement = judgementFor(slot, request, subject);
|
|
4147
|
+
const ranked = aiStockRank(found, blocked, `${input.seed}:${index}:${request.query}`, judgement);
|
|
4148
|
+
// A team's portraits are of different people where the hits allow (AGL-3856).
|
|
4149
|
+
return slot.people && !repeat ? aiStockFreshShootsFirst(ranked, shown, judgement) : ranked;
|
|
2513
4150
|
};
|
|
2514
4151
|
/** The first of these searches with a hit to take: a new one, or one the job already shows. */ const choose = async (tried, repeat)=>{
|
|
2515
4152
|
for (const request of tried){
|
|
@@ -2517,7 +4154,10 @@ const EXTENSIONS = {
|
|
|
2517
4154
|
// Spent searches answer nothing; those already asked still answer from memory.
|
|
2518
4155
|
const ranked = await accepted(request, repeat);
|
|
2519
4156
|
if (!(ranked == null ? void 0 : ranked.length)) continue;
|
|
2520
|
-
if (!repeat)
|
|
4157
|
+
if (!repeat) {
|
|
4158
|
+
claimed.add(stockPhotoSourceKey(ranked[0]));
|
|
4159
|
+
shown.push(ranked[0]);
|
|
4160
|
+
}
|
|
2521
4161
|
return _extends({
|
|
2522
4162
|
candidates: ranked.slice(0, AI_LAYOUT_STOCK_TRIES_PER_SLOT),
|
|
2523
4163
|
query: request.query
|