@playpilot/tpi 1.1.1 → 1.4.0

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.
Files changed (48) hide show
  1. package/dist/link-injections.js +7 -7
  2. package/package.json +3 -1
  3. package/src/lib/api.js +5 -1
  4. package/src/lib/data/translations.js +238 -0
  5. package/src/lib/enums/Language.js +4 -0
  6. package/src/lib/linkInjection.js +11 -4
  7. package/src/lib/localization.js +36 -0
  8. package/src/lib/meta.js +82 -0
  9. package/src/lib/playlink.js +28 -0
  10. package/src/lib/scss/_functions.scss +3 -0
  11. package/src/lib/scss/global.scss +75 -0
  12. package/src/lib/{variables.css → scss/variables.scss} +8 -2
  13. package/src/lib/url.js +7 -0
  14. package/src/main.js +3 -1
  15. package/src/routes/+layout.svelte +25 -21
  16. package/src/routes/+page.svelte +20 -18
  17. package/src/routes/components/AfterArticlePlaylinks.svelte +18 -16
  18. package/src/routes/components/ContextMenu.svelte +9 -9
  19. package/src/routes/components/Description.svelte +7 -7
  20. package/src/routes/components/Editorial/Alert.svelte +4 -4
  21. package/src/routes/components/Editorial/DragHandle.svelte +27 -26
  22. package/src/routes/components/Editorial/Editor.svelte +64 -48
  23. package/src/routes/components/Editorial/EditorItem.svelte +44 -44
  24. package/src/routes/components/Editorial/ManualInjection.svelte +23 -20
  25. package/src/routes/components/Editorial/PlaylinkTypeSelect.svelte +22 -22
  26. package/src/routes/components/Editorial/Search/TitleSearch.svelte +22 -22
  27. package/src/routes/components/Editorial/Switch.svelte +21 -20
  28. package/src/routes/components/Editorial/TextInput.svelte +12 -12
  29. package/src/routes/components/Genres.svelte +16 -11
  30. package/src/routes/components/Icons/IconChevron.svelte +1 -1
  31. package/src/routes/components/Modal.svelte +14 -16
  32. package/src/routes/components/Participants.svelte +11 -9
  33. package/src/routes/components/Playlinks.svelte +51 -45
  34. package/src/routes/components/Popover.svelte +18 -16
  35. package/src/routes/components/RoundButton.svelte +6 -6
  36. package/src/routes/components/SkeletonText.svelte +15 -15
  37. package/src/routes/components/Title.svelte +64 -59
  38. package/src/tests/lib/api.test.js +5 -0
  39. package/src/tests/lib/linkInjection.test.js +8 -5
  40. package/src/tests/lib/localization.test.js +67 -0
  41. package/src/tests/lib/meta.test.js +201 -0
  42. package/src/tests/lib/playlink.test.js +82 -0
  43. package/src/tests/lib/url.test.js +35 -0
  44. package/src/tests/routes/components/Editorial/Editor.test.js +8 -2
  45. package/src/tests/routes/components/Editorial/ManualInjection.test.js +51 -0
  46. package/src/typedefs.js +9 -1
  47. package/svelte.config.js +9 -0
  48. package/src/lib/global.css +0 -69
package/src/lib/api.js CHANGED
@@ -1,6 +1,8 @@
1
1
  import { authorize, getAuthToken, isEditorialModeEnabled } from './auth'
2
2
  import { apiBaseUrl } from './constants'
3
3
  import { stringToHash } from './hash'
4
+ import { getPageMetaData } from './meta'
5
+ import { getFullUrlPath } from './url'
4
6
 
5
7
  /** @type {NodeJS.Timeout | number | null} */
6
8
  let pollTimeout = null
