UNPKG

@chengkang/wildfire

Version:

A drop-in replacement for other comment systems.

257 lines (237 loc) 12.7 kB
(() => { function loadCSS (item) { if (item === undefined || item === null) { return } let url = null let loaded = null if (typeof item === 'object') { url = item.url loaded = item.loaded } else if (typeof item === 'string') { url = item } let newCSS = document.createElement('link') newCSS.rel = 'stylesheet' newCSS.type = 'text/css' newCSS.href = url newCSS.media = 'all' newCSS.onload = () => { console.log('Loaded:', url) if (loaded) { loaded() } } document.head.appendChild(newCSS) } /** * Dynamically load a list JS files sequentially. * * @param {(string|Object)[]} aList The list of JS files to load * @param {string} aList[].url The url of the JS file to load * @param {function} aList[].loaded Callback when the file is loaded * @param {function} aList[].shouldSkip Check before loading the file. * Note: if `loaded` is set, it will be called no matter this loading is skiped or not. * Skipping loading means only to skip the loading part. * * @param {function} finished Callback when all files loaded * * Example: * 1. `loadJSSequentially([ * 'https://unpkg.com/vue', * 'https://unpkg.com/vuefire' * ], () => { * console.log('Finished loading!') * })` * 2. `loadJSSequentially([ * { * url: 'https://unpkg.com/vue', * shouldSkip: () => window.Vue !== undefined, * loaded: () => { console.log('Loaded Vue!') } * }, * 'https://unpkg.com/vuefire' * ], () => { * console.log('Finished loading!') * })` */ function loadJSSequentially (aList, finished) { if (aList.length === 0) { console.log('Finished loadJSSequentially.') if (finished) { finished() } return } let item = aList.shift() let newScript = document.createElement('script') let url = null let shouldSkip = null let loaded = null if (typeof item === 'object') { ({ url, shouldSkip, loaded } = item) if (shouldSkip()) { console.log('Skipped loading', url) console.timeEnd(`\tLoading time of "${url}"\n\t`) if (loaded) { loaded() } loadJSSequentially(aList, finished) } else { newScript.onload = () => { console.log('Loaded:', url) console.timeEnd(`\tLoading time of "${url}"\n\t`) if (loaded) { loaded() } loadJSSequentially(aList, finished) } } } else if (typeof item === 'string') { url = item newScript.onload = () => { console.log('Loaded:', url) console.timeEnd(`\tLoading time of "${url}"\n\t`) loadJSSequentially(aList, finished) } } newScript.src = url document.head.appendChild(newScript) console.time(`\tLoading time of "${url}"\n\t`) } // Custom `Exception` function WfException (message) { this.message = message this.toString = () => { return this.message } } // Custom translator to replace `i18next` function WfI18n (translation = {}, fallback = null, locale = 'en') { this.translation = translation this.locale = locale this.fallback = fallback this.t = (key) => { let result = this.translation[this.locale] if (!result) { result = this.translation[this.fallback] } if (!result) { throw new WfException(`Translation for locale "${this.locale}" not found.`) } const keys = key.split('.') if (keys.length === 0) { throw new WfException('Empty translation key.') } for (let i = 0; i < keys.length; i++) { result = result[keys[i]] if (!result) { setTimeout(() => { throw new WfException(`Translation for key "${key}" not found.`) }) return key } } return result } return { t: this.t } } function startWildfire () { console.log('Starting Wildfire...') loadCSS(`https://unpkg.com/@chengkang/wildfire/dist/${databaseProvider}/static/wildfire.css`) let jsList = [] if (!window.Vue) { jsList.push('https://unpkg.com/vue') } jsList.push(databaseProvider === 'firebase' ? 'https://www.gstatic.com/firebasejs/4.6.2/firebase.js' : 'https://cdn.wilddog.com/sdk/js/2.5.17/wilddog.js') jsList.push(`https://unpkg.com/@chengkang/wildfire/dist/${databaseProvider}/wildfire.min.js`) loadJSSequentially(jsList, () => { window.wildfire.default.install(window.Vue, { databaseProvider, databaseConfig, pageURL, pageTitle, theme, locale, defaultAvatarURL }) /* eslint-disable no-new */ new window.Vue({ el: '#wildfire' }) }) } function initDom () { const initialCSS = `.wildfire_thread{font-family:'Helvetica Neue',arial,sans-serif;width: 100%;margin:0 auto}[v-cloak]{display:none}#wf-loading-modal{font-size:12px;display:flex;flex-direction:column;height:300px;color:#656c7a;justify-content:center;align-items:center}#wf-loading-modal img{width:66px;height:66px}@keyframes flickerAnimation{0%{opacity:1}40%{opacity:0}100%{opacity:1}}@-o-keyframes flickerAnimation{0%{opacity:1}40%{opacity:0}100%{opacity:1}}@-moz-keyframes flickerAnimation{0%{opacity:1}40%{opacity:0}100%{opacity:1}}@-webkit-keyframes flickerAnimation{0%{opacity:1}40%{opacity:0}100%{opacity:1}}.animate-flicker{-webkit-animation:flickerAnimation 1.5s infinite;-moz-animation:flickerAnimation 1.5s infinite;-o-animation:flickerAnimation 1.5s infinite;animation:flickerAnimation 1.5s infinite}` let initialStyle = document.createElement('style') initialStyle.type = 'text/css' if (initialStyle.styleSheet) { initialStyle.styleSheet.cssText = initialCSS } else { initialStyle.appendChild(document.createTextNode(initialCSS)) } document.head.appendChild(initialStyle) // Insert template let wildfireThreadDom = document.getElementsByClassName('wildfire_thread')[0] wildfireThreadDom.innerHTML = ` <div id="wf-loading-modal" class="wf wf-theme-${theme} animate-flicker"> <img src="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4NCjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgd2lkdGg9IjI1NnB4IiBoZWlnaHQ9IjM1MXB4IiB2aWV3Qm94PSIwIDAgMjU2IDM1MSIgdmVyc2lvbj0iMS4xIiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWlkWU1pZCI+DQogIDxkZWZzPg0KICAgIDxwYXRoIGQ9Ik0xLjI1MjczNDM3IDI4MC43MzE2NDFMMi44NTgzNDUzMyAyNzcuNjAwODU4IDEwMi4yMTExNzcgODkuMDgzMzU0NiA1OC4wNjEzMjY2IDUuNjA4MjAzM0M1NC4zOTIwMDExLTEuMjgzMDQ1NzggNDUuMDc0MTI0NSAwLjQ3MzY3NDM5OCA0My44Njk5MjAzIDguMTg3ODkwODZMMS4yNTI3MzQzNyAyODAuNzMxNjQxWiIgaWQ9InBhdGgtMSIgLz4NCiAgICA8ZmlsdGVyIHg9Ii01MCUiIHk9Ii01MCUiIHdpZHRoPSIyMDAlIiBoZWlnaHQ9IjIwMCUiIGZpbHRlclVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgaWQ9ImZpbHRlci0yIj4NCiAgICAgIDxmZUdhdXNzaWFuQmx1ciBzdGREZXZpYXRpb249IjE3LjUiIGluPSJTb3VyY2VBbHBoYSIgcmVzdWx0PSJzaGFkb3dCbHVySW5uZXIxIiAvPg0KICAgICAgPGZlT2Zmc2V0IGR4PSIwIiBkeT0iMCIgaW49InNoYWRvd0JsdXJJbm5lcjEiIHJlc3VsdD0ic2hhZG93T2Zmc2V0SW5uZXIxIiAvPg0KICAgICAgPGZlQ29tcG9zaXRlIGluPSJzaGFkb3dPZmZzZXRJbm5lcjEiIGluMj0iU291cmNlQWxwaGEiIG9wZXJhdG9yPSJhcml0aG1ldGljIiBrMj0iLTEiIGszPSIxIiByZXN1bHQ9InNoYWRvd0lubmVySW5uZXIxIiAvPg0KICAgICAgPGZlQ29sb3JNYXRyaXggdmFsdWVzPSIwIDAgMCAwIDAgICAwIDAgMCAwIDAgICAwIDAgMCAwIDAgIDAgMCAwIDAuMDYgMCIgdHlwZT0ibWF0cml4IiBpbj0ic2hhZG93SW5uZXJJbm5lcjEiIC8+DQogICAgPC9maWx0ZXI+DQogICAgPHBhdGggZD0iTTEzNC40MTcxMDMgMTQ4Ljk3NDIzNUwxNjYuNDU1NzIyIDExNi4xNjE3MzggMTM0LjQxNzEwNCA1NS4xNTQ2ODc0QzEzMS4zNzQ4MjggNDkuMzYzNTkxMSAxMjMuOTgzOTExIDQ4Ljc1NjgzNjIgMTIwLjk3MzgyOCA1NC41NjQ2NDgzTDEwMy4yNjg3NSA4OC42NzM4Mjk2IDEwMi43Mzk0MjMgOTAuNDE3NTQ3MyAxMzQuNDE3MTAzIDE0OC45NzQyMzVaIiBpZD0icGF0aC0zIiAvPg0KICAgIDxmaWx0ZXIgeD0iLTUwJSIgeT0iLTUwJSIgd2lkdGg9IjIwMCUiIGhlaWdodD0iMjAwJSIgZmlsdGVyVW5pdHM9Im9iamVjdEJvdW5kaW5nQm94IiBpZD0iZmlsdGVyLTQiPg0KICAgICAgPGZlR2F1c3NpYW5CbHVyIHN0ZERldmlhdGlvbj0iMy41IiBpbj0iU291cmNlQWxwaGEiIHJlc3VsdD0ic2hhZG93Qmx1cklubmVyMSIgLz4NCiAgICAgIDxmZU9mZnNldCBkeD0iMSIgZHk9Ii05IiBpbj0ic2hhZG93Qmx1cklubmVyMSIgcmVzdWx0PSJzaGFkb3dPZmZzZXRJbm5lcjEiIC8+DQogICAgICA8ZmVDb21wb3NpdGUgaW49InNoYWRvd09mZnNldElubmVyMSIgaW4yPSJTb3VyY2VBbHBoYSIgb3BlcmF0b3I9ImFyaXRobWV0aWMiIGsyPSItMSIgazM9IjEiIHJlc3VsdD0ic2hhZG93SW5uZXJJbm5lcjEiIC8+DQogICAgICA8ZmVDb2xvck1hdHJpeCB2YWx1ZXM9IjAgMCAwIDAgMCAgIDAgMCAwIDAgMCAgIDAgMCAwIDAgMCAgMCAwIDAgMC4wOSAwIiB0eXBlPSJtYXRyaXgiIGluPSJzaGFkb3dJbm5lcklubmVyMSIgLz4NCiAgICA8L2ZpbHRlcj4NCiAgPC9kZWZzPg0KICA8cGF0aCBkPSJNMCAyODIuOTk3NjJMMi4xMjI1MDc0NiAyODAuMDI1NiAxMDIuNTI3MzYzIDg5LjUxMTkyODQgMTAyLjczOTQyMyA4Ny40OTUxMzIzIDU4LjQ3ODgwNiA0LjM1ODE3NzExQzU0Ljc3MDYyNjktMi42MDYwNDE3OSA0NC4zMzEzMDM1LTAuODQ1MjQ1NzcxIDQzLjExNDM0ODMgNi45NTA2NTQ3M0wwIDI4Mi45OTc2MloiIGZpbGw9IiNEQjQ1MEQiIC8+DQogIDx1c2UgZmlsbD0iI0RCNDUwRCIgZmlsbC1ydWxlPSJldmVub2RkIiB4bGluazpocmVmPSIjcGF0aC0xIiAvPg0KICA8dXNlIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjEiIGZpbHRlcj0idXJsKCNmaWx0ZXItMikiIHhsaW5rOmhyZWY9IiNwYXRoLTEiIC8+DQogIDx1c2UgZmlsbD0iI0RCNDUwRCIgZmlsbC1ydWxlPSJldmVub2RkIiB4bGluazpocmVmPSIjcGF0aC0zIiAvPg0KICA8dXNlIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjEiIGZpbHRlcj0idXJsKCNmaWx0ZXItNCkiIHhsaW5rOmhyZWY9IiNwYXRoLTMiIC8+DQogIDxwb2x5Z29uIGZpbGw9IiNCMTMxMDEiIHBvaW50cz0iMCAyODIuOTk3NjIgMC45NjIwOTcxNjggMjgyLjAzMDM5NiA0LjQ1NzcxMTQ0IDI4MC42MDk1NiAxMzIuOTM1MzIzIDE1Mi42MDk1NiAxMzQuNTYzMDI1IDE0OC4xNzg1OTUgMTAyLjUxMzEyMyA4Ny4xMDQ4NTg0IiAvPg0KICA8cGF0aCBkPSJNMTM5LjEyMDk3MSAzNDcuNTUxMjY4TDI1NS4zOTU5MTYgMjgyLjcwMzY2NiAyMjIuMTkxNjk4IDc4LjIwOTMzNzNDMjIxLjE1MzA1MSA3MS44MTEyNDc4IDIxMy4zMDM2NTggNjkuMjgxODE0OSAyMDguNzI0MzE0IDczLjg2OTQzNjhMMC4wMDAyNTQ3MjYzNjggMjgyLjk5Nzg3NSAxMTUuNjA4NDU0IDM0Ny41NDU1MzZDMTIyLjkxNDY0MyAzNTEuNjI0OTc5IDEzMS44MTI4NzIgMzUxLjYyNjg5IDEzOS4xMjA5NzEgMzQ3LjU1MTI2OE0yNTQuMzU0MDg0IDI4Mi4xNTk4MzdMMjIxLjQwMTkzNyA3OS4yMTc5MzY5QzIyMC4zNzExNzUgNzIuODY4NDE4OCAyMTMuODQzNzkyIDcwLjI0MDk1NTMgMjA5LjI5OTIxMyA3NC43OTM3NUwxLjI4OTQ1MzEyIDI4Mi42MDA3ODUgMTE1LjYyNzgyNSAzNDYuNTA5NDU4QzEyMi44Nzg1NDggMzUwLjU1NzkzMSAxMzEuNzA5MjI2IDM1MC41NTk4MjcgMTM4Ljk2MTg0NiAzNDYuNTE1MTQ2TDI1NC4zNTQwODQgMjgyLjE1OTgzN1oiIGZpbGw9IiNGMzZBMzgiIC8+DQo8L3N2Zz4=" title="Wildfire - Provided by Lahk"> <span>${window._i18n.t('text.poweringWildfire')}</span> </div> <div id="wildfire" v-cloak><wildfire></wildfire></div> ` } /* Configs Validation */ function presentErrorMsg (errorCode) { const msg = window._i18n.t(errorCode) let wfLoadingModal = document.getElementById('wf-loading-modal') wfLoadingModal.className = `wf wf-theme-${theme}` // cancel flicker animation let msgSpan = wfLoadingModal.children[1] msgSpan.innerHTML = msg msgSpan.style.color = 'red' } function checkConfigs () { const wildfireThreadDom = document.getElementsByClassName('wildfire_thread') if (wildfireThreadDom.length === 0) { presentErrorMsg('error.wildfireThreadNotFound') return } else if (databaseProvider !== 'firebase' && databaseProvider !== 'wilddog') { presentErrorMsg('error.invalidDatabaseProvider') return } else if (!databaseConfig) { presentErrorMsg('error.noDatabaseConfig') return } startWildfire() } /* End of: Configs Validation */ // Get configs from global configuration object const { databaseProvider, databaseConfig, // required pageTitle = document.title, pageURL = window.location.href, locale = 'en', theme = 'light', defaultAvatarURL = 'https://cdn.rawgit.com/cheng-kang/wildfire/088cf3de/resources/wildfire-avatar.svg' } = window.wildfireConfig() // Set up custom translator for loading text & error message window._i18n = new WfI18n({ en: { error: { invalidDatabaseProvider: 'Please check your config: "databaseProvider" should be "firebase" or "wilddog".', multipleWildfireThread: '"wildfire-thread" not found, please follow the steps in documentation.', noDatabaseConfig: 'Please check your config: missing "databaseConfig"' }, text: { poweringWildfire: 'Powering Wildfire...' } }, 'zh-CN': { error: { invalidDatabaseProvider: '请检查你的配置: "databaseProvider" 应该为 "firebase" 或者 "wilddog"。', multipleWildfireThread: '未检测到 “wildfire-thread”,请依照文档所示步骤添加。', noDatabaseConfig: '请检查你的配置: 找不到 "databaseConfig"' }, text: { poweringWildfire: '正在启动 Wildfire……' } } }, 'en', locale) initDom() // Forcing a 1s loading animation setTimeout(checkConfigs, 1000) })()