@everymatrix/casino-jackpot-banner 1.16.1

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.
@@ -0,0 +1,48 @@
1
+ /* http://meyerweb.com/eric/tools/css/reset/
2
+ v2.0 | 20110126
3
+ License: none (public domain)
4
+ */
5
+
6
+ html, body, div, span, applet, object, iframe,
7
+ h1, h2, h3, h4, h5, h6, p, blockquote, pre,
8
+ a, abbr, acronym, address, big, cite, code,
9
+ del, dfn, em, img, ins, kbd, q, s, samp,
10
+ small, strike, strong, sub, sup, tt, var,
11
+ b, u, i, center,
12
+ dl, dt, dd, ol, ul, li,
13
+ fieldset, form, label, legend,
14
+ table, caption, tbody, tfoot, thead, tr, th, td,
15
+ article, aside, canvas, details, embed,
16
+ figure, figcaption, footer, header, hgroup,
17
+ menu, nav, output, ruby, section, summary,
18
+ time, mark, audio, video {
19
+ margin: 0;
20
+ padding: 0;
21
+ border: 0;
22
+ font-size: 100%;
23
+ font: inherit;
24
+ vertical-align: baseline;
25
+ }
26
+ /* HTML5 display-role reset for older browsers */
27
+ article, aside, details, figcaption, figure,
28
+ footer, header, hgroup, menu, nav, section {
29
+ display: block;
30
+ }
31
+ body {
32
+ line-height: 1;
33
+ }
34
+ ol, ul {
35
+ list-style: none;
36
+ }
37
+ blockquote, q {
38
+ quotes: none;
39
+ }
40
+ blockquote:before, blockquote:after,
41
+ q:before, q:after {
42
+ content: '';
43
+ content: none;
44
+ }
45
+ table {
46
+ border-collapse: collapse;
47
+ border-spacing: 0;
48
+ }
@@ -0,0 +1,59 @@
1
+ import svelte from 'rollup-plugin-svelte';
2
+ import commonjs from '@rollup/plugin-commonjs';
3
+ import resolve from '@rollup/plugin-node-resolve';
4
+ import serve from 'rollup-plugin-dev-server';
5
+ import livereload from 'rollup-plugin-livereload';
6
+ import { terser } from 'rollup-plugin-terser';
7
+ import sveltePreprocess from 'svelte-preprocess';
8
+ import typescript from '@rollup/plugin-typescript';
9
+ const production = process.env.NODE_ENV === 'production';
10
+ const dev = process.env.NODE_ENV === 'development';
11
+
12
+ export default {
13
+ input: 'src/index.ts',
14
+ output: {
15
+ sourcemap: true,
16
+ format: 'umd',
17
+ name: 'app',
18
+ file: 'dist/casino-jackpot-banner.js'
19
+ },
20
+ plugins: [
21
+ svelte({
22
+ preprocess: sveltePreprocess(),
23
+ compilerOptions: {
24
+ // enable run-time checks when not in production
25
+ customElement: true,
26
+ dev: !production
27
+ }
28
+ }),
29
+ commonjs(),
30
+ resolve({
31
+ browser: true,
32
+ dedupe: ['svelte']
33
+ }),
34
+ dev && serve({
35
+ open: true,
36
+ verbose: true,
37
+ allowCrossOrigin: true,
38
+ historyApiFallback: false,
39
+ host: 'localhost',
40
+ port: 5050,
41
+ }),
42
+ dev && livereload({ watch: ['', 'dist'] }),
43
+ typescript({
44
+ sourceMap: !production,
45
+ inlineSources: !production,
46
+ }),
47
+ // If we're building for production (npm run build
48
+ // instead of npm run dev), minify
49
+ production &&
50
+ terser({
51
+ output: {
52
+ comments: "all"
53
+ },
54
+ })
55
+ ],
56
+ watch: {
57
+ clearScreen: false
58
+ }
59
+ };
@@ -0,0 +1,515 @@
1
+ <svelte:options tag={null} />
2
+ <script lang="ts">
3
+
4
+ import { isMobile } from 'rvhelper';
5
+ import { _, addNewMessages, setLocale } from './i18n';
6
+ import { TRANSLATIONS } from './translations';
7
+
8
+ // Type decides the visual structure of the widget, as per the requirements
9
+ export let visualstructure:string = '';
10
+ export let title:string = '';
11
+ // Exposing these attributes individually as the links could get pretty long and difficult to discern for a human parsing the HTML.
12
+ export let caticon0:string = '';
13
+ export let caticon1:string = '';
14
+ export let caticon2:string = '';
15
+ export let caticon3:string = '';
16
+ // Exposing the labels as a single string since the input is short and concise
17
+ export let iconlabels:string = '';
18
+ export let backgroundsrc:string='';
19
+ export let currency:string='';
20
+ // If mobile background is not given, the desktop one is used instead
21
+ export let backgroundsrcmobile:string='';
22
+ export let titlelogopath = '';
23
+ export let lang:string ='en';
24
+ export let clientstyling:string = '';
25
+ export let clientstylingurl:string = '';
26
+ export let translationurl:string = '';
27
+
28
+ export let endpoint:string = '';
29
+ export let gameid:string = ''; // which game's data to pull
30
+
31
+
32
+ let userAgent:any = window.navigator.userAgent;
33
+ let mobileView:boolean = false;
34
+ let tabletView:boolean = false;
35
+
36
+ let vendorIcon:string = ``;
37
+
38
+ let jackpotValues:Array<number> = [];
39
+ let validGameIds:Array<string> = [];
40
+ let formattedJackpotValues:Array<string> = [];
41
+ let formattedLabels:Array<string> = [];
42
+ let caticons:Array<string> =[];
43
+ let backgroundImg:string = "";
44
+ let titleLogoImg:string = "";
45
+ let customStylingContainer: HTMLElement;
46
+ let displayNone:boolean = false;
47
+
48
+ let isLoading:boolean = true;
49
+ let hasErrors:boolean = false;
50
+ let receivedJackpotData:any;
51
+
52
+ const setTranslationUrl = ():void => {
53
+ let url:string = translationurl;
54
+
55
+ fetch(url).then((res:any) => res.json())
56
+ .then((res) => {
57
+ Object.keys(res).forEach((item:any):void => {
58
+ addNewMessages(item, res[item]);
59
+ });
60
+ }).catch((err:any) => {
61
+ console.log(err);
62
+ });
63
+ }
64
+
65
+ Object.keys(TRANSLATIONS).forEach((item:any) => {
66
+ addNewMessages(item, TRANSLATIONS[item]);
67
+ });
68
+
69
+ const availableTypes = ['typeSpread', 'typeLeft'];
70
+
71
+ const setJackpotValues = (jackpotData:any):void => {
72
+ const valuesObj = jackpotData?.items[0]?.additionalData;
73
+
74
+ if(valuesObj) Object.values(valuesObj).forEach(value => jackpotValues.push(+value));
75
+ }
76
+
77
+ const setVendorLogo = (jackpotLogoData):void => {
78
+ vendorIcon = jackpotLogoData?.items[0]?.vendorLogo || '';
79
+ }
80
+
81
+ const getJackpotData = () => {
82
+ if(endpoint && lang && currency) {
83
+ let url:URL = new URL(`${endpoint}/casino/jackpots`);
84
+
85
+ url.searchParams.append('filter', `id=${gameid}`);
86
+ url.searchParams.append('language', lang);
87
+ url.searchParams.append('additionalDataCurrency', currency);
88
+
89
+ fetch(url.href)
90
+ .then((res:any) =>
91
+ res.json().then((data:any) => {
92
+ isLoading = false;
93
+
94
+ receivedJackpotData = data;
95
+
96
+ if(validGameIds.includes(gameid)) {
97
+ setJackpotValues(receivedJackpotData);
98
+ setVendorLogo(receivedJackpotData);
99
+ }
100
+ }).catch((err:any) => {
101
+ hasErrors = true;
102
+ isLoading = false;
103
+
104
+ console.error(err);
105
+ })
106
+ )
107
+ }
108
+ }
109
+
110
+ const getJackpotConfig = () => {
111
+ let url:URL = new URL(`${endpoint}/casino/jackpots/config`);
112
+
113
+ fetch(url.href)
114
+ .then((res:any) => res.json())
115
+ .then((configData:any) =>{
116
+ validGameIds = configData.egtJackpotIds.map(id => id);
117
+ })
118
+ }
119
+
120
+ const initialize = ():void => {
121
+ // Also checking that ratio is above 1 to work with edge cases where the ratio is landscape but the userAgent comes back as mobile
122
+ mobileView = isMobile(userAgent) && window.innerHeight / window.innerWidth > 1;
123
+ tabletView = isTablet();
124
+
125
+ // Format jackpot values & labels
126
+ if(jackpotValues.length !== 0) {
127
+ formattedJackpotValues = formatJackpotValues(jackpotValues);
128
+ }
129
+
130
+ formattedLabels = iconlabels ? iconlabels.split(',').map(label => label.trim().toUpperCase()) : new Array(4).fill('');
131
+
132
+ // check if visualtype passed is correct (longer string prone to errors. maybe shorten the strings to be less descriptive, but also less error prone?)
133
+ if (availableTypes.includes(visualstructure.trim())) {
134
+ visualstructure = visualstructure.trim();
135
+ } else {
136
+ console.warn(`Invalid Attribute Value: "${visualstructure}" is not a valid value for the "visualstructure" attribute. Defaulting to "${availableTypes[0]}"`);
137
+ visualstructure = availableTypes[0];
138
+ }
139
+
140
+ backgroundImg = backgroundsrcmobile && mobileView && !tabletView ? backgroundsrcmobile : backgroundsrc;
141
+ titleLogoImg = titlelogopath;
142
+ caticons = [caticon0, caticon1, caticon2, caticon3]; // This isn't pretty, we could move to caticons as a list, but it'll look bad in the DOM since they can hold long URLs
143
+ }
144
+
145
+ const formatJackpotValues = (jackpotValues:Array<number>):Array<string> => {
146
+ return jackpotValues.map(val => Intl.NumberFormat("en-US", {maximumFractionDigits: 2}).format(val));
147
+ };
148
+
149
+
150
+ const isTablet = ():boolean => {
151
+ const ratio = window.innerHeight / window.innerWidth;
152
+ return 0.75 < ratio && ratio < 1.6;
153
+ };
154
+
155
+ const handleClick = ():void => {
156
+ window.postMessage({ type: 'JackpotBannerClicked', gameid }, window.location.href);
157
+
158
+ //Analytics event
159
+ if(typeof gtag == 'function'){
160
+ gtag('event', 'JackpotBannerClicked', {
161
+ 'context': 'JackpotBanner',
162
+ 'gameId' : `${gameid}`
163
+ });
164
+ }
165
+ }
166
+
167
+ const setClientStyling = ():void => {
168
+ let sheet = document.createElement('style');
169
+ sheet.innerHTML = clientstyling;
170
+ customStylingContainer.appendChild(sheet);
171
+ }
172
+
173
+ const setClientStylingURL = ():void => {
174
+ displayNone = true;
175
+
176
+ let url:URL = new URL(clientstylingurl);
177
+ let cssFile:HTMLElement = document.createElement('style');
178
+
179
+ fetch(url.href)
180
+ .then((res:any) => res.text())
181
+ .then((data:any) => {
182
+ cssFile.innerHTML = data
183
+
184
+ setTimeout(() => { customStylingContainer.appendChild(cssFile) }, 1);
185
+ setTimeout(() => { displayNone = false; }, 500);
186
+ });
187
+ }
188
+
189
+ const setActiveLanguage = () => {
190
+ setLocale(lang);
191
+ }
192
+
193
+ $: endpoint && getJackpotConfig();
194
+ $: customStylingContainer && clientstyling && setClientStyling();
195
+ $: customStylingContainer && clientstylingurl && setClientStylingURL();
196
+ $: (endpoint && lang && currency) && validGameIds.length > 0 && getJackpotData();
197
+ $: currency && lang && !isLoading && initialize();
198
+ $: lang && setActiveLanguage();
199
+ $: translationurl && setTranslationUrl();
200
+ </script>
201
+
202
+ <div bind:this={customStylingContainer}>
203
+ {#if hasErrors}
204
+ <p>500 Server error</p>
205
+ {:else if isLoading}
206
+ <div class="LoadingSpinner" part="LoadingSpinner"></div>
207
+ {:else}
208
+ <div class={displayNone ? 'DisplayNone' : ''} part="{displayNone ? 'DisplayNone' : ''}">
209
+ <div class="jackpotBanner" on:click={() => handleClick()}>
210
+ <div class="mainWrapper {visualstructure} {mobileView && !tabletView ? 'mobile' : tabletView ? 'tablet' : ''}" style="background: url({backgroundImg});" >
211
+ <div class="vendorLogo"><img src="{vendorIcon}" alt=""></div>
212
+ <div class="displayContainer">
213
+ {#if !tabletView}
214
+ {#each Array(4) as _, i}
215
+ <div class="displayCard"><div class="iconWrapper"><img class="displayCardIcon" src="{caticons[i]}" alt=""><span class="cardLabel">{formattedLabels[i]}</span></div><span class="cardTextWrapper"><span class="jackpotValue">{formattedJackpotValues[i] ?? ''}</span> <span class="jackpot-currency">{currency}</span></span></div>
216
+ {/each}
217
+ {:else}
218
+ <!-- Building these by looping would be a bit unreadable and require a higher scope counting variable for correct array access. I can do that, but this approach, while not DRY, is easier to parse -->
219
+ <div class="pairWrapper">
220
+ <div class="displayCard"><div class="iconWrapper"><img class="displayCardIcon" src="{caticons[0]}" alt=""><span class="cardLabel">{formattedLabels[0]}</span></div><span class="cardTextWrapper"><span class="jackpotValue">{formattedJackpotValues[0]}</span> <span class="jackpot-currency">{currency}</span></span></div>
221
+ <div class="displayCard"><div class="iconWrapper"><img class="displayCardIcon" src="{caticons[1]}" alt=""><span class="cardLabel">{formattedLabels[1]}</span></div><span class="cardTextWrapper"><span class="jackpotValue">{formattedJackpotValues[1]}</span> <span class="jackpot-currency">{currency}</span></span></div>
222
+ </div>
223
+ <div class="pairWrapper">
224
+ <div class="displayCard"><div class="iconWrapper"><img class="displayCardIcon" src="{caticons[2]}" alt=""><span class="cardLabel">{formattedLabels[2]}</span></div><span class="cardTextWrapper"><span class="jackpotValue">{formattedJackpotValues[2]}</span> <span class="jackpot-currency">{currency}</span></span></div>
225
+ <div class="displayCard"><div class="iconWrapper"><img class="displayCardIcon" src="{caticons[3]}" alt=""><span class="cardLabel">{formattedLabels[3]}</span></div><span class="cardTextWrapper"><span class="jackpotValue">{formattedJackpotValues[3]}</span> <span class="jackpot-currency">{currency}</span></span></div>
226
+ </div>
227
+ {/if}
228
+ </div>
229
+ <div class="title">
230
+ <img src={titleLogoImg} alt=""/>
231
+ </div>
232
+ </div>
233
+ </div>
234
+ </div>
235
+ {/if}
236
+ </div>
237
+
238
+ <style lang="scss">
239
+ :host {
240
+ font-family: "Roboto", Helvetica, Arial, sans-serif, "Apple Color Emoji",
241
+ "Segoe UI Emoji";
242
+ color: #fff;
243
+ }
244
+
245
+ *,
246
+ *::before,
247
+ *::after {
248
+ margin: 0;
249
+ padding: 0;
250
+ list-style: none;
251
+ text-decoration: none;
252
+ outline: none;
253
+ box-sizing: border-box;
254
+ }
255
+
256
+ .mainWrapper {
257
+ padding: 33px 3vw;
258
+ // For some reason the browser overrides these values unless !important, despite them not being set anywhere else in this document
259
+ background-repeat: no-repeat !important;
260
+ background-size: cover !important;
261
+ border-radius: 7px;
262
+
263
+ display: grid;
264
+ grid-gap: 0.5rem;
265
+
266
+ z-index: 0;
267
+ }
268
+
269
+ .vendorLogo {
270
+ grid-area: vendorLogo;
271
+ height: 24px;
272
+ width: auto;
273
+
274
+ display: flex;
275
+ align-items: center;
276
+
277
+ img {
278
+ height: 100%;
279
+ width: auto;
280
+ }
281
+ }
282
+
283
+ .displayCard {
284
+ position: relative;
285
+ background: #1c2136;
286
+ height: Max(0.5vw, 1rem);
287
+ padding: 10px 20px 10px 24px;
288
+ margin: 0 8px;
289
+
290
+ box-sizing: content-box;
291
+ border-radius: 7px;
292
+
293
+ display: flex;
294
+ justify-content: center;
295
+ align-items: center;
296
+
297
+ border: 1px solid #ffc052;
298
+
299
+ .iconWrapper {
300
+ width: 44px;
301
+ height: 44px;
302
+
303
+ position: absolute;
304
+ left: -24px;
305
+
306
+ display: flex;
307
+ flex-direction: column;
308
+ justify-content: flex-start;
309
+ align-items: center;
310
+ }
311
+
312
+ .displayCardIcon {
313
+ height: 100%;
314
+ width: auto;
315
+ }
316
+
317
+ .cardLabel {
318
+ font-size: 0.8rem;
319
+ font-style: italic;
320
+ }
321
+
322
+ .cardTextWrapper {
323
+ display: flex;
324
+ justify-content: center;
325
+ flex-wrap: nowrap;
326
+ align-items: flex-end;
327
+ }
328
+ }
329
+
330
+ .title {
331
+ grid-area: title;
332
+ display: flex;
333
+ align-items: center;
334
+ img {
335
+ height: 50px;
336
+ }
337
+ }
338
+
339
+ .displayContainer {
340
+ grid-area: displayArea;
341
+ padding: 0 0 0 20px;
342
+
343
+ display: flex;
344
+ gap: 40px;
345
+ justify-content: space-between;
346
+ align-items: center;
347
+
348
+ .jackpotValue {
349
+ font-size: 1.2rem;
350
+ font-weight: 400;
351
+ display: flex;
352
+ align-items: flex-end;
353
+ }
354
+
355
+ .jackpot-currency {
356
+ font-weight: 400;
357
+ font-style: italic;
358
+ font-size: 8px;
359
+
360
+ padding-left: 2px;
361
+ display: flex;
362
+ align-items: flex-end;
363
+ }
364
+ }
365
+
366
+ .pairWrapper {
367
+ width: 100%;
368
+ padding-left: 4%;
369
+ display: flex;
370
+ justify-content: flex-start;
371
+ gap: 30px;
372
+ }
373
+
374
+ .typeSpread {
375
+ grid-template-columns: 1fr 2.5fr 1fr;
376
+ grid-template-areas: "title displayArea vendorLogo";
377
+ align-items: center;
378
+
379
+ &.mobile {
380
+ grid-template-areas:
381
+ "title"
382
+ "vendorLogo"
383
+ "displayArea";
384
+ margin-top: 25px;
385
+ }
386
+
387
+ .pairWrapper {
388
+ justify-content: center;
389
+ }
390
+
391
+ .vendorLogo {
392
+ justify-content: flex-end;
393
+ }
394
+
395
+ .title {
396
+ justify-content: flex-start;
397
+ }
398
+
399
+ .displayContainer {
400
+ justify-content: space-evenly;
401
+ }
402
+ }
403
+
404
+ .typeLeft {
405
+ grid-template-columns: 1fr 4fr;
406
+ grid-template-areas:
407
+ "title displayArea"
408
+ "vendorLogo displayArea";
409
+ padding: 44px 33px;
410
+
411
+ &.mobile {
412
+ grid-template-areas:
413
+ "title"
414
+ "vendorLogo"
415
+ "displayArea";
416
+ padding: 33px;
417
+
418
+ .displayContainer {
419
+ margin-top: 50px;
420
+ gap:2.6rem;
421
+ }
422
+ }
423
+
424
+ .displayContainer {
425
+ padding-left: 2rem;
426
+ width: auto;
427
+ gap: 2rem;
428
+ }
429
+
430
+ .pairWrapper {
431
+ justify-content: space-evenly;
432
+ }
433
+
434
+ .title {
435
+ align-items: flex-end;
436
+ }
437
+
438
+ .vendorLogo {
439
+ height: 24px;
440
+ width: auto;
441
+ }
442
+ }
443
+
444
+ .mobile {
445
+ grid-template-columns: 1fr;
446
+ grid-gap: 1rem;
447
+
448
+ .title {
449
+ justify-content: center;
450
+ align-items: center;
451
+ img {
452
+ height: 80px;
453
+ }
454
+ }
455
+
456
+ .displayContainer {
457
+ // this padding coutners the left movement of the absolutely positioned card icons on mobile, in order to make them look centered, despite being offset
458
+ padding-left: 26px;
459
+ flex-direction: column;
460
+ flex-wrap: nowrap;
461
+ justify-content: center;
462
+ align-items: center;
463
+ }
464
+
465
+ .vendorLogo {
466
+ justify-content: center;
467
+ align-items: center;
468
+ }
469
+
470
+ .displayCard {
471
+ width: 40vw;
472
+ }
473
+ }
474
+
475
+ .tablet {
476
+ .displayContainer {
477
+ flex-wrap: wrap;
478
+ }
479
+ }
480
+
481
+ .DisplayNone {
482
+ display: none;
483
+ }
484
+
485
+ .loadingPrompt {
486
+ color: #333;
487
+ }
488
+
489
+ .LoadingSpinner {
490
+ display: block;
491
+ width: 80px;
492
+ height: 80px;
493
+ margin: 0 auto
494
+ }
495
+ .LoadingSpinner:after {
496
+ content: " ";
497
+ display: block;
498
+ width: 64px;
499
+ height: 64px;
500
+ margin: 8px;
501
+ border-radius: 50%;
502
+ border: 6px solid var(--emfe-w-color-primary, #D0046C);
503
+ border-color: var(--emfe-w-color-primary, #D0046C) transparent var(--emfe-w-color-primary, #D0046C) transparent;
504
+ animation: LoadingSpinner 1.2s linear infinite;
505
+ }
506
+ @keyframes LoadingSpinner {
507
+ 0% {
508
+ transform: rotate(0deg);
509
+ }
510
+ 100% {
511
+ transform: rotate(360deg);
512
+ }
513
+ }
514
+
515
+ </style>
package/src/i18n.js ADDED
@@ -0,0 +1,27 @@
1
+ import {
2
+ dictionary,
3
+ locale,
4
+ addMessages,
5
+ _
6
+ } from 'svelte-i18n';
7
+
8
+ function setupI18n({ withLocale: _locale, translations }) {
9
+ locale.subscribe((data) => {
10
+ if (data == null) {
11
+ dictionary.set(translations);
12
+ locale.set(_locale);
13
+ }
14
+ }); // maybe we will need this to make sure that the i18n is set up only once
15
+ /*dictionary.set(translations);
16
+ locale.set(_locale);*/
17
+ }
18
+
19
+ function addNewMessages(lang, dict) {
20
+ addMessages(lang, dict);
21
+ }
22
+
23
+ function setLocale(_locale) {
24
+ locale.set(_locale);
25
+ }
26
+
27
+ export { _, setupI18n, addNewMessages, setLocale };
package/src/index.ts ADDED
@@ -0,0 +1,4 @@
1
+ import CasinoJackpotBanner from './CasinoJackpotBanner.svelte';
2
+
3
+ !customElements.get('casino-jackpot-banner') && customElements.define('casino-jackpot-banner', CasinoJackpotBanner);
4
+ export default CasinoJackpotBanner;
@@ -0,0 +1,23 @@
1
+ export const TRANSLATIONS = {
2
+ "en": {
3
+ "loadingText": "Loading, please wait..."
4
+ },
5
+ "zh-hk": {
6
+ "loadingText": "加载请稍候 ..."
7
+ },
8
+ "fr": {
9
+ "loadingText": "Chargement, veuillez patienter..."
10
+ },
11
+ "ro": {
12
+ "loadingText": "Se incarca, va rugam asteptati..."
13
+ },
14
+ "es": {
15
+ "loadingText": "Cargando"
16
+ },
17
+ "pt": {
18
+ "loadingText": "Carregando, por favor espere..."
19
+ },
20
+ "de": {
21
+ "loadingText": "Laden, bitte warten..."
22
+ }
23
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "extends": "@tsconfig/svelte/tsconfig.json",
3
+
4
+ "include": ["src/**/*"],
5
+ "exclude": ["node_modules/*", "__sapper__/*", "public/*"]
6
+ }