@@ -19,6 +21,7 @@ export async function fetchLinkInjections(url, html, { hash = stringToHash(html)
19
21
  const headers = new Headers({ 'Content-Type': 'application/json' })
20
22
  const apiToken = getApiToken()
21
23
  const isEditorialMode = isEditorialModeEnabled() ? await authorize() : false
24
+ const metadata = getPageMetaData()
22
25
 
23
26
  if (!apiToken) throw new Error('No token was provided')
24
27
 
@@ -28,6 +31,7 @@ export async function fetchLinkInjections(url, html, { hash = stringToHash(html)
28
31
  body: JSON.stringify(({
29
32
  hash,
30
33
  url,
34
+ metadata,
31
35
  page_text: html,
32
36
  ...params,
33
37
  })),
@@ -119,7 +123,7 @@ export async function saveLinkInjections(linkInjections, html) {
119
123
  inactive: !!linkInjection.inactive,
120
124
  }))
121
125
 
122
- const response = await fetchLinkInjections(window.location.host + location.pathname, html, {
126
+ const response = await fetchLinkInjections(getFullUrlPath(), html, {
123
127
  params: {
124
128
  private_token: getAuthToken(),
125
129
  automation_enabled: false,
@@ -0,0 +1,238 @@
1
+ import { Language } from '$lib/enums/Language.js'
2
+
3
+ export const translations = {
4
+ 'Where To Stream Online': {
5
+ [Language.English]: 'Where to stream online',
6
+ [Language.Swedish]: 'Var kan man streama online',
7
+ },
8
+ 'Affiliate Disclaimer': {
9
+ [Language.English]: 'We may earn a commission if you make a purchase through these links. In collaboration with',
10
+ [Language.Swedish]: 'Vi kan få provision om du gör ett köp via dessa länkar. I samarbete med',
11
+ },
12
+ 'An Error Occurred': {
13
+ [Language.English]: 'An error occurred',
14
+ [Language.Swedish]: 'Ett fel inträffade',
15
+ },
16
+ 'Title Unavailable': {
17
+ [Language.English]: 'This title is not currently available to stream.',
18
+ [Language.Swedish]: 'Den här titeln går inte att streama just nu.',
19
+ },
20
+ 'Title Unavailable Suffix': {
21
+ [Language.English]: 'is not currently available to stream.',
22
+ [Language.Swedish]: 'går inte att streama just nu.',
23
+ },
24
+ 'Is Available To Stream On': {
25
+ [Language.English]: 'is available to stream on',
26
+ [Language.Swedish]: 'finns att streama på',
27
+ },
28
+ 'Is Available To Stream': {
29
+ [Language.English]: 'is available to stream.',
30
+ [Language.Swedish]: 'finns att streama.',
31
+ },
32
+ 'View Streaming Options': {
33
+ [Language.English]: 'View streaming options',
34
+ [Language.Swedish]: 'Se streamingalternativ',
35
+ },
36
+ 'And': {
37
+ [Language.English]: 'and',
38
+ [Language.Swedish]: 'och',
39
+ },
40
+ 'Minutes': {
41
+ [Language.English]: 'minutes',
42
+ [Language.Swedish]: 'minuter',
43
+ },
44
+ 'Type: movie': {
45
+ [Language.English]: 'Movie',
46
+ [Language.Swedish]: 'Film',
47
+ },
48
+ 'Type: series': {
49
+ [Language.English]: 'Series',
50
+ [Language.Swedish]: 'Serie',
51
+ },
52
+ 'Stream': {
53
+ [Language.English]: 'Stream',
54
+ [Language.Swedish]: 'Strömma',
55
+ },
56
+ 'Buy': {
57
+ [Language.English]: 'Buy',
58
+ [Language.Swedish]: 'Köp',
59
+ },
60
+ 'Rent': {
61
+ [Language.English]: 'Rent',
62
+ [Language.Swedish]: 'Hyra',
63
+ },
64
+ 'Rent Or Buy': {
65
+ [Language.English]: 'Rent or Buy',
66
+ [Language.Swedish]: 'Hyra',
67
+ },
68
+
69
+ // Genres
70
+ 'All': {
71
+ [Language.English]: 'All',
72
+ [Language.Swedish]: 'Alla',
73
+ },
74
+ 'Unscripted': {
75
+ [Language.English]: 'Unscripted',
76
+ [Language.Swedish]: 'Oscripterat',
77
+ },
78
+ 'Independent': {
79
+ [Language.English]: 'Independent',
80
+ [Language.Swedish]: 'Indie',
81
+ },
82
+ 'Game Show': {
83
+ [Language.English]: 'Game Show',
84
+ [Language.Swedish]: 'Gameshow',
85
+ },
86
+ 'Film Noir': {
87
+ [Language.English]: 'Film Noir',
88
+ [Language.Swedish]: 'Film noir',
89
+ },
90
+ 'Entertainment': {
91
+ [Language.English]: 'Entertainment',
92
+ [Language.Swedish]: 'Underhållning',
93
+ },
94
+ 'News': {
95
+ [Language.English]: 'News',
96
+ [Language.Swedish]: 'Nyheter',
97
+ },
98
+ 'Short': {
99
+ [Language.English]: 'Short',
100
+ [Language.Swedish]: 'Kortfilm',
101
+ },
102
+ 'Bollywood': {
103
+ [Language.English]: 'Bollywood',
104
+ [Language.Swedish]: 'Bollywood',
105
+ },
106
+ 'Concert': {
107
+ [Language.English]: 'Concert',
108
+ [Language.Swedish]: 'Konsert',
109
+ },
110
+ 'Arthouse': {
111
+ [Language.English]: 'Arthouse',
112
+ [Language.Swedish]: 'Arthouse',
113
+ },
114
+ 'Alternate Version': {
115
+ [Language.English]: 'Alternate Version',
116
+ [Language.Swedish]: 'Alternativ version',
117
+ },
118
+ 'Reality TV': {
119
+ [Language.English]: 'Reality TV',
120
+ [Language.Swedish]: 'Reality-TV',
121
+ },
122
+ 'Culture': {
123
+ [Language.English]: 'Culture',
124
+ [Language.Swedish]: 'Kultur',
125
+ },
126
+ 'Drama': {
127
+ [Language.English]: 'Drama',
128
+ [Language.Swedish]: 'Drama',
129
+ },
130
+ 'Crime': {
131
+ [Language.English]: 'Crime',
132
+ [Language.Swedish]: 'Kriminal',
133
+ },
134
+ 'Western': {
135
+ [Language.English]: 'Western',
136
+ [Language.Swedish]: 'Western',
137
+ },
138
+ 'Thriller': {
139
+ [Language.English]: 'Thriller',
140
+ [Language.Swedish]: 'Thriller',
141
+ },
142
+ 'Animation': {
143
+ [Language.English]: 'Animation',
144
+ [Language.Swedish]: 'Animerat',
145
+ },
146
+ 'Mystery': {
147
+ [Language.English]: 'Mystery',
148
+ [Language.Swedish]: 'Mysterium',
149
+ },
150
+ 'Science': {
151
+ [Language.English]: 'Science',
152
+ [Language.Swedish]: 'Vetenskap',
153
+ },
154
+ 'Nature': {
155
+ [Language.English]: 'Nature',
156
+ [Language.Swedish]: 'Natur',
157
+ },
158
+ 'War': {
159
+ [Language.English]: 'War',
160
+ [Language.Swedish]: 'Krig',
161
+ },
162
+ 'Sci-Fi': {
163
+ [Language.English]: 'Sci-Fi',
164
+ [Language.Swedish]: 'Sci-fi',
165
+ },
166
+ 'Documentary': {
167
+ [Language.English]: 'Documentary',
168
+ [Language.Swedish]: 'Dokumentär',
169
+ },
170
+ 'Stand-up': {
171
+ [Language.English]: 'Stand-up',
172
+ [Language.Swedish]: 'Standup',
173
+ },
174
+ 'Talk Show': {
175
+ [Language.English]: 'Talk Show',
176
+ [Language.Swedish]: 'Talkshow',
177
+ },
178
+ 'Fantasy': {
179
+ [Language.English]: 'Fantasy',
180
+ [Language.Swedish]: 'Fantasy',
181
+ },
182
+ 'History': {
183
+ [Language.English]: 'History',
184
+ [Language.Swedish]: 'Historia',
185
+ },
186
+ 'Adventure': {
187
+ [Language.English]: 'Adventure',
188
+ [Language.Swedish]: 'Äventyr',
189
+ },
190
+ 'Action': {
191
+ [Language.English]: 'Action',
192
+ [Language.Swedish]: 'Action',
193
+ },
194
+ 'Horror': {
195
+ [Language.English]: 'Horror',
196
+ [Language.Swedish]: 'Skräck',
197
+ },
198
+ 'Comedy': {
199
+ [Language.English]: 'Comedy',
200
+ [Language.Swedish]: 'Komedi',
201
+ },
202
+ 'Biography': {
203
+ [Language.English]: 'Biography',
204
+ [Language.Swedish]: 'Biografi',
205
+ },
206
+ 'Music': {
207
+ [Language.English]: 'Music',
208
+ [Language.Swedish]: 'Musik',
209
+ },
210
+ 'Sport': {
211
+ [Language.English]: 'Sport',
212
+ [Language.Swedish]: 'Sport',
213
+ },
214
+ 'Romance': {
215
+ [Language.English]: 'Romance',
216
+ [Language.Swedish]: 'Romantik',
217
+ },
218
+ 'Kids': {
219
+ [Language.English]: 'Kids',
220
+ [Language.Swedish]: 'Barn',
221
+ },
222
+ 'Lifestyle': {
223
+ [Language.English]: 'Lifestyle',
224
+ [Language.Swedish]: 'Livsstil',
225
+ },
226
+ 'Musical': {
227
+ [Language.English]: 'Musical',
228
+ [Language.Swedish]: 'Musikal',
229
+ },
230
+ 'Anime': {
231
+ [Language.English]: 'Anime',
232
+ [Language.Swedish]: 'Anime',
233
+ },
234
+ 'Family': {
235
+ [Language.English]: 'Family',
236
+ [Language.Swedish]: 'Familj',
237
+ },
238
+ }
@@ -0,0 +1,4 @@
1
+ export const Language = Object.freeze({
2
+ English: 'en-US',
3
+ Swedish: 'sv-SE',
4
+ })
@@ -18,10 +18,10 @@ let afterArticlePlaylinkInsertedComponent = null
18
18
  export function getLinkInjectionElements(parentElement) {
19
19
  let validElements = []
20
20
 
21
- let remainingChildren = [...parentElement.children]
21
+ let remainingChildren = [parentElement]
22
22
 
23
23
  while (remainingChildren.length > 0) {
24
- const element = /** @type {HTMLElement} */ (remainingChildren.shift())
24
+ const element = /** @type {HTMLElement} */ (remainingChildren.pop())
25
25
 
26
26
  // Ignore links, buttons, and headers
27
27
  if (/^(A|BUTTON|SCRIPT|NOSCRIPT|STYLE|IFRAME|H[1-6])$/.test(element.tagName)) continue
@@ -33,8 +33,15 @@ export function getLinkInjectionElements(parentElement) {
33
33
 
34
34
  // If this element has a text node we add it to the valid elements and stop there
35
35
  // Otherwise we add all children to be checked in this same loop.
36
- if (hasTextNode) validElements.push(element)
37
- else remainingChildren.push(...element.children)
36
+ if (hasTextNode) {
37
+ validElements.push(element)
38
+ continue
39
+ }
40
+
41
+ const children = /** @type {HTMLElement[]} */ (Array.from(element.children))
42
+ for (let i = children.length - 1; i >= 0; i--) {
43
+ remainingChildren.push(children[i])
44
+ }
38
45
  }
39
46
 
40
47
  return validElements
@@ -0,0 +1,36 @@
1
+ import { translations } from '$lib/data/translations'
2
+ import { Language } from '$lib/enums/Language'
3
+
4
+ /**
5
+ * Super basic implementation to get a string for the given key and language
6
+ * @param {string} key Key of the wanted translation
7
+ * @param {LanguageCode} language Language code
8
+ * @returns
9
+ */
10
+ export function t(key, language = getLanguage()) {
11
+ // @ts-ignore It's ok if a key is not found.
12
+ return translations[key]?.[language] || key
13
+ }
14
+
15
+ /**
16
+ * @returns {LanguageCode}
17
+ */
18
+ export function getLanguage() {
19
+ // @ts-ignore
20
+ const configLanguage = window.PlayPilotLinkInjections?.language
21
+ const languageCodes = /** @type {string[]} */ (Object.values(Language))
22
+
23
+ if (configLanguage) {
24
+ if (languageCodes.includes(configLanguage)) return configLanguage
25
+
26
+ console.warn(`PlayPilot Link Injections: ${configLanguage} is not an accepted language`)
27
+ }
28
+
29
+ const documentLanguage = document.querySelector('html')?.getAttribute('lang')
30
+
31
+ if (documentLanguage && languageCodes.includes(documentLanguage)) {
32
+ return /** @type {LanguageCode} */ (documentLanguage)
33
+ }
34
+
35
+ return Language.English
36
+ }
@@ -0,0 +1,82 @@
1
+ import { getLinkInjectionsParentElement } from './linkInjection'
2
+
3
+ /**
4
+ * Returns meta data to be included with injections.
5
+ * @returns {ArticleMetaData}
6
+ */
7
+ export function getPageMetaData() {
8
+ const parent = getLinkInjectionsParentElement()
9
+
10
+ return {
11
+ heading: getPageHeading(parent),
12
+ modified_time: getPageModifiedTime(parent),
13
+ published_time: getPageModifiedTime(parent),
14
+ }
15
+ }
16
+
17
+ /**
18
+ * Returns the most relevant `h1` content on the page.
19
+ * A page should only ever contain exactly 1 `h1` tag, but we don't control these things.
20
+ * @param {HTMLElement} parent
21
+ * @returns {string | null}
22
+ */
23
+ export function getPageHeading(parent) {
24
+ const heading = parent.querySelector('h1') || document.querySelector('h1')
25
+
26
+ return heading?.innerText.trim() || null
27
+ }
28
+
29
+ /**
30
+ * Returns the datetime for the article, intended to be the last time the article was modified.
31
+ * @param {HTMLElement} parent
32
+ * @returns {string | null}
33
+ */
34
+ export function getPageModifiedTime(parent) {
35
+ const element =
36
+ /** @type {HTMLElement | null} */
37
+ (document.querySelector('meta[property*="modified_time"]') ||
38
+ parent.querySelector('[itemprop="dateModified"]') ||
39
+ document.querySelector('[itemprop="dateModified"]') || null)
40
+
41
+ const datetime = element?.getAttribute('content') || element?.getAttribute('datetime') || element?.innerText
42
+
43
+ if (!datetime) return null
44
+
45
+ return getDatetime(datetime)
46
+ }
47
+
48
+ /**
49
+ * Returns the datetime for the article, intended to be the date the article was first published
50
+ * @param {HTMLElement} parent
51
+ * @returns {string | null}
52
+ */
53
+ export function getPagePublishedTime(parent) {
54
+ const element =
55
+ /** @type {HTMLElement | null} */
56
+ (document.querySelector('meta[property*="published_time"]') ||
57
+ parent.querySelector('[itemprop="datePublished"]') ||
58
+ document.querySelector('[itemprop="datePublished"]') ||
59
+ parent.querySelector('time') ||
60
+ document.querySelector('time') ||
61
+ parent.querySelector('[datetime]') ||
62
+ document.querySelector('[datetime]') || null)
63
+
64
+ const datetime = element?.getAttribute('content') || element?.getAttribute('datetime') || element?.innerText
65
+
66
+ if (!datetime) return null
67
+
68
+ return getDatetime(datetime)
69
+ }
70
+
71
+ /**
72
+ * Get datetime string as ISO datetime string
73
+ * @param {string} datetime
74
+ * @returns {string | null}
75
+ */
76
+ function getDatetime(datetime) {
77
+ try {
78
+ return new Date(datetime).toISOString()
79
+ } catch {
80
+ return null
81
+ }
82
+ }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Merge playlinks of the same provider of BUY and RENT categories into a shared TVOD category.
3
+ * @param {PlaylinkData[]} playlinks
4
+ * @returns {PlaylinkData[]}
5
+ */
6
+ export function mergePlaylinks(playlinks) {
7
+ /** @type {PlaylinkData[]} */
8
+ let merged = []
9
+
10
+ for (const playlink of playlinks) {
11
+ let newPlaylink = playlink
12
+ const existingPlaylink = merged.find(p => p.name === newPlaylink.name)
13
+
14
+ const category1 = existingPlaylink?.extra_info.category
15
+ const category2 = newPlaylink.extra_info.category
16
+
17
+ // If a playlink for the same provider already exists in a BUY or RENT category, we remove it from
18
+ // the array and re-add it as a new entry with the TVOD category
19
+ if ((category1 === 'RENT' && category2 === 'BUY') || (category1 === 'BUY' && category2 === 'RENT')) {
20
+ merged = merged.filter(p => p.name !== newPlaylink.name)
21
+ newPlaylink = { ...newPlaylink, extra_info: { ...newPlaylink.extra_info, category: 'TVOD' } }
22
+ }
23
+
24
+ merged.push(newPlaylink)
25
+ }
26
+
27
+ return merged
28
+ }
@@ -0,0 +1,3 @@
1
+ @function margin($value) {
2
+ @return 16px * $value;
3
+ }
@@ -0,0 +1,75 @@
1
+ @use "./_functions" as *;
2
+
3
+ [data-playpilot-injection-key] {
4
+ position: relative;
5
+
6
+ &.injection-highlight {
7
+ outline: margin(0.25) solid var(--playpilot-primary) !important;
8
+ outline-offset: margin(0.5) !important;
9
+ border-radius: margin(0.05);
10
+ scroll-margin: margin(5);
11
+ }
12
+ }
13
+
14
+ .playpilot-styled-scrollbar {
15
+ scrollbar-color: var(--playpilot-content-light) var(--playpilot-lighter);
16
+ scrollbar-width: thin;
17
+ }
18
+
19
+ .playpilot-styled-scrollbed {
20
+ &::-webkit-scrollbar {
21
+ width: margin(0.75);
22
+ }
23
+
24
+ &::-webkit-scrollbar-track {
25
+ background: var(--playpilot-light);
26
+ }
27
+
28
+ &::-webkit-scrollbar-thumb {
29
+ border: 2px solid var(--playpilot-light);
30
+ border-radius: margin(1);
31
+ background: var(--playpilot-lighter);
32
+
33
+ &:hover {
34
+ background: var(--playpilot-content-light);
35
+ }
36
+
37
+ &:active {
38
+ background: var(--playpilot-text-color-alt);
39
+ }
40
+ }
41
+ }
42
+
43
+ /*
44
+ Styling for individual CSS variables, with a selector for each variable.
45
+ This is very verbose, but necessary in order to only use the CSS variable when they are actually set.
46
+ */
47
+ [data-playpilot-injection-key] {
48
+ &[data-used-css-variables*="--playpilot-injection-font-weight"] {
49
+ font-weight: var(--playpilot-injection-font-weight);
50
+ }
51
+
52
+ &[data-used-css-variables*="--playpilot-injection-text-color"] {
53
+ color: var(--playpilot-injection-text-color);
54
+ }
55
+
56
+ &[data-used-css-variables*="--playpilot-injection-text-color-hover"]:hover {
57
+ color: var(--playpilot-injection-text-color-hover);
58
+ }
59
+
60
+ &[data-used-css-variables*="--playpilot-injection-text-decoration"] {
61
+ text-decoration: var(--playpilot-injection-text-decoration);
62
+ }
63
+
64
+ &[data-used-css-variables*="--playpilot-injection-text-decoration-hover"]:hover {
65
+ text-decoration: var(--playpilot-injection-text-decoration-hover);
66
+ }
67
+
68
+ &[data-used-css-variables*="--playpilot-injection-background-color"] {
69
+ background-color: var(--playpilot-injection-background-color);
70
+ }
71
+
72
+ &[data-used-css-variables*="--playpilot-injection-background-color-hover"]:hover {
73
+ background-color: var(--playpilot-injection-background-color-hover);
74
+ }
75
+ }
@@ -1,3 +1,5 @@
1
+ @use "./_functions" as *;
2
+
1
3
  :root {
2
4
  --playpilot-primary: #fa548a;
3
5
  --playpilot-dark: #101426;
@@ -9,8 +11,12 @@
9
11
  --playpilot-font-family: "Poppins", sans-serif;
10
12
  --playpilot-text-color: #fff;
11
13
  --playpilot-text-color-alt: #c8d4de;
12
- --playpilot-shadow: 0.2rem 0.15rem 0.15rem rgba(0, 0, 0, 0.2);
13
- --playpilot-shadow-large: 0.15rem 0.15rem 0.2rem rgba(0, 0, 0, 0.05), 0.2rem 0.35rem 0.35rem rgba(0, 0, 0, 0.1), 0.1rem 0.1rem 0.75rem rgba(0, 0, 0, 0.25);
14
+ --playpilot-shadow:
15
+ #{margin(0.2) margin(0.15) margin(0.15)} rgba(0, 0, 0, 0.2);
16
+ --playpilot-shadow-large:
17
+ #{margin(0.15) margin(0.15) margin(0.2)} rgba(0, 0, 0, 0.05),
18
+ #{margin(0.2) margin(0.35) margin(0.35)} rgba(0, 0, 0, 0.1),
19
+ #{margin(0.1) margin(0.1) margin(0.75)} rgba(0, 0, 0, 0.25);
14
20
  --playpilot-error: #ea5a5a;
15
21
  --playpilot-error-dark: #442533;
16
22
  }
package/src/lib/url.js ADDED
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Get the full path, including host and protocol, but excluding url params
3
+ * @returns {string}
4
+ */
5
+ export function getFullUrlPath() {
6
+ return location.protocol + '//' + location.host + location.pathname
7
+ }
package/src/main.js CHANGED
@@ -8,9 +8,10 @@ window.PlayPilotLinkInjections = {
8
8
  token: '',
9
9
  editorial_token: '',
10
10
  selector: '',
11
+ language: null,
11
12
  app: null,
12
13
 
13
- initialize(config = { token: '', selector: '', editorial_token: '' }) {
14
+ initialize(config = { token: '', selector: '', language: null, editorial_token: '' }) {
14
15
  if (!config.token) {
15
16
  console.error('An API token is required.')
16
17
  return
@@ -19,6 +20,7 @@ window.PlayPilotLinkInjections = {
19
20
  this.token = config.token
20
21
  this.editorial_token = config.editorial_token
21
22
  this.selector = config.selector
23
+ this.language = config.language
22
24
 
23
25
  if (this.app) this.destroy()
24
26
 
@@ -19,35 +19,39 @@
19
19
  if (browser) window.PlayPilotLinkInjections = { token: 'ZoAL14yqzevMyQiwckbvyetOkeIUeEDN', selector: 'article' }
20
20
  </script>
21
21
 
22
- {#key Math.random()}
23
- <article use:noClass>
24
- <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>
25
- <p use:noClass>In an interview with Epire &amp; 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>
26
- </article>
27
-
28
- {#if browser}
29
- {@render children()}
30
- {/if}
31
- {/key}
32
-
33
-
34
- <style>
35
- :global {
22
+ <div>
23
+ {#key Math.random()}
24
+ <article use:noClass>
25
+ <h1 use:noClass>Some heading</h1>
26
+ <time datetime="13:00">1 hour ago</time>
27
+ <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>
28
+ <p use:noClass>In an interview with Epire &amp; 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>
29
+ </article>
30
+
31
+ {#if browser}
32
+ {@render children()}
33
+ {/if}
34
+ {/key}
35
+ </div>
36
+
37
+ <style lang="scss">
38
+ div :global {
36
39
  article {
37
40
  min-height: 100vh;
38
- padding: 2rem;
41
+ padding: margin(2);
39
42
  background: #222;
40
43
  color: white;
41
44
  font-family: sans-serif;
42
45
  line-height: 1.4em;
43
- }
44
46
 
45
- article > :first-child {
46
- margin-top: 0;
47
- }
47
+ > :first-child {
48
+ margin-top: 0;
49
+ }
48
50
 
49
- article a {
50
- color: hotpink;
51
+ a {
52
+ color: hotpink;
53
+ font-style: italic;
54
+ }
51
55
  }
52
56
  }
53
57
  </style>