' +\n imageWithAttrs({'src': '/images/transparent.png', 'width': 60, 'height': 60, ...extras})\n rv.push({value: styles[i], src: samplesimg, label, ...extras})\n }\n return rv\n }\n\n let coptions = {}\n let smfm = this.$store.getters.smfm\n coptions.style = getStyleOptions(smfm.style, 0, smfm.defaults)\n coptions.eyeoutline = getStyleOptions(smfm.eyeoutline, 1)\n coptions.eyeball = getStyleOptions(smfm.eyeball, 2)\n coptions.gap = getStyleOptions(smfm.gap, 3)\n coptions.fill = getStyleOptions(smfm.fill, 4)\n\n return coptions\n },\n\n },\n methods: {\n detectBrowserLang() {\n const available = this.languages.map(l => l.short)\n if (this.isPro && window.localStorage.preferredLang && available.includes(window.localStorage.preferredLang)) {\n return window.localStorage.preferredLang\n }\n const singlelanguage = navigator.language || navigator.browserLanguage || navigator.userLanguage\n const clientlanguages = navigator.languages && Array.isArray(navigator.languages) && navigator.languages.length ?\n navigator.languages : [singlelanguage]\n for (let clientlanguage of clientlanguages) {\n if (available.includes(clientlanguage)) {\n return clientlanguage\n } else if (clientlanguage.length > 2 && available.includes(clientlanguage.substr(0, 2))) {\n return clientlanguage.substr(0, 2)\n }\n }\n return 'en'\n },\n\n compareSlugs(slug, currentSlug = null) {\n currentSlug = (currentSlug === null) ? this.$route.params.slug : currentSlug\n if (!slug) return false\n if (Array.isArray(slug)) {\n for (var i in slug) {\n if (slug[i] === currentSlug) return true;\n }\n return false\n } else {\n return slug === currentSlug\n }\n },\n\n acceptPrivacy() {\n window.localStorage.privacyAccepted = 1\n },\n\n isPrivacyAccepted() {\n return !!window.localStorage.privacyAccepted\n },\n\n base64encode(str) {\n // https://attacomsian.com/blog/javascript-base64-encode-decode\n\n // first we use encodeURIComponent to get percent-encoded UTF-8,\n // then we convert the percent encodings into raw bytes which\n // can be fed into btoa.\n return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g,\n function toSolidBytes(match, p1) {\n return String.fromCharCode('0x' + p1);\n }));\n },\n\n base64decode(str) {\n // Going backwards: from bytestream, to percent-encoding, to original string.\n return decodeURIComponent(atob(str).split('').map(function (c) {\n return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);\n }).join(''));\n },\n getPlanYearPrice(price, rate) {\n return Math.round(price * rate);\n },\n getPlanMonthPrice(price, rate) {\n let perMonth = (price * rate) / 12;\n if (Number.isInteger(perMonth)) {\n return perMonth;\n } else {\n return perMonth.toFixed(1);\n }\n },\n\n getArticleFromToc(shortname, lang = null, defaultValue = null) {\n if (!this.$store.getters.resourcesToc) return defaultValue\n if (!lang) lang = this.currentLang\n if (!(lang in this.$store.getters.resourcesToc)) lang = 'en'\n const f = this.$store.getters.resourcesToc[lang].filter(a => a.shortname === shortname)\n return f.length ? f[0] : defaultValue\n },\n\n getArticleUrl(shortname, lang = null, defaultValue = null) {\n const article = this.getArticleFromToc(shortname, lang)\n if (!article) return defaultValue\n if (!lang) lang = this.currentLang\n if (!(lang in this.$store.getters.resourcesToc)) lang = 'en'\n // TODO: use article.actuallang ?\n return article ? `${lang}/resources/${article.slug}` : defaultValue\n },\n\n getStaticCardUrl(shortname, lang = null, defaultValue = null) {\n if (!this.staticcards) return defaultValue\n if (!lang) lang = this.currentLang\n if (!(lang in this.staticcards)) lang = 'en'\n const f = this.staticcards[lang].filter(a => a.shortname === shortname)\n return f.length ? `${lang}/${f[0].slug}` : defaultValue\n },\n\n prepareTextWithLinks(text) {\n const replaceHref = (str, url) => {\n const replacement = this.isPro ?\n `href=\"${this.appUrl}/${url}\" target=\"_blank\" rel=\"noopener\" class=\"smfm-externallink\"` :\n `href=\"/${url}\" class=\"smfm-locallink\"`\n return str.replace(/^
]*)\\bhref=\"[^\"]*\"([^>]*)>/, ``)\n }\n const regex = pattern => new RegExp(']*\\\\bhref=\"#' + pattern + '\"[^>]*>(.*?)', 'g')\n\n if (!text) return ''\n if (text.match(/#(resource|article):([\\w]+)/)) {\n if (this.$store.getters.resourcesToc) {\n const r = (m, key) => replaceHref(m, this.getArticleUrl(key, null, '#'))\n text = text.replace(regex('resource:([\\\\w]*)'), r, text)\n text = text.replace(regex('article:([\\\\w]*)'), r, text)\n } else {\n this.$nextTick(() => this.$store.dispatch('loadResourcesToc'))\n }\n }\n const s = (m, key) => replaceHref(m, this.getStaticCardUrl(key, null, '#'))\n text = text.replace(regex('static:([\\\\w]*)'), s, text)\n const ab = (m, key) => replaceHref(m, `${this.currentLang}/${key}`)\n text = text.replace(regex('about:([\\\\w]*)'), ab, text)\n text = text.replace(regex('pro'), m => ab(m, 'product#pro'), text)\n return text\n },\n\n /**\n * Alternative URLs for the current page in the other languages (demo only)\n *\n * @param {Boolean} strictSearch\n * @returns {Object}\n */\n getAlternatives(strictSearch = true) {\n if (this.isPro || this.isAdmin) return {}\n const currentLang = this.$route.params.lang ? this.$route.params.lang : this.$i18n.currentLang\n let links = {}, shortname = null\n const resourcesToc = this.$store.getters.resourcesToc ? this.$store.getters.resourcesToc : this.$store.getters.resourcesTocStatic\n\n if (this.$route.name === 'article' && this.$route.params.slug && resourcesToc && (currentLang in resourcesToc)) {\n const findArticle = (el) => el && (this.compareSlugs(el.slug) || this.compareSlugs(el.aliases))\n const f = resourcesToc[currentLang].filter(findArticle)\n if (f.length) shortname = f[0].shortname\n } else if (this.$route.name === 'langandslug' && this.$route.params.slug\n && (currentLang in this.staticcards)) {\n const f = this.staticcards[currentLang].filter(v=>this.compareSlugs(v.slug))\n if (f.length) shortname = f[0].shortname\n }\n\n for (let code in this.languagesMenu) {\n let url = '/'+code\n let name = this.$route.name\n if (this.$route.name) {\n let params = {...this.$route.params, lang: code}\n // Special treatment for 'article' and 'langandslug' since slug is different in different languages\n if (this.$route.name === 'article') {\n if (shortname) {\n const f2 = resourcesToc[code].filter(v=>v.shortname === shortname)\n if (f2.length) params.slug = f2[0].slug\n else name = 'resources'\n } else name = 'resources'\n } else if (this.$route.name === 'langandslug') {\n if (shortname && this.staticcards[code][0].shortname === shortname) {\n // This is the first, default article, the canonical name for it is different\n name = 'lang'\n } else if (shortname) {\n const f2 = this.staticcards[code].filter(v=>v.shortname === shortname)\n if (f2.length) params.slug = f2[0].slug\n else name = 'lang'\n } else name = 'lang'\n }\n url = this.$router.resolve({name, params}).href\n }\n\n if (strictSearch && (!this.$route.name || name !== this.$route.name)) continue\n links[code] = url\n }\n\n return links\n }\n }\n}\n","import Vue from 'vue'\nimport Demo from './views/Demo'\nimport demo from './router/demo'\nimport store from './store/index-demo'\nimport vuetify from './plugins/vuetify'\nimport {i18n} from './i18n'\nimport globalMixin from \"@/mixins/globalMixin\";\nimport Router from 'vue-router';\nimport \"@/assets/shared/scss/smfm.scss\";\nimport \"@/assets/shared/scss/vuetify.scss\";\nimport \"@/assets/demo/scss/global.scss\";\n\n// https://www.npmjs.com/package/vcards-js\nwindow.Vcard = require('vcards-js')\n\n// Disable annoying message \"You are running Vue in development mode\"\nVue.config.productionTip = false\n\n// Preload all dynamic and static components\nlet files = require.context('@/components/static', true, /\\.vue$/i)\nfiles.keys().map(key => Vue.component(key.split('/').pop().split('.')[0], files(key).default))\n\nconst originalPush = Router.prototype.push;\nRouter.prototype.push = function push(location, onResolve, onReject) {\n if (onResolve || onReject) return originalPush.call(this, location, onResolve, onReject)\n return originalPush.call(this, location).catch(err => err)\n}\n\n// Special components that are used in QrStyles, this is shared between demo and pro but\n// the advanced components should be loaded only in pro:\nimport DemoEmpty from './components/demo/DemoEmpty'\nVue.component('pro-save-preset', DemoEmpty)\nVue.component('input-file', DemoEmpty)\n\nVue.mixin(globalMixin)\nnew Vue({\n router: demo,\n store,\n vuetify,\n i18n,\n render: h => h(Demo)\n}).$mount('#app')\n","export default {\n data() {\n return {\n }\n },\n computed: {\n },\n methods: {\n replaceTemplateTexts(str, captions) {\n if (!str.match(/