@playpilot/tpi 2.0.0 → 2.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/link-injections.js +5 -5
- package/jsconfig.json +19 -19
- package/package.json +1 -1
- package/src/lib/api.js +1 -2
- package/src/lib/data/translations.js +59 -1
- package/src/lib/enums/Language.js +1 -0
- package/src/lib/index.js +1 -1
- package/src/lib/linkInjection.js +7 -5
- package/src/lib/text.js +9 -0
- package/src/routes/+layout.svelte +2 -1
- package/src/routes/+page.svelte +4 -1
- package/src/routes/components/Title.svelte +8 -2
- package/src/tests/lib/api.test.js +0 -1
- package/src/tests/lib/linkInjection.test.js +66 -0
- package/src/tests/lib/text.test.js +15 -1
- package/src/tests/routes/+page.test.js +3 -3
- package/src/tests/routes/components/Title.test.js +6 -0
package/jsconfig.json
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
{
|
|
2
|
-
"extends": "./.svelte-kit/tsconfig.json",
|
|
3
|
-
"compilerOptions": {
|
|
4
|
-
"allowJs": true,
|
|
5
|
-
"checkJs": true,
|
|
6
|
-
"esModuleInterop": true,
|
|
7
|
-
"forceConsistentCasingInFileNames": true,
|
|
8
|
-
"resolveJsonModule": true,
|
|
9
|
-
"skipLibCheck": true,
|
|
10
|
-
"sourceMap": true,
|
|
11
|
-
"strict": true,
|
|
12
|
-
"moduleResolution": "bundler"
|
|
13
|
-
}
|
|
14
|
-
// Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias
|
|
15
|
-
// except $lib which is handled by https://svelte.dev/docs/kit/configuration#files
|
|
16
|
-
//
|
|
17
|
-
// If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes
|
|
18
|
-
// from the referenced tsconfig.json - TypeScript does not merge them in
|
|
19
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"extends": "./.svelte-kit/tsconfig.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"allowJs": true,
|
|
5
|
+
"checkJs": true,
|
|
6
|
+
"esModuleInterop": true,
|
|
7
|
+
"forceConsistentCasingInFileNames": true,
|
|
8
|
+
"resolveJsonModule": true,
|
|
9
|
+
"skipLibCheck": true,
|
|
10
|
+
"sourceMap": true,
|
|
11
|
+
"strict": true,
|
|
12
|
+
"moduleResolution": "bundler"
|
|
13
|
+
}
|
|
14
|
+
// Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias
|
|
15
|
+
// except $lib which is handled by https://svelte.dev/docs/kit/configuration#files
|
|
16
|
+
//
|
|
17
|
+
// If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes
|
|
18
|
+
// from the referenced tsconfig.json - TypeScript does not merge them in
|
|
19
|
+
}
|
package/package.json
CHANGED
package/src/lib/api.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { authorize, getAuthToken, isEditorialModeEnabled } from './auth'
|
|
2
2
|
import { apiBaseUrl } from './constants'
|
|
3
|
-
import { linkInjections } from './fakeData'
|
|
4
3
|
import { stringToHash } from './hash'
|
|
5
4
|
import { getPageMetaData } from './meta'
|
|
6
5
|
import { getFullUrlPath } from './url'
|
|
@@ -81,7 +80,7 @@ export async function pollLinkInjections(url, html, { requireCompletedResult = f
|
|
|
81
80
|
currentTry++
|
|
82
81
|
|
|
83
82
|
if (currentTry >= maxTries) {
|
|
84
|
-
console.error('Max poll limit exceeded. Please return later.')
|
|
83
|
+
if (maxTries > 1) console.error('Max poll limit exceeded. Please return later.')
|
|
85
84
|
resolve()
|
|
86
85
|
return
|
|
87
86
|
}
|
|
@@ -4,235 +4,293 @@ export const translations = {
|
|
|
4
4
|
'Where To Stream Online': {
|
|
5
5
|
[Language.English]: 'Where to stream online',
|
|
6
6
|
[Language.Swedish]: 'Var kan man streama online',
|
|
7
|
+
[Language.Danish]: 'Hvor kan man streame online',
|
|
7
8
|
},
|
|
8
9
|
'Affiliate Disclaimer': {
|
|
9
10
|
[Language.English]: 'We may earn a commission if you make a purchase through these links. In collaboration with',
|
|
10
11
|
[Language.Swedish]: 'Vi kan få provision om du gör ett köp via dessa länkar. I samarbete med',
|
|
12
|
+
[Language.Danish]: 'Vi kan modtage en kommission, hvis du foretager et køb via disse links. I samarbejde med',
|
|
11
13
|
},
|
|
12
14
|
'An Error Occurred': {
|
|
13
15
|
[Language.English]: 'An error occurred',
|
|
14
16
|
[Language.Swedish]: 'Ett fel inträffade',
|
|
17
|
+
[Language.Danish]: 'Der opstod en fejl',
|
|
15
18
|
},
|
|
16
19
|
'Title Unavailable': {
|
|
17
20
|
[Language.English]: 'This title is not currently available to stream.',
|
|
18
21
|
[Language.Swedish]: 'Den här titeln går inte att streama just nu.',
|
|
22
|
+
[Language.Danish]: 'Denne titel er i øjeblikket ikke tilgængelig for streaming.',
|
|
19
23
|
},
|
|
20
24
|
'Title Unavailable Suffix': {
|
|
21
25
|
[Language.English]: 'is not currently available to stream.',
|
|
22
26
|
[Language.Swedish]: 'går inte att streama just nu.',
|
|
27
|
+
[Language.Danish]: 'er i øjeblikket ikke tilgængelig for streaming.',
|
|
23
28
|
},
|
|
24
29
|
'Is Available To Stream On': {
|
|
25
30
|
[Language.English]: 'is available to stream on',
|
|
26
31
|
[Language.Swedish]: 'finns att streama på',
|
|
32
|
+
[Language.Danish]: 'kan streames på',
|
|
27
33
|
},
|
|
28
34
|
'Is Available To Stream': {
|
|
29
35
|
[Language.English]: 'is available to stream.',
|
|
30
36
|
[Language.Swedish]: 'finns att streama.',
|
|
37
|
+
[Language.Danish]: 'kan streames.',
|
|
31
38
|
},
|
|
32
39
|
'View Streaming Options': {
|
|
33
40
|
[Language.English]: 'View streaming options',
|
|
34
41
|
[Language.Swedish]: 'Se streamingalternativ',
|
|
42
|
+
[Language.Danish]: 'Se streamingmuligheder',
|
|
35
43
|
},
|
|
36
44
|
'And': {
|
|
37
45
|
[Language.English]: 'and',
|
|
38
46
|
[Language.Swedish]: 'och',
|
|
47
|
+
[Language.Danish]: 'og',
|
|
39
48
|
},
|
|
40
49
|
'Minutes': {
|
|
41
50
|
[Language.English]: 'minutes',
|
|
42
51
|
[Language.Swedish]: 'minuter',
|
|
52
|
+
[Language.Danish]: 'minutter',
|
|
43
53
|
},
|
|
44
54
|
'Type: movie': {
|
|
45
55
|
[Language.English]: 'Movie',
|
|
46
56
|
[Language.Swedish]: 'Film',
|
|
57
|
+
[Language.Danish]: 'Film',
|
|
47
58
|
},
|
|
48
59
|
'Type: series': {
|
|
49
60
|
[Language.English]: 'Series',
|
|
50
61
|
[Language.Swedish]: 'Serie',
|
|
62
|
+
[Language.Danish]: 'Serie',
|
|
51
63
|
},
|
|
52
64
|
'Stream': {
|
|
53
65
|
[Language.English]: 'Stream',
|
|
54
|
-
[Language.Swedish]: '
|
|
66
|
+
[Language.Swedish]: 'Streama',
|
|
67
|
+
[Language.Danish]: 'Stream',
|
|
55
68
|
},
|
|
56
69
|
'Buy': {
|
|
57
70
|
[Language.English]: 'Buy',
|
|
58
71
|
[Language.Swedish]: 'Köp',
|
|
72
|
+
[Language.Danish]: 'Køb',
|
|
59
73
|
},
|
|
60
74
|
'Rent': {
|
|
61
75
|
[Language.English]: 'Rent',
|
|
62
76
|
[Language.Swedish]: 'Hyra',
|
|
77
|
+
[Language.Danish]: 'Lej',
|
|
63
78
|
},
|
|
64
79
|
'Rent Or Buy': {
|
|
65
80
|
[Language.English]: 'Rent or Buy',
|
|
66
81
|
[Language.Swedish]: 'Hyra',
|
|
82
|
+
[Language.Danish]: 'Lej eller køb',
|
|
67
83
|
},
|
|
68
84
|
|
|
69
85
|
// Genres
|
|
70
86
|
'All': {
|
|
71
87
|
[Language.English]: 'All',
|
|
72
88
|
[Language.Swedish]: 'Alla',
|
|
89
|
+
[Language.Danish]: 'Alle',
|
|
73
90
|
},
|
|
74
91
|
'Unscripted': {
|
|
75
92
|
[Language.English]: 'Unscripted',
|
|
76
93
|
[Language.Swedish]: 'Oscripterat',
|
|
94
|
+
[Language.Danish]: 'Umanuskriptet',
|
|
77
95
|
},
|
|
78
96
|
'Independent': {
|
|
79
97
|
[Language.English]: 'Independent',
|
|
80
98
|
[Language.Swedish]: 'Indie',
|
|
99
|
+
[Language.Danish]: 'Indie',
|
|
81
100
|
},
|
|
82
101
|
'Game Show': {
|
|
83
102
|
[Language.English]: 'Game Show',
|
|
84
103
|
[Language.Swedish]: 'Gameshow',
|
|
104
|
+
[Language.Danish]: 'Quizshow',
|
|
85
105
|
},
|
|
86
106
|
'Film Noir': {
|
|
87
107
|
[Language.English]: 'Film Noir',
|
|
88
108
|
[Language.Swedish]: 'Film noir',
|
|
109
|
+
[Language.Danish]: 'Film noir',
|
|
89
110
|
},
|
|
90
111
|
'Entertainment': {
|
|
91
112
|
[Language.English]: 'Entertainment',
|
|
92
113
|
[Language.Swedish]: 'Underhållning',
|
|
114
|
+
[Language.Danish]: 'Underholdning',
|
|
93
115
|
},
|
|
94
116
|
'News': {
|
|
95
117
|
[Language.English]: 'News',
|
|
96
118
|
[Language.Swedish]: 'Nyheter',
|
|
119
|
+
[Language.Danish]: 'Nyheder',
|
|
97
120
|
},
|
|
98
121
|
'Short': {
|
|
99
122
|
[Language.English]: 'Short',
|
|
100
123
|
[Language.Swedish]: 'Kortfilm',
|
|
124
|
+
[Language.Danish]: 'Kortfilm',
|
|
101
125
|
},
|
|
102
126
|
'Bollywood': {
|
|
103
127
|
[Language.English]: 'Bollywood',
|
|
104
128
|
[Language.Swedish]: 'Bollywood',
|
|
129
|
+
[Language.Danish]: 'Bollywood',
|
|
105
130
|
},
|
|
106
131
|
'Concert': {
|
|
107
132
|
[Language.English]: 'Concert',
|
|
108
133
|
[Language.Swedish]: 'Konsert',
|
|
134
|
+
[Language.Danish]: 'Koncert',
|
|
109
135
|
},
|
|
110
136
|
'Arthouse': {
|
|
111
137
|
[Language.English]: 'Arthouse',
|
|
112
138
|
[Language.Swedish]: 'Arthouse',
|
|
139
|
+
[Language.Danish]: 'Arthouse',
|
|
113
140
|
},
|
|
114
141
|
'Alternate Version': {
|
|
115
142
|
[Language.English]: 'Alternate Version',
|
|
116
143
|
[Language.Swedish]: 'Alternativ version',
|
|
144
|
+
[Language.Danish]: 'Alternativ version',
|
|
117
145
|
},
|
|
118
146
|
'Reality TV': {
|
|
119
147
|
[Language.English]: 'Reality TV',
|
|
120
148
|
[Language.Swedish]: 'Reality-TV',
|
|
149
|
+
[Language.Danish]: 'Reality-TV',
|
|
121
150
|
},
|
|
122
151
|
'Culture': {
|
|
123
152
|
[Language.English]: 'Culture',
|
|
124
153
|
[Language.Swedish]: 'Kultur',
|
|
154
|
+
[Language.Danish]: 'Kultur',
|
|
125
155
|
},
|
|
126
156
|
'Drama': {
|
|
127
157
|
[Language.English]: 'Drama',
|
|
128
158
|
[Language.Swedish]: 'Drama',
|
|
159
|
+
[Language.Danish]: 'Drama',
|
|
129
160
|
},
|
|
130
161
|
'Crime': {
|
|
131
162
|
[Language.English]: 'Crime',
|
|
132
163
|
[Language.Swedish]: 'Kriminal',
|
|
164
|
+
[Language.Danish]: 'Krimi',
|
|
133
165
|
},
|
|
134
166
|
'Western': {
|
|
135
167
|
[Language.English]: 'Western',
|
|
136
168
|
[Language.Swedish]: 'Western',
|
|
169
|
+
[Language.Danish]: 'Western',
|
|
137
170
|
},
|
|
138
171
|
'Thriller': {
|
|
139
172
|
[Language.English]: 'Thriller',
|
|
140
173
|
[Language.Swedish]: 'Thriller',
|
|
174
|
+
[Language.Danish]: 'Thriller',
|
|
141
175
|
},
|
|
142
176
|
'Animation': {
|
|
143
177
|
[Language.English]: 'Animation',
|
|
144
178
|
[Language.Swedish]: 'Animerat',
|
|
179
|
+
[Language.Danish]: 'Animation',
|
|
145
180
|
},
|
|
146
181
|
'Mystery': {
|
|
147
182
|
[Language.English]: 'Mystery',
|
|
148
183
|
[Language.Swedish]: 'Mysterium',
|
|
184
|
+
[Language.Danish]: 'Mysterium',
|
|
149
185
|
},
|
|
150
186
|
'Science': {
|
|
151
187
|
[Language.English]: 'Science',
|
|
152
188
|
[Language.Swedish]: 'Vetenskap',
|
|
189
|
+
[Language.Danish]: 'Videnskab',
|
|
153
190
|
},
|
|
154
191
|
'Nature': {
|
|
155
192
|
[Language.English]: 'Nature',
|
|
156
193
|
[Language.Swedish]: 'Natur',
|
|
194
|
+
[Language.Danish]: 'Natur',
|
|
157
195
|
},
|
|
158
196
|
'War': {
|
|
159
197
|
[Language.English]: 'War',
|
|
160
198
|
[Language.Swedish]: 'Krig',
|
|
199
|
+
[Language.Danish]: 'Krig',
|
|
161
200
|
},
|
|
162
201
|
'Sci-Fi': {
|
|
163
202
|
[Language.English]: 'Sci-Fi',
|
|
164
203
|
[Language.Swedish]: 'Sci-fi',
|
|
204
|
+
[Language.Danish]: 'Sci-fi',
|
|
165
205
|
},
|
|
166
206
|
'Documentary': {
|
|
167
207
|
[Language.English]: 'Documentary',
|
|
168
208
|
[Language.Swedish]: 'Dokumentär',
|
|
209
|
+
[Language.Danish]: 'Dokumentar',
|
|
169
210
|
},
|
|
170
211
|
'Stand-up': {
|
|
171
212
|
[Language.English]: 'Stand-up',
|
|
172
213
|
[Language.Swedish]: 'Standup',
|
|
214
|
+
[Language.Danish]: 'Stand-up',
|
|
173
215
|
},
|
|
174
216
|
'Talk Show': {
|
|
175
217
|
[Language.English]: 'Talk Show',
|
|
176
218
|
[Language.Swedish]: 'Talkshow',
|
|
219
|
+
[Language.Danish]: 'Talkshow',
|
|
177
220
|
},
|
|
178
221
|
'Fantasy': {
|
|
179
222
|
[Language.English]: 'Fantasy',
|
|
180
223
|
[Language.Swedish]: 'Fantasy',
|
|
224
|
+
[Language.Danish]: 'Fantasy',
|
|
181
225
|
},
|
|
182
226
|
'History': {
|
|
183
227
|
[Language.English]: 'History',
|
|
184
228
|
[Language.Swedish]: 'Historia',
|
|
229
|
+
[Language.Danish]: 'Historie',
|
|
185
230
|
},
|
|
186
231
|
'Adventure': {
|
|
187
232
|
[Language.English]: 'Adventure',
|
|
188
233
|
[Language.Swedish]: 'Äventyr',
|
|
234
|
+
[Language.Danish]: 'Eventyr',
|
|
189
235
|
},
|
|
190
236
|
'Action': {
|
|
191
237
|
[Language.English]: 'Action',
|
|
192
238
|
[Language.Swedish]: 'Action',
|
|
239
|
+
[Language.Danish]: 'Action',
|
|
193
240
|
},
|
|
194
241
|
'Horror': {
|
|
195
242
|
[Language.English]: 'Horror',
|
|
196
243
|
[Language.Swedish]: 'Skräck',
|
|
244
|
+
[Language.Danish]: 'Gyser',
|
|
197
245
|
},
|
|
198
246
|
'Comedy': {
|
|
199
247
|
[Language.English]: 'Comedy',
|
|
200
248
|
[Language.Swedish]: 'Komedi',
|
|
249
|
+
[Language.Danish]: 'Komedie',
|
|
201
250
|
},
|
|
202
251
|
'Biography': {
|
|
203
252
|
[Language.English]: 'Biography',
|
|
204
253
|
[Language.Swedish]: 'Biografi',
|
|
254
|
+
[Language.Danish]: 'Biografi',
|
|
205
255
|
},
|
|
206
256
|
'Music': {
|
|
207
257
|
[Language.English]: 'Music',
|
|
208
258
|
[Language.Swedish]: 'Musik',
|
|
259
|
+
[Language.Danish]: 'Musik',
|
|
209
260
|
},
|
|
210
261
|
'Sport': {
|
|
211
262
|
[Language.English]: 'Sport',
|
|
212
263
|
[Language.Swedish]: 'Sport',
|
|
264
|
+
[Language.Danish]: 'Sport',
|
|
213
265
|
},
|
|
214
266
|
'Romance': {
|
|
215
267
|
[Language.English]: 'Romance',
|
|
216
268
|
[Language.Swedish]: 'Romantik',
|
|
269
|
+
[Language.Danish]: 'Romantik',
|
|
217
270
|
},
|
|
218
271
|
'Kids': {
|
|
219
272
|
[Language.English]: 'Kids',
|
|
220
273
|
[Language.Swedish]: 'Barn',
|
|
274
|
+
[Language.Danish]: 'Børn',
|
|
221
275
|
},
|
|
222
276
|
'Lifestyle': {
|
|
223
277
|
[Language.English]: 'Lifestyle',
|
|
224
278
|
[Language.Swedish]: 'Livsstil',
|
|
279
|
+
[Language.Danish]: 'Livsstil',
|
|
225
280
|
},
|
|
226
281
|
'Musical': {
|
|
227
282
|
[Language.English]: 'Musical',
|
|
228
283
|
[Language.Swedish]: 'Musikal',
|
|
284
|
+
[Language.Danish]: 'Musical',
|
|
229
285
|
},
|
|
230
286
|
'Anime': {
|
|
231
287
|
[Language.English]: 'Anime',
|
|
232
288
|
[Language.Swedish]: 'Anime',
|
|
289
|
+
[Language.Danish]: 'Anime',
|
|
233
290
|
},
|
|
234
291
|
'Family': {
|
|
235
292
|
[Language.English]: 'Family',
|
|
236
293
|
[Language.Swedish]: 'Familj',
|
|
294
|
+
[Language.Danish]: 'Familie',
|
|
237
295
|
},
|
|
238
296
|
}
|
package/src/lib/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
// place files you want to import through the `$lib` alias in this folder.
|
|
1
|
+
// place files you want to import through the `$lib` alias in this folder.
|
package/src/lib/linkInjection.js
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { mount, unmount } from 'svelte'
|
|
2
2
|
import TitlePopover from '../routes/components/TitlePopover.svelte'
|
|
3
3
|
import AfterArticlePlaylinks from '../routes/components/AfterArticlePlaylinks.svelte'
|
|
4
|
-
import { findTextNodeContaining, isNodeInLink, replaceStartingFrom } from './text'
|
|
4
|
+
import { cleanPhrase, findTextNodeContaining, isNodeInLink, replaceStartingFrom } from './text'
|
|
5
5
|
import { getLargestValueInArray } from './array'
|
|
6
|
-
import { decodeHtmlEntities } from './html'
|
|
7
6
|
|
|
8
7
|
const keyDataAttribute = 'data-playpilot-injection-key'
|
|
9
8
|
const keySelector = `[${keyDataAttribute}]`
|
|
@@ -88,14 +87,14 @@ export function injectLinksInDocument(elements, onclick, injections = { aiInject
|
|
|
88
87
|
|
|
89
88
|
const validInjections = filterInvalidInTextInjections(mergedInjections)
|
|
90
89
|
const foundInjections = validInjections.filter(i => {
|
|
91
|
-
return
|
|
90
|
+
return cleanPhrase(fullText).includes(cleanPhrase(i.sentence))
|
|
92
91
|
})
|
|
93
92
|
|
|
94
93
|
/** @type {LinkInjectionRanges} */
|
|
95
94
|
const ranges = {}
|
|
96
95
|
|
|
97
96
|
for (const injection of foundInjections) {
|
|
98
|
-
const elementIndex = elements.findIndex(element => element.innerText.includes(
|
|
97
|
+
const elementIndex = elements.findIndex(element => cleanPhrase(element.innerText).includes(cleanPhrase(injection.sentence)))
|
|
99
98
|
const element = elements[elementIndex]
|
|
100
99
|
|
|
101
100
|
if (!element) continue
|
|
@@ -243,6 +242,9 @@ function openLinkModal(event, injection, onclick) {
|
|
|
243
242
|
* @param {LinkInjection} injection
|
|
244
243
|
*/
|
|
245
244
|
function openLinkPopover(event, injection) {
|
|
245
|
+
// Popover for this link was already open and was called again... for some reason
|
|
246
|
+
if (activePopovers[injection.key]) return
|
|
247
|
+
|
|
246
248
|
// Skip touch devices
|
|
247
249
|
if (window.matchMedia('(pointer: coarse)').matches) return
|
|
248
250
|
|
|
@@ -416,5 +418,5 @@ export function isAvailableAsManualInjection(injection, injectionIndex, injectio
|
|
|
416
418
|
* @returns {boolean}
|
|
417
419
|
*/
|
|
418
420
|
export function isEquivalentInjection(injection1, injection2) {
|
|
419
|
-
return injection1.title === injection2.title && injection1.sentence === injection2.sentence
|
|
421
|
+
return injection1.title === injection2.title && cleanPhrase(injection1.sentence) === cleanPhrase(injection2.sentence)
|
|
420
422
|
}
|
package/src/lib/text.js
CHANGED
|
@@ -58,3 +58,12 @@ export function replaceStartingFrom(text, search, replacement, startIndex) {
|
|
|
58
58
|
|
|
59
59
|
return before + updatedAfter
|
|
60
60
|
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Returns a string for most consistent string comparisons, decoding Html symbols and removing spaces.
|
|
64
|
+
* @param {string} phrase
|
|
65
|
+
* @returns {string}
|
|
66
|
+
*/
|
|
67
|
+
export function cleanPhrase(phrase) {
|
|
68
|
+
return decodeHtmlEntities(phrase).toLowerCase().replace(/\s+/g, '')
|
|
69
|
+
}
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
if (browser) window.PlayPilotLinkInjections = { token: 'ZoAL14yqzevMyQiwckbvyetOkeIUeEDN', selector: 'article' }
|
|
20
20
|
</script>
|
|
21
21
|
|
|
22
|
-
<meta property="article:modified_time" content="2025-05-
|
|
22
|
+
<meta property="article:modified_time" content="2025-05-16T20:00:00+00:00" />
|
|
23
23
|
|
|
24
24
|
<div>
|
|
25
25
|
{#key Math.random()}
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
<time datetime="14:00">1 hour ago</time>
|
|
29
29
|
<p use:noClass>Following the success of John M. Chu's 2018 romantic-comedy Crazy Rich Asians, Quan was inspired to return to acting. He first scored a supporting role in the Netflix movie Finding 'Ohana, before securing a starring role in the absurdist comedy-drama Everything Everywhere all At Once. A critical and commercial success, the film earned $143 million against a budget of $14-25 million, and saw Quan win the Academy Award for Best Supporting Actor. Following his win, Quan struggled to choose projects he was satisfied with, passing on an action-comedy three times, before finally taking his first leading role in it, following advice from Spielberg.</p>
|
|
30
30
|
<p use:noClass>In an interview with Epire & Magazine, Quan reveals he quested starring in Love Hurts, which sees him in the leading role of a former assassin turned successful realtor, whose past returns when his brother attempts to hunt him down. The movie is in a similar vein to successful films such as The Long Kiss Goodnight and Nobody, and Quan discussed how he was reluctant to take the part due to his conditioned beliefs about how an action hero should look. But he reveals that he changed his mind following a meeting with Spielberg, who convinced him to do it.</p>
|
|
31
|
+
<p use:noClass><strong use:noClass>Jason Momoa</strong> (”Aquaman”), <strong use:noClass>Jack Black</strong> (”Nacho Libre”) och <strong use:noClass>Jennifer Coolidge</strong> (”The White Lotus”) medverkar i den <strong use:noClass>Jared Hess</strong>-regisserade (”Napolen Dynamite”) filmen. Filmen följer fyra utbölingar som via en magisk portal sugs in i en värld där allt är kubformat. För att komma hem igen måste de övervinna den färgstarka världen.</p>
|
|
31
32
|
</article>
|
|
32
33
|
|
|
33
34
|
{#if browser}
|
package/src/routes/+page.svelte
CHANGED
|
@@ -44,7 +44,10 @@
|
|
|
44
44
|
async function initialize() {
|
|
45
45
|
if (isEditorialMode) authorized = await authorize()
|
|
46
46
|
|
|
47
|
-
|
|
47
|
+
// Only trying once when not in editorial mode to prevent late injections (as well as a ton of requests)
|
|
48
|
+
// by users who are not in the editorial view.
|
|
49
|
+
// [TODO] TEMP: Only try once for editorial as well
|
|
50
|
+
response = await pollLinkInjections(getFullUrlPath(), htmlString, { maxTries: 1 })
|
|
48
51
|
|
|
49
52
|
inject({ aiInjections, manualInjections })
|
|
50
53
|
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
</div>
|
|
19
19
|
{/if}
|
|
20
20
|
|
|
21
|
-
<h1>{title.title}</h1>
|
|
21
|
+
<h1 class:truncate={small}>{title.title}</h1>
|
|
22
22
|
|
|
23
23
|
<div class="info">
|
|
24
24
|
<div class="imdb">
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
<div>{title.year}</div>
|
|
32
32
|
<div class="capitalize">{t(`Type: ${title.type}`)}</div>
|
|
33
33
|
|
|
34
|
-
{#if title.length}
|
|
34
|
+
{#if !small && title.length}
|
|
35
35
|
<div>{title.length} {t('Minutes')}</div>
|
|
36
36
|
{/if}
|
|
37
37
|
</div>
|
|
@@ -183,4 +183,10 @@
|
|
|
183
183
|
.capitalize {
|
|
184
184
|
text-transform: capitalize;
|
|
185
185
|
}
|
|
186
|
+
|
|
187
|
+
.truncate {
|
|
188
|
+
overflow: hidden;
|
|
189
|
+
text-overflow: ellipsis;
|
|
190
|
+
white-space: nowrap;
|
|
191
|
+
}
|
|
186
192
|
</style>
|
|
@@ -2,7 +2,6 @@ import { describe, it, expect, vi, afterEach, beforeEach } from 'vitest'
|
|
|
2
2
|
import { fetchLinkInjections, pollLinkInjections } from '$lib/api'
|
|
3
3
|
import { fakeFetch } from '../helpers'
|
|
4
4
|
import { authorize, isEditorialModeEnabled } from '$lib/auth'
|
|
5
|
-
import { linkInjections } from '$lib/fakeData'
|
|
6
5
|
|
|
7
6
|
vi.mock('$lib/auth', async importActual => {
|
|
8
7
|
const actual = await importActual()
|
|
@@ -324,6 +324,24 @@ describe('linkInjection.js', () => {
|
|
|
324
324
|
expect(unmount).toHaveBeenCalled()
|
|
325
325
|
})
|
|
326
326
|
|
|
327
|
+
it('Should mount popover component only once when the same popover is already open', async () => {
|
|
328
|
+
document.body.innerHTML = '<p>This is a sentence with an injection.</p>'
|
|
329
|
+
|
|
330
|
+
const elements = Array.from(document.body.querySelectorAll('p'))
|
|
331
|
+
const injection = generateInjection('This is a sentence with an injection.', 'an injection')
|
|
332
|
+
|
|
333
|
+
const mock = vi.fn()
|
|
334
|
+
injectLinksInDocument(elements, mock, { aiInjections: [injection], manualInjections: [] })
|
|
335
|
+
|
|
336
|
+
const link = /** @type {HTMLAnchorElement} */ (document.querySelector('[data-playpilot-injection-key]'))
|
|
337
|
+
|
|
338
|
+
await fireEvent.mouseEnter(link)
|
|
339
|
+
expect(mount).toHaveBeenCalled()
|
|
340
|
+
|
|
341
|
+
await fireEvent.mouseEnter(link)
|
|
342
|
+
expect(mount).toHaveBeenCalledTimes(1)
|
|
343
|
+
})
|
|
344
|
+
|
|
327
345
|
it('Should inject links of the same phrase when multiple are present', () => {
|
|
328
346
|
document.body.innerHTML = '<p>This is a sentence with an injection and another injection</p>'
|
|
329
347
|
|
|
@@ -389,6 +407,54 @@ describe('linkInjection.js', () => {
|
|
|
389
407
|
expect(mount).not.toHaveBeenCalled()
|
|
390
408
|
expect(document.querySelector('[data-playpilot-after-article-playlinks]')).not.toBeTruthy()
|
|
391
409
|
})
|
|
410
|
+
|
|
411
|
+
it('Should treat sentences the same regardless of differences spaces', () => {
|
|
412
|
+
document.body.innerHTML = `<section>
|
|
413
|
+
<div>Some text</div>
|
|
414
|
+
<div>Someothertext</div>
|
|
415
|
+
<div>Some thirdtext</div>
|
|
416
|
+
<div>Some fourth text </div>
|
|
417
|
+
</section>
|
|
418
|
+
`
|
|
419
|
+
|
|
420
|
+
const elements = Array.from(document.body.querySelectorAll('div'))
|
|
421
|
+
const injections = [
|
|
422
|
+
generateInjection('Sometext', 'Some'),
|
|
423
|
+
generateInjection('Some other text ', 'Some'),
|
|
424
|
+
generateInjection('Somethird text', 'Some'),
|
|
425
|
+
generateInjection('Some fourthtext', 'Some'),
|
|
426
|
+
]
|
|
427
|
+
|
|
428
|
+
const mock = vi.fn()
|
|
429
|
+
injectLinksInDocument(elements, mock, { aiInjections: [], manualInjections: injections })
|
|
430
|
+
|
|
431
|
+
console.log(document.body.innerHTML)
|
|
432
|
+
|
|
433
|
+
expect(document.querySelectorAll('a')).toHaveLength(4)
|
|
434
|
+
})
|
|
435
|
+
|
|
436
|
+
it('Should treat sentences the same regardless of casings', () => {
|
|
437
|
+
document.body.innerHTML = `<section>
|
|
438
|
+
<div>Some text</div>
|
|
439
|
+
<div>Some OTHER TEXT</div>
|
|
440
|
+
<div>Some ThIrD tExT</div>
|
|
441
|
+
</section>
|
|
442
|
+
`
|
|
443
|
+
|
|
444
|
+
const elements = Array.from(document.body.querySelectorAll('div'))
|
|
445
|
+
const injections = [
|
|
446
|
+
generateInjection('Some text', 'Some'),
|
|
447
|
+
generateInjection('Some OTHER Text ', 'Some'),
|
|
448
|
+
generateInjection('Somethird text', 'Some'),
|
|
449
|
+
]
|
|
450
|
+
|
|
451
|
+
const mock = vi.fn()
|
|
452
|
+
injectLinksInDocument(elements, mock, { aiInjections: [], manualInjections: injections })
|
|
453
|
+
|
|
454
|
+
console.log(document.body.innerHTML)
|
|
455
|
+
|
|
456
|
+
expect(document.querySelectorAll('a')).toHaveLength(3)
|
|
457
|
+
})
|
|
392
458
|
})
|
|
393
459
|
|
|
394
460
|
describe('clearLinkInjections', () => {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, it, expect, beforeEach } from 'vitest'
|
|
2
|
-
import { findTextNodeContaining, isNodeInLink, replaceStartingFrom } from '$lib/text'
|
|
2
|
+
import { cleanPhrase, findTextNodeContaining, isNodeInLink, replaceStartingFrom } from '$lib/text'
|
|
3
3
|
|
|
4
4
|
describe('text.js', () => {
|
|
5
5
|
beforeEach(() => {
|
|
@@ -91,4 +91,18 @@ describe('text.js', () => {
|
|
|
91
91
|
expect(replaceStartingFrom('Some text & more', 'text & more', 'word', 0)).toBe('Some word')
|
|
92
92
|
})
|
|
93
93
|
})
|
|
94
|
+
|
|
95
|
+
describe('cleanPhrase', () => {
|
|
96
|
+
it('Should return the given text without spaces', () => {
|
|
97
|
+
expect(cleanPhrase('Some phrase with spaces ')).toBe('somephrasewithspaces')
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
it('Should return the given text in lowercase', () => {
|
|
101
|
+
expect(cleanPhrase('SomE PhrASE')).toBe('somephrase')
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
it('Should return the given phrase with decoded characters', () => {
|
|
105
|
+
expect(cleanPhrase('{&}')).toBe('{&}')
|
|
106
|
+
})
|
|
107
|
+
})
|
|
94
108
|
})
|
|
@@ -53,8 +53,8 @@ describe('$routes/+page.svelte', () => {
|
|
|
53
53
|
render(page)
|
|
54
54
|
|
|
55
55
|
await waitFor(() => {
|
|
56
|
-
expect(pollLinkInjections).
|
|
57
|
-
expect(pollLinkInjections).
|
|
56
|
+
expect(pollLinkInjections).toHaveBeenCalledWith(expect.any(String), expect.any(String), { maxTries: 1 })
|
|
57
|
+
expect(pollLinkInjections).toHaveBeenCalledWith(expect.any(String), expect.any(String), { requireCompletedResult: true })
|
|
58
58
|
})
|
|
59
59
|
})
|
|
60
60
|
|
|
@@ -65,7 +65,7 @@ describe('$routes/+page.svelte', () => {
|
|
|
65
65
|
window.PlayPilotLinkInjections = { selector: '.some-element' }
|
|
66
66
|
|
|
67
67
|
render(page)
|
|
68
|
-
expect(pollLinkInjections).toHaveBeenCalledWith(expect.
|
|
68
|
+
expect(pollLinkInjections).toHaveBeenCalledWith(expect.any(String), '<p>Here</p>', { maxTries: 1 })
|
|
69
69
|
})
|
|
70
70
|
|
|
71
71
|
it('Should render editor if correct url param is given', async () => {
|
|
@@ -60,6 +60,12 @@ describe('Title.svelte', () => {
|
|
|
60
60
|
expect(container.querySelector('.small')).toBeTruthy()
|
|
61
61
|
})
|
|
62
62
|
|
|
63
|
+
it('Should truncate title when small prop is given', () => {
|
|
64
|
+
const { container } = render(Title, { title, small: true })
|
|
65
|
+
|
|
66
|
+
expect(container.querySelector('h1.truncate')).toBeTruthy()
|
|
67
|
+
})
|
|
68
|
+
|
|
63
69
|
it('Should render given description', () => {
|
|
64
70
|
const { getByText } = render(Title, { title: { ...title, description: 'Some description' } })
|
|
65
71
|
|