crypto-slots
Version:
A minimal test server is provided, see server
953 lines (868 loc) • 28.4 kB
JavaScript
/**
* Add space between camelCase text.
*/
var unCamelCase = (string) => {
string = string.replace(/([a-z\xE0-\xFF])([A-Z\xC0\xDF])/g, '$1 $2');
string = string.toLowerCase();
return string;
};
/**
* Replaces all accented chars with regular ones
*/
var replaceAccents = (string) => {
// verifies if the String has accents and replace them
if (string.search(/[\xC0-\xFF]/g) > -1) {
string = string
.replace(/[\xC0-\xC5]/g, 'A')
.replace(/[\xC6]/g, 'AE')
.replace(/[\xC7]/g, 'C')
.replace(/[\xC8-\xCB]/g, 'E')
.replace(/[\xCC-\xCF]/g, 'I')
.replace(/[\xD0]/g, 'D')
.replace(/[\xD1]/g, 'N')
.replace(/[\xD2-\xD6\xD8]/g, 'O')
.replace(/[\xD9-\xDC]/g, 'U')
.replace(/[\xDD]/g, 'Y')
.replace(/[\xDE]/g, 'P')
.replace(/[\xE0-\xE5]/g, 'a')
.replace(/[\xE6]/g, 'ae')
.replace(/[\xE7]/g, 'c')
.replace(/[\xE8-\xEB]/g, 'e')
.replace(/[\xEC-\xEF]/g, 'i')
.replace(/[\xF1]/g, 'n')
.replace(/[\xF2-\xF6\xF8]/g, 'o')
.replace(/[\xF9-\xFC]/g, 'u')
.replace(/[\xFE]/g, 'p')
.replace(/[\xFD\xFF]/g, 'y');
}
return string;
};
var removeNonWord = (string) => string.replace(/[^0-9a-zA-Z\xC0-\xFF \-]/g, '');
const WHITE_SPACES = [
' ', '\n', '\r', '\t', '\f', '\v', '\u00A0', '\u1680', '\u180E',
'\u2000', '\u2001', '\u2002', '\u2003', '\u2004', '\u2005', '\u2006',
'\u2007', '\u2008', '\u2009', '\u200A', '\u2028', '\u2029', '\u202F',
'\u205F', '\u3000'
];
/**
* Remove chars from beginning of string.
*/
var ltrim = (string, chars) => {
chars = chars || WHITE_SPACES;
let start = 0,
len = string.length,
charLen = chars.length,
found = true,
i, c;
while (found && start < len) {
found = false;
i = -1;
c = string.charAt(start);
while (++i < charLen) {
if (c === chars[i]) {
found = true;
start++;
break;
}
}
}
return (start >= len) ? '' : string.substr(start, len);
};
/**
* Remove chars from end of string.
*/
var rtrim = (string, chars) => {
chars = chars || WHITE_SPACES;
var end = string.length - 1,
charLen = chars.length,
found = true,
i, c;
while (found && end >= 0) {
found = false;
i = -1;
c = string.charAt(end);
while (++i < charLen) {
if (c === chars[i]) {
found = true;
end--;
break;
}
}
}
return (end >= 0) ? string.substring(0, end + 1) : '';
};
/**
* Remove white-spaces from beginning and end of string.
*/
var trim = (string, chars) => {
chars = chars || WHITE_SPACES;
return ltrim(rtrim(string, chars), chars);
};
/**
* Convert to lower case, remove accents, remove non-word chars and
* replace spaces with the specified delimeter.
* Does not split camelCase text.
*/
var slugify = (string, delimeter) => {
if (delimeter == null) {
delimeter = "-";
}
string = replaceAccents(string);
string = removeNonWord(string);
string = trim(string) //should come after removeNonWord
.replace(/ +/g, delimeter) //replace spaces with delimeter
.toLowerCase();
return string;
};
/**
* Replaces spaces with hyphens, split camelCase text, remove non-word chars, remove accents and convert to lower case.
*/
var hyphenate = string => {
string = unCamelCase(string);
return slugify(string, "-");
};
const shouldRegister = name => {
return customElements.get(name) ? false : true;
};
var define = klass => {
const name = hyphenate(klass.name);
return shouldRegister(name) ? customElements.define(name, klass) : '';
};
/**
* @module CSSMixin
* @mixin Backed
* @param {class} base class to extend from
*/
const mixins = {
'mixin(--css-row)': `display: flex;
flex-direction: row;
`,
'mixin(--css-column)': `display: flex;
flex-direction: column;
`,
'mixin(--css-center)': `align-items: center;`,
'mixin(--css-header)': `height: 128px;
width: 100%;
background: var(--primary-color);
color: var(--text-color);
mixin(--css-column)`,
'mixin(--css-flex)': `flex: 1;`,
'mixin(--css-flex-2)': `flex: 2;`,
'mixin(--css-flex-3)': `flex: 3;`,
'mixin(--css-flex-4)': `flex: 4;`,
'mixin(--material-palette)': `--dark-primary-color: #00796B;
--light-primary-color: #B2DFDB;
--primary-color: #009688;
--text-color: #FFF;
--primary-text-color: #212121;
--secondary-text-color: #757575;
--divider-color: #BDBDBD;
--accent-color: #4CAF50;
--disabled-text-color: #BDBDBD;
--primary-background-color: #f9ffff;
--dialog-background-color: #FFFFFF;`,
'mixin(--css-hero)': `display: flex;
max-width: 600px;
max-height: 340px;
height: 100%;
width: 100%;
box-shadow: 3px 2px 4px 2px rgba(0,0,0, 0.15),
-2px 0px 4px 2px rgba(0,0,0, 0.15);
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
border-radius: 2px;
`,
'mixin(--css-elevation-2dp)': `
box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14),
0 1px 5px 0 rgba(0, 0, 0, 0.12),
0 3px 1px -2px rgba(0, 0, 0, 0.2);`,
'mixin(--css-elevation-3dp)': `
box-shadow: 0 3px 4px 0 rgba(0, 0, 0, 0.14),
0 1px 8px 0 rgba(0, 0, 0, 0.12),
0 3px 3px -2px rgba(0, 0, 0, 0.4);`,
'mixin(--css-elevation-4dp)': `
box-shadow: 0 4px 5px 0 rgba(0, 0, 0, 0.14),
0 1px 10px 0 rgba(0, 0, 0, 0.12),
0 2px 4px -1px rgba(0, 0, 0, 0.4);`,
'mixin(--css-elevation-6dp)': `
box-shadow: 0 6px 10px 0 rgba(0, 0, 0, 0.14),
0 1px 18px 0 rgba(0, 0, 0, 0.12),
0 3px 5px -1px rgba(0, 0, 0, 0.4);`,
'mixin(--css-elevation-8dp)': `
box-shadow: 0 8px 10px 1px rgba(0, 0, 0, 0.14),
0 3px 14px 2px rgba(0, 0, 0, 0.12),
0 5px 5px -3px rgba(0, 0, 0, 0.4);`,
'mixin(--css-elevation-12dp)': `
box-shadow: 0 12px 16px 1px rgba(0, 0, 0, 0.14),
0 4px 22px 3px rgba(0, 0, 0, 0.12),
0 6px 7px -4px rgba(0, 0, 0, 0.4);`,
'mixin(--css-elevation-16dp)': `
box-shadow: 0 16px 24px 2px rgba(0, 0, 0, 0.14),
0 6px 30px 5px rgba(0, 0, 0, 0.12),
0 8px 10px -5px rgba(0, 0, 0, 0.4);`,
'mixin(--css-elevation-24dp)': `
box-shadow: 0 24px 38px 3px rgba(0, 0, 0, 0.14),
0 9px 46px 8px rgba(0, 0, 0, 0.12),
0 11px 15px -7px rgba(0, 0, 0, 0.4);`
};
const classes = {
'apply(--css-row)': `.row {
mixin(--css-row)
}
`,
'apply(--css-column)': `.column {
mixin(--css-column)
}
`,
'apply(--css-flex)': `.flex {
mixin(--css-flex)
}
`,
'apply(--css-flex-2)': `.flex-2 {
mixin(--css-flex-2)
}`,
'apply(--css-flex-3)': `.flex-3 {
mixin(--css-flex-3)
}`,
'apply(--css-flex-4)': `.flex-4 {
mixin(--css-flex-4)
}`,
'apply(--css-center)': `.center {
align-items: center;
}
`,
'apply(--css-center-center)': `.center-center {
align-items: center;
justify-content: center;
}
`,
'apply(--css-header)': `header, .header {
mixin(--css-header)
}`,
'apply(--css-hero)': `.hero {
mixin(--css-hero)
}`,
'apply(--css-elevation-2dp)': `.elevation-2dp {
mixin(--css-elevation-2dp)
}`,
'apply(--css-elevation-3dp)': `.elevation-3dp {
mixin(--css-elevation-3dp)
}`,
'apply(--css-elevation-4dp)': `.elevation-4dp {
mixin(--css-elevation-4dp)
}`,
'apply(--css-elevation-6dp)': `.elevation-6dp {
mixin(--css-elevation-6dp)
}`,
'apply(--css-elevation-8dp)': `.elevation-8dp {
mixin(--css-elevation-8dp)
}`,
'apply(--css-elevation-12dp)': `.elevation-12dp {
mixin(--css-elevation-12dp)
}`,
'apply(--css-elevation-16dp)': `.elevation-16dp {
mixin(--css-elevation-16dp)
}`,
'apply(--css-elevation-18dp)': `.elevation-18dp {
mixin(--css-elevation-18dp)
}`
};
var CSSMixin = base => {
return class CSSMixin extends base {
get __style() {
return this.shadowRoot.querySelector('style');
}
constructor() {
super();
}
connectedCallback() {
// TODO: test
if (super.connectedCallback) super.connectedCallback();
// TODO: Implement better way to check if a renderer is used
if (this.render) this.hasRenderer = true;
else if(this.template) console.log(`Render method undefined ${this.localname}`);
this._init();
}
_init() {
if (this.hasRenderer) {
if (!this.rendered) {
return requestAnimationFrame(() => {
this._init();
});
}
}
const styles = this.shadowRoot ? this.shadowRoot.querySelectorAll('style') : this.querySelectorAll('style');
// const matches = style.innerHTML.match(/apply((.*))/g);
styles.forEach(style => {
this._applyClasses(style.innerHTML).then(innerHTML => {
if (innerHTML) this.__style.innerHTML = innerHTML;
this._applyMixins(style.innerHTML).then(innerHTML => {
if (innerHTML) this.__style.innerHTML = innerHTML;
});
}).catch(error => {
console.error(error);
});
});
// this._applyVariables(matches, style);
}
_applyMixins(string) {
const mixinInMixin = string => {
if (!string) return console.warn(`Nothing found for ${string}`);
const matches = string.match(/mixin((.*))/g);
if (matches) {
for (const match of matches) {
const mixin = mixins[match];
string = string.replace(match, mixin);
}
}
return string;
};
return new Promise((resolve, reject) => {
const matches = string.match(/mixin((.*))/g);
if (matches) for (const match of matches) {
const mixin = mixinInMixin(mixins[match]);
string = string.replace(match, mixin);
// return [
// match, mixins[match]
// ]
} resolve(string);
});
}
_applyClasses(string) {
return new Promise((resolve, reject) => {
const matches = string.match(/apply((.*))/g);
if (matches) for (const match of matches) {
// this._applyMixins(classes[match]).then(klass => {
string = string.replace(match, classes[match]);
// });
}
// this.style.innerHTML = string;
resolve(string);
});
}
}
};
/**
* @param {object} element HTMLElement
* @param {function} tagResult custom-renderer-mixin {changes: [], template: ''}
*/
var render = (element, {changes, template}) => {
if (!changes && !template) return console.warn('changes or template expected');
if (element.shadowRoot) element = element.shadowRoot;
if (!element.innerHTML) element.innerHTML = template;
for (const key of Object.keys(changes)) {
const els = Array.from(element.querySelectorAll(`[render-mixin-id="${key}"]`));
for (const el of els) {
el.innerHTML = changes[key];
}
}
return;
};
/**
*
* @example
```js
const template = html`<h1>${'name'}</h1>`;
let templateResult = template({name: 'Olivia'});
templateResult.values // property values 'Olivia'
templateResult.keys // property keys 'name'
templateResult.strings // raw template array '["<h1>", "</h1>"]'
```
*/
const html$1 = (strings, ...keys) => {
return ((...values) => {
return {strings, keys, values};
});
};
window.html = window.html || html$1;
var RenderMixin = (base = HTMLElement) =>
class RenderMixin extends base {
constructor() {
super();
this.set = [];
this.renderer = this.renderer.bind(this);
this.render = this.renderer;
}
beforeRender({values, strings, keys}) {
const dict = values[values.length - 1] || {};
const changes = {};
let template = null;
if (!this.rendered) template = strings[0];
if (values[0] !== undefined) {
keys.forEach((key, i) => {
const string = strings[i + 1];
let value = Number.isInteger(key) ? values[key] : dict[key];
if (value === undefined && Array.isArray(key)) {
value = key.join('');
} else if (value === undefined && !Array.isArray(key) && this.set[i]) {
value = this.set[i].value; // set previous value, doesn't require developer to pass all properties
} else if (value === undefined && !Array.isArray(key) && !this.set[i]) {
value = '';
}
if (!this.rendered) {
template = template.replace(/(>)[^>]*$/g, ` render-mixin-id="${key}">`);
template += `${value}${string}`;
}
if (this.set[key] && this.set[key] !== value) {
changes[key] = value;
this.set[key] = value;
} else if (!this.set[key]) {
this.set[key] = value;
changes[key] = value;
}
});
} else {
template += strings[0];
}
return {
template,
changes
};
}
renderer(properties = this.properties, template = this.template) {
if (!properties) properties = {};
else if (!this.isFlat(properties)) {
// check if we are dealing with an flat or indexed object
// create flat object getting the values from super if there is one
// default to given properties set properties[key].value
// this implementation is meant to work with 'property-mixin'
// checkout https://github.com/vandeurenglenn/backed/src/mixin/property-mixin
// while I did not test, I believe it should be compatible with PolymerElements
const object = {};
// try getting value from this.property
// try getting value from properties.property.value
// try getting value from property.property
// fallback to property
for (const key of Object.keys(properties)) {
let value;
if (this[key] !== undefined) value = this[key];
else if (properties[key] && properties[key].value !== undefined) {
value = properties[key].value;
} else {
value = '';
}
object[key] = value;
} properties = object;
}
render(this, this.beforeRender(template(properties)));
}
/**
* wether or not properties is just an object or indexed object (like {prop: {value: 'value'}})
*/
isFlat(object) {
const firstObject = object[Object.keys(object)[0]];
if (firstObject)
if (firstObject.hasOwnProperty('value') ||
firstObject.hasOwnProperty('reflect') ||
firstObject.hasOwnProperty('observer') ||
firstObject.hasOwnProperty('render'))
return false;
else return true;
}
connectedCallback() {
if (super.connectedCallback) super.connectedCallback();
if (this.render) {
this.render();
this.rendered = true;
} }
};
window.Backed = window.Backed || {};
// binding does it's magic using the propertyStore ...
window.Backed.PropertyStore = window.Backed.PropertyStore || new Map();
// TODO: Create & add global observer
var PropertyMixin = base => {
return class PropertyMixin extends base {
static get observedAttributes() {
return Object.entries(this.properties).map(entry => {if (entry[1].reflect) {return entry[0]} else return null});
}
get properties() {
return customElements.get(this.localName).properties;
}
constructor() {
super();
if (this.properties) {
for (const entry of Object.entries(this.properties)) {
const { observer, reflect, renderer } = entry[1];
// allways define property even when renderer is not found.
this.defineProperty(entry[0], entry[1]);
}
}
}
connectedCallback() {
if (super.connectedCallback) super.connectedCallback();
if (this.attributes)
for (const attribute of this.attributes) {
if (String(attribute.name).includes('on-')) {
const fn = attribute.value;
const name = attribute.name.replace('on-', '');
this.addEventListener(String(name), event => {
let target = event.path[0];
while (!target.host) {
target = target.parentNode;
}
if (target.host[fn]) {
target.host[fn](event);
}
});
}
}
}
attributeChangedCallback(name, oldValue, newValue) {
this[name] = newValue;
}
/**
* @param {function} options.observer callback function returns {instance, property, value}
* @param {boolean} options.reflect when true, reflects value to attribute
* @param {function} options.render callback function for renderer (example: usage with lit-html, {render: render(html, shadowRoot)})
*/
defineProperty(property = null, {strict = false, observer, reflect = false, renderer, value}) {
Object.defineProperty(this, property, {
set(value) {
if (value === this[`___${property}`]) return;
this[`___${property}`] = value;
if (reflect) {
if (value) this.setAttribute(property, String(value));
else this.removeAttribute(property);
}
if (observer) {
if (observer in this) this[observer]();
else console.warn(`observer::${observer} undefined`);
}
if (renderer) {
const obj = {};
obj[property] = value;
if (renderer in this) this.render(obj, this[renderer]);
else console.warn(`renderer::${renderer} undefined`);
}
},
get() {
return this[`___${property}`];
},
configurable: strict ? false : true
});
// check if attribute is defined and update property with it's value
// else fallback to it's default value (if any)
const attr = this.getAttribute(property);
this[property] = attr || this.hasAttribute(property) || value;
}
}
};
/**
* @mixin Backed
* @module utils
* @export merge
*
* some-prop -> someProp
*
* @param {object} object The object to merge with
* @param {object} source The object to merge
* @return {object} merge result
*/
var merge = (object = {}, source = {}) => {
// deep assign
for (const key of Object.keys(object)) {
if (source[key]) {
Object.assign(object[key], source[key]);
}
}
// assign the rest
for (const key of Object.keys(source)) {
if (!object[key]) {
object[key] = source[key];
}
}
return object;
};
var SelectMixin = base => {
return class SelectMixin extends PropertyMixin(base) {
static get properties() {
return merge(super.properties, {
selected: {
value: 0,
observer: '__selectedObserver__'
}
});
}
constructor() {
super();
}
get slotted() {
return this.shadowRoot ? this.shadowRoot.querySelector('slot') : this;
}
get _assignedNodes() {
const nodes = 'assignedNodes' in this.slotted ? this.slotted.assignedNodes() : this.children;
const arr = [];
for (var i = 0; i < nodes.length; i++) {
const node = nodes[i];
if (node.nodeType === 1) arr.push(node);
}
return arr;
}
/**
* @return {String}
*/
get attrForSelected() {
return this.getAttribute('attr-for-selected') || 'name';
}
set attrForSelected(value) {
this.setAttribute('attr-for-selected', value);
}
attributeChangedCallback(name, oldValue, newValue) {
if (oldValue !== newValue) {
// check if value is number
if (!isNaN(newValue)) {
newValue = Number(newValue);
}
this[name] = newValue;
}
}
/**
* @param {string|number|HTMLElement} selected
*/
select(selected) {
if (selected) this.selected = selected;
// TODO: fix selectedobservers
if (this.multi) this.__selectedObserver__();
}
next(string) {
const index = this.getIndexFor(this.currentSelected);
if (index !== -1 && index >= 0 && this._assignedNodes.length > index &&
(index + 1) <= this._assignedNodes.length - 1) {
this.selected = this._assignedNodes[index + 1];
}
}
previous() {
const index = this.getIndexFor(this.currentSelected);
if (index !== -1 && index >= 0 && this._assignedNodes.length > index &&
(index - 1) >= 0) {
this.selected = this._assignedNodes[index - 1];
}
}
getIndexFor(element) {
if (element && element instanceof HTMLElement === false)
return console.error(`${element} is not an instanceof HTMLElement`);
return this._assignedNodes.indexOf(element || this.selected);
}
_updateSelected(selected) {
selected.classList.add('custom-selected');
if (this.currentSelected && this.currentSelected !== selected) {
this.currentSelected.classList.remove('custom-selected');
}
this.currentSelected = selected;
}
/**
* @param {string|number|HTMLElement} change.value
*/
__selectedObserver__(value) {
const type = typeof this.selected;
if (Array.isArray(this.selected)) {
for (const child of this._assignedNodes) {
if (child.nodeType === 1) {
if (this.selected.indexOf(child.getAttribute(this.attrForSelected)) !== -1) {
child.classList.add('custom-selected');
} else {
child.classList.remove('custom-selected');
}
}
}
return;
} else if (type === 'object') return this._updateSelected(this.selected);
else if (type === 'string') {
for (const child of this._assignedNodes) {
if (child.nodeType === 1) {
if (child.getAttribute(this.attrForSelected) === this.selected) {
return this._updateSelected(child);
}
}
}
} else {
// set selected by index
const child = this._assignedNodes[this.selected];
if (child && child.nodeType === 1) this._updateSelected(child);
// remove selected even when nothing found, better to return nothing
}
}
}
};
/**
* @extends HTMLElement
*/
class CustomPages extends SelectMixin(HTMLElement) {
constructor() {
super();
this.slotchange = this.slotchange.bind(this);
this.attachShadow({mode: 'open'});
this.shadowRoot.innerHTML = `
<style>
:host {
flex: 1;
position: relative;
--primary-background-color: #ECEFF1;
overflow: hidden;
}
::slotted(*) {
display: flex;
position: absolute;
opacity: 0;
pointer-events: none;
top: 0;
left: 0;
right: 0;
bottom: 0;
transition: transform ease-out 160ms, opacity ease-out 60ms;
/*transform: scale(0.5);*/
transform-origin: left;
}
::slotted(.animate-up) {
transform: translateY(-120%);
}
::slotted(.animate-down) {
transform: translateY(120%);
}
::slotted(.custom-selected) {
opacity: 1;
pointer-events: auto;
transform: translateY(0);
transition: transform ease-in 160ms, opacity ease-in 320ms;
max-height: 100%;
max-width: 100%;
}
</style>
<!-- TODO: scale animation, ace doesn't resize that well ... -->
<div class="wrapper">
<slot></slot>
</div>
`;
}
connectedCallback() {
super.connectedCallback();
this.shadowRoot.querySelector('slot').addEventListener('slotchange', this.slotchange);
}
isEvenNumber(number) {
return Boolean(number % 2 === 0)
}
/**
* set animation class when slot changes
*/
slotchange() {
let call = 0;
for (const child of this.slotted.assignedNodes()) {
if (child && child.nodeType === 1) {
child.style.zIndex = 99 - call;
if (this.isEvenNumber(call++)) {
child.classList.add('animate-down');
} else {
child.classList.add('animate-up');
}
this.dispatchEvent(new CustomEvent('child-change', {detail: child}));
}
}
}
}customElements.define('custom-pages', CustomPages);
window.slotsAPI = window.slotsAPI || {};
window.slotsAPI.amount = {
get: async () => Promise.resolve(localStorage.getItem('amount')),
set: async amount => Promise.resolve(localStorage.setItem('amount', amount))
};
var slots = define(class CryptoSlots extends CSSMixin(RenderMixin(HTMLElement)) {
get pages() {
return this.shadowRoot.querySelector('custom-pages')
}
constructor() {
super();
window.slots = new Map();
this.attachShadow({mode: 'open'});
}
connectedCallback() {
if (super.connectedCallback) super.connectedCallback();
(async () => {
globalThis.loadGame = this.loadGame.bind(this);
this.backToSlots();
this.amount = await slotsAPI.amount.get();
this.shadowRoot.querySelector('.amount').innerHTML = `${this.amount} LFC`;
document.addEventListener('spin-end', async () => {
this.amount = await slotsAPI.amount.get();
this.shadowRoot.querySelector('.amount').innerHTML = `${this.amount} LFC`;
});
})();
}
async loadGame(game) {
const tag = `${game}-slot`;
if (!await customElements.get(tag)) {
await import(`./slots/${tag}.js`);
}
const el = document.createElement(tag);
el.dataset.route = game;
window.slots.set('game', game === 'default' ? 'top-ten' : game);
this.pages.appendChild(el);
this.pages.select(game);
}
async backToSlots() {
if (!await customElements.get('slots-view')) {
await import('./chunk-b16f93d6.js');
}
const el = document.createElement('slots-view');
el.dataset.route = 'slots';
this.pages.appendChild(el);
this.pages.select('slots');
}
get template() {
return html`
<style>
:host {
display: flex;
flex-direction: column;
height: 100%;
width: 100%;
background: #022e44;
color: #fff;
font-size: 16px;
position: relative;
}
header {
display: flex;
height: 56px;
width: 100%;
align-items: center;
background: #042434;
padding: 0 12px;
box-sizing: border-box;
}
.hero {
mixin(--css-hero)
max-height: 500px;
overflow: hidden;
}
a {
text-decoration: none;
cursor: pointer;
color: #c5cae9;
}
footer {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
height: 40px;
font-size: 12px;
}
apply(--css-flex)
</style>
<header>
<span class="flex"></span>
<span class="amount"></span>
<span class="flex"></span>
</header>
<custom-pages attr-for-selected="data-route">
<slots-view></slots-view>
<default-slot></default-slot>
<onebythree-slot></onebythree-slot>
</custom-pages>
<footer>
<span>© 2019 Glenn Vandeuren. Code licensed under the <a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/">CC-BY-NC-SA-4.0</a> License.</span>
</footer>`;
}
});
export default slots;