@playpilot/tpi 5.24.2 → 5.24.4
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 +11 -11
- package/package.json +1 -1
- package/src/lib/injection.ts +45 -14
- package/src/routes/+layout.svelte +1 -1
- package/src/routes/components/ListTitle.svelte +1 -1
- package/src/routes/components/Playlinks/PlaylinkLabel.svelte +2 -2
- package/src/tests/lib/injections.test.js +11 -0
package/package.json
CHANGED
package/src/lib/injection.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { mount, unmount } from 'svelte'
|
|
2
2
|
import TitlePopover from '../routes/components/TitlePopover.svelte'
|
|
3
3
|
import AfterArticlePlaylinks from '../routes/components/Playlinks/AfterArticlePlaylinks.svelte'
|
|
4
|
-
import { cleanPhrase, findNumberOfMatchesInString, findShortestMatchBetweenPhrases, findTextNodeContaining, getIndexOfPhraseInElement, getIndexOfPhraseInBoundary, getNumberOfLeadingAndTrailingSpaces, isNodeInLink, replaceBetween, replaceStartingFrom } from './text'
|
|
4
|
+
import { cleanPhrase, findNumberOfMatchesInString, findShortestMatchBetweenPhrases, findTextNodeContaining, getIndexOfPhraseInElement, getIndexOfPhraseInBoundary, getNumberOfLeadingAndTrailingSpaces, isNodeInLink, replaceBetween, replaceStartingFrom, findSurroundingPhrases } from './text'
|
|
5
5
|
import type { LinkInjection, LinkInjectionTypes } from './types/injection'
|
|
6
6
|
import { isHoldingSpecialKey } from './event'
|
|
7
7
|
import { playFallbackViewTransition } from './viewTransition'
|
|
@@ -158,7 +158,6 @@ export function injectLinksInDocument(elements: HTMLElement[], injections: LinkI
|
|
|
158
158
|
}
|
|
159
159
|
|
|
160
160
|
const { injectionElement, linkElement } = createLinkInjectionElement(injection)
|
|
161
|
-
const { phrase_before, phrase_after } = injection
|
|
162
161
|
|
|
163
162
|
let replacementIndex = -1
|
|
164
163
|
let hasBeenReplaced = false
|
|
@@ -167,11 +166,15 @@ export function injectLinksInDocument(elements: HTMLElement[], injections: LinkI
|
|
|
167
166
|
// Check if there is only one occurance in the element, in which case the replacement is simple
|
|
168
167
|
// It's important that we check against the phrase without cleanPhrase(), as we need to check if an
|
|
169
168
|
// element might contain attributes that contain the phrase.
|
|
170
|
-
const
|
|
171
|
-
if (
|
|
169
|
+
const numberOfHtmlMatches = findNumberOfMatchesInString(element.innerHTML, injection.title)
|
|
170
|
+
if (numberOfHtmlMatches === 1) replacementIndex = element.innerHTML.indexOf(injection.title)
|
|
172
171
|
|
|
173
172
|
// !! Option 2 - Replace by phrase_before and phrase_after
|
|
174
|
-
// If multiple or no occurences were found, we use
|
|
173
|
+
// If multiple or no occurences were found, we use the phrases before and after the injection to find
|
|
174
|
+
// the location of the correct title. This helps with multiple occurrences of the same phrase, but also
|
|
175
|
+
// with text that is broken up by html elements.
|
|
176
|
+
const { phrase_before, phrase_after } = getPhrasesSurroundingInjection(element, injection)
|
|
177
|
+
|
|
175
178
|
if (replacementIndex === -1 && (phrase_before || phrase_after)) {
|
|
176
179
|
// The before and after phrase are combined to see if the sentence contains the match exactly.
|
|
177
180
|
// This is a fairly simple comparison that will fail on special characters, html tags, or inconsistencies
|
|
@@ -202,7 +205,8 @@ export function injectLinksInDocument(elements: HTMLElement[], injections: LinkI
|
|
|
202
205
|
|
|
203
206
|
// !! Option 3 - Replace by title only, taking previous injections into account
|
|
204
207
|
// If no occurences were found previously, we check the element from start to finish only checking for the title,
|
|
205
|
-
// without considering phrase_before and phrase_after
|
|
208
|
+
// without considering phrase_before and phrase_after. Here we check the index of multiple different types of matches
|
|
209
|
+
// and go with the highest index between them all, assuming that is always going to be the correct phrase.
|
|
206
210
|
if (replacementIndex === -1 && nodeContainingText?.nodeValue) {
|
|
207
211
|
// Start searching for injection from either the value, the sentence, or the occurrence. This prevents injecting into
|
|
208
212
|
// text in an element earlier than the sentence started. A element might contain many sentences, after all.
|
|
@@ -305,28 +309,55 @@ function createLinkInjectionElement(injection: LinkInjection): { injectionElemen
|
|
|
305
309
|
/**
|
|
306
310
|
* In some coses injections lead to broken HTML. The reason for this varies and is hard to figure out. In any case, we
|
|
307
311
|
* should never serve broken HTML.
|
|
308
|
-
* Before replacing the HTML, we apply the the replacement to a dummy element. From here we check if the
|
|
309
|
-
*
|
|
310
|
-
*
|
|
312
|
+
* Before replacing the HTML, we apply the the replacement to a dummy element. From here we check if the length of the text has
|
|
313
|
+
* increased. If it did, something went wrong. What exactly is hard to say, we just know something didn't go as intended and
|
|
314
|
+
* we should not inject.
|
|
311
315
|
*
|
|
312
|
-
* No tests exists for this function because writing tests would require finding a scenario in which things break. If we
|
|
313
|
-
*
|
|
316
|
+
* No tests exists for this function because writing tests would require finding a scenario in which things break. If we knew
|
|
317
|
+
* when things break we'd fix it instead.
|
|
314
318
|
*/
|
|
315
319
|
function replaceIfSafeInjection(originalHtml: string, phrase: string, sentenceElement: HTMLElement, injectionElement: HTMLElement, replacementIndex: number): boolean {
|
|
316
320
|
const dummyElement = document.createElement('div')
|
|
317
321
|
dummyElement.innerHTML = originalHtml
|
|
318
322
|
|
|
319
|
-
const
|
|
323
|
+
const originalText = dummyElement.innerText
|
|
320
324
|
dummyElement.innerHTML = replaceStartingFrom(originalHtml, phrase, injectionElement.outerHTML, replacementIndex)
|
|
321
|
-
const finalNumberOfLinks = Array.from(dummyElement.querySelectorAll<HTMLAnchorElement>('a')).filter(a => a.innerText).length
|
|
322
325
|
|
|
323
|
-
|
|
326
|
+
// If the text has changed at all, something probably went wrong as the new text is supposed to be the same as the old.
|
|
327
|
+
if (Math.abs(dummyElement.innerText.length - originalText.length) > 1) return false
|
|
324
328
|
|
|
325
329
|
sentenceElement.innerHTML = dummyElement.innerHTML
|
|
326
330
|
|
|
327
331
|
return true
|
|
328
332
|
}
|
|
329
333
|
|
|
334
|
+
/**
|
|
335
|
+
* phrase_before and phrase_after are set for manual injections to better get their placement within a sentence.
|
|
336
|
+
* This helps when an injection contains multiple injections of the same word and it also helps when an injection
|
|
337
|
+
* is broken up by html elements. For instance, DigitalSpy sometimes uses multiple styling tags on the same phrase.
|
|
338
|
+
* Something like `<strong>phr</strong><strong>ase</strong.
|
|
339
|
+
*
|
|
340
|
+
* Secondary to manual injections we can also get the phrase_before and phrase_after for ai injections.
|
|
341
|
+
* This only works if the injection occurs only once. In this case it is only used when the phrase is broken up
|
|
342
|
+
* by html elements, like before.
|
|
343
|
+
*/
|
|
344
|
+
function getPhrasesSurroundingInjection(element: HTMLElement, injection: LinkInjection): { phrase_before: string | null | undefined, phrase_after: string | null | undefined } {
|
|
345
|
+
const { phrase_before, phrase_after } = injection
|
|
346
|
+
|
|
347
|
+
if (phrase_before || phrase_after) return { phrase_before, phrase_after }
|
|
348
|
+
|
|
349
|
+
// Get the number of occurrences of the same phrase in the element. We can only use the before and after
|
|
350
|
+
// phrases if there is only 1 occurrence, as we can't guarentee we injection into the expected phrase otherwise.
|
|
351
|
+
const numberOfTextMatches = findNumberOfMatchesInString(element.innerText, injection.title)
|
|
352
|
+
|
|
353
|
+
if (numberOfTextMatches !== 1) return { phrase_before: null, phrase_after: null }
|
|
354
|
+
|
|
355
|
+
const startIndex = element.innerText.indexOf(injection.title)
|
|
356
|
+
const surroundingPhrases = findSurroundingPhrases(element, startIndex, startIndex + injection.title.length)
|
|
357
|
+
|
|
358
|
+
return { phrase_before: surroundingPhrases.before, phrase_after: surroundingPhrases.after }
|
|
359
|
+
}
|
|
360
|
+
|
|
330
361
|
/**
|
|
331
362
|
* Add all used CSS variables to a data attribute. This data attribute is then used for selectors that for each
|
|
332
363
|
* individual CSS variable. This is done this way so that CSS variables are only set when they are used.
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
<time datetime="14:00">1 hour ago</time>
|
|
60
60
|
</header>
|
|
61
61
|
|
|
62
|
-
<p>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>
|
|
62
|
+
<p>Following the success of John M. Chu's 2018 romantic-comedy <strong>Crazy</strong> 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>
|
|
63
63
|
|
|
64
64
|
<h2>A smaller heading, possibly with an injection in it</h2>
|
|
65
65
|
<p>In an interview with Epire & Magazine, Quan reveals he quested starring in Love Hurts, which sees him Love Hurts 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>
|
|
@@ -194,7 +194,7 @@
|
|
|
194
194
|
margin: 0 margin(0.5) 0 auto;
|
|
195
195
|
padding-left: margin(0.5);
|
|
196
196
|
align-self: center;
|
|
197
|
-
color: var(--playpilot-list-item-action-color, var(--playpilot-detail-text-color,
|
|
197
|
+
color: var(--playpilot-list-item-action-color, var(--playpilot-detail-text-color, rgba(255, 255, 255, 0.75)));
|
|
198
198
|
|
|
199
199
|
&:hover,
|
|
200
200
|
&:active {
|
|
@@ -20,11 +20,11 @@
|
|
|
20
20
|
<style lang="scss">
|
|
21
21
|
.playlink {
|
|
22
22
|
display: inline-block;
|
|
23
|
-
background: var(--playpilot-playlink-label-background, var(--playpilot-genre-background, var(--playpilot-
|
|
23
|
+
background: var(--playpilot-playlink-label-background, var(--playpilot-genre-background, var(--playpilot-content)));
|
|
24
24
|
border-radius: var(--playpilot-playlink-label-border-radius, margin(0.25));
|
|
25
25
|
padding: margin(0.25) margin(0.5);
|
|
26
26
|
font-size: var(--playpilot-playlink-label-font-size, 12px);
|
|
27
|
-
color: var(--playpilot-playlink-label-text-color, var(--playpilot-detail-text-color,
|
|
27
|
+
color: var(--playpilot-playlink-label-text-color, var(--playpilot-detail-text-color-alt, rgba(255, 255, 255, 0.75))) !important;
|
|
28
28
|
text-decoration: none;
|
|
29
29
|
font-style: normal !important;
|
|
30
30
|
overflow: hidden;
|
|
@@ -852,6 +852,17 @@ describe('linkInjection.js', () => {
|
|
|
852
852
|
expect(document.querySelector('a')?.innerText).toBe(injection.title)
|
|
853
853
|
})
|
|
854
854
|
|
|
855
|
+
it('Should properly inject into titles that are split up by multiple elements when neither phrase_before or phrase_after are given but the phrase occurs only once', () => {
|
|
856
|
+
document.body.innerHTML = '<section>Some text with a <strong>phra</strong><strong>se</strong> in it</section>'
|
|
857
|
+
|
|
858
|
+
const elements = getLinkInjectionElements(/** @type {HTMLElement} */ (document.querySelector('section')))
|
|
859
|
+
const injection = generateInjection('Some text with a phrase in it', 'phrase')
|
|
860
|
+
|
|
861
|
+
injectLinksInDocument(elements, { aiInjections: [], manualInjections: [injection] })
|
|
862
|
+
|
|
863
|
+
expect(document.querySelector('a')?.innerText).toBe(injection.title)
|
|
864
|
+
})
|
|
865
|
+
|
|
855
866
|
it('Should replace existing PlayPilot links', () => {
|
|
856
867
|
document.body.innerHTML = '<p>This is a sentence with <a href="https://playpilot.com/movie/123">an injection</a>.</p>'
|
|
857
868
|
|