diff --git a/quasar.extensions.json b/quasar.extensions.json new file mode 100644 index 0000000..97d1675 --- /dev/null +++ b/quasar.extensions.json @@ -0,0 +1,6 @@ +{ + "@quasar/typescript": { + "webpack": "plugin", + "rename": true + } +} \ No newline at end of file diff --git a/src/boot/axios.ts b/src/boot/axios.ts new file mode 100644 index 0000000..11b8239 --- /dev/null +++ b/src/boot/axios.ts @@ -0,0 +1,5 @@ +import axios from 'axios' + +export default ({ Vue }) => { + Vue.prototype.$axios = axios +} diff --git a/src/boot/dialog.ts b/src/boot/dialog.ts new file mode 100644 index 0000000..5b145d3 --- /dev/null +++ b/src/boot/dialog.ts @@ -0,0 +1,5 @@ +import Dialog from 'quasar' + +export default ({ Vue }) => { + Vue.use(Dialog) +} diff --git a/src/boot/dragula.ts b/src/boot/dragula.ts new file mode 100644 index 0000000..76abac7 --- /dev/null +++ b/src/boot/dragula.ts @@ -0,0 +1,10 @@ +import Vue from 'vue' +import { Vue2Dragula } from 'vue2-dragula' + +export default ({ Vue }) => { + Vue.use(Vue2Dragula, { + logging: { + service: false // to only log methods in service (DragulaService) + } + }) +} diff --git a/src/boot/error-handler.ts b/src/boot/error-handler.ts new file mode 100644 index 0000000..77e6339 --- /dev/null +++ b/src/boot/error-handler.ts @@ -0,0 +1,7 @@ +// import something here +import errorHandler from '../error-handler' +// leave the export, even if you don't use it +export default ({ app, router, Vue }) => { + // something to do + Vue.prototype.$errorHandler = errorHandler +} diff --git a/src/boot/globalroutines.ts b/src/boot/globalroutines.ts new file mode 100644 index 0000000..b3ff5a0 --- /dev/null +++ b/src/boot/globalroutines.ts @@ -0,0 +1,8 @@ +import globalroutines from '../globalroutines' + +export default ({ app, router, store, Vue }) => { + // something to do + Vue.prototype.$globalroutines = globalroutines +} + + diff --git a/src/boot/guard.ts b/src/boot/guard.ts new file mode 100644 index 0000000..45dde93 --- /dev/null +++ b/src/boot/guard.ts @@ -0,0 +1,89 @@ +// import something here + +// leave the export, even if you don't use it +export default ({ app, router, store, Vue }) => { + // something to do + + // ****************************************** + // *** Per non permettere di accedere alle pagine in cui è necessario essere Loggati ! *** + // ****************************************** + + // Creates a `nextMiddleware()` function which not only +// runs the default `next()` callback but also triggers +// the subsequent Middleware function. + function nextFactory(context, middleware, index) { + const subsequentMiddleware = middleware[index] + // If no subsequent Middleware exists, + // the default `next()` callback is returned. + if (!subsequentMiddleware) return context.next + + return (...parameters) => { + // Run the default Vue Router `next()` callback first. + context.next(...parameters) + // Then run the subsequent Middleware with a new + // `nextMiddleware()` callback. + const nextMiddleware = nextFactory(context, middleware, index + 1) + subsequentMiddleware({ ...context, next: nextMiddleware }) + }; + } + + router.beforeEach((to, from, next) => { + if (to.meta.middleware) { + const middleware = Array.isArray(to.meta.middleware) + ? to.meta.middleware + : [to.meta.middleware]; + + const context = { + from, + next, + router, + to, + }; + const nextMiddleware = nextFactory(context, middleware, 1) + + return middleware[0]({ ...context, next: nextMiddleware }) + } + + return next() + }) + + + /*router.beforeEach((to, from, next) => { + var accessToken = store.state.session.userSession.accessToken + // ESTANDO LOGEADO + if (accessToken) { + // SE PERMITE IR DE AREA PUBLICA A PRIVADA + if (!from.matched.some(record => record.meta.requiresAuth) && to.matched.some(record => record.meta.requiresAuth)) { + next() + } + // SE PERMITE IR DE UNA AREA PRIVADA A OTRA PRIVADA + if (from.matched.some(record => record.meta.requiresAuth) && to.matched.some(record => record.meta.requiresAuth)) { + next() + } + // NO SE PERMITE IR A UN AREA PUBLICA DESDE UN AREA PRIVADA + if (from.matched.some(record => record.meta.requiresAuth) && !to.matched.some(record => record.meta.requiresAuth)) { + next(false) + } + // SE REDIRIJE AL PANEL + if (!from.matched.some(record => record.meta.requiresAuth) && !to.matched.some(record => record.meta.requiresAuth)) { + next('/Panel') + } + // NO ESTA LOGEADO + } else { + // SE PERMITE IR DE UNA AREA PUBLICA A OTRA PUBLICA + if (!from.matched.some(record => record.meta.requiresAuth) && !to.matched.some(record => record.meta.requiresAuth)) { + next() + } + // SE PERMITE IR DE UNA AREA PRIVADA A UNA PUBLICA (LOGOUT) + if (from.matched.some(record => record.meta.requiresAuth) && !to.matched.some(record => record.meta.requiresAuth)) { + next() + } + // NO SE PERMITE IR DE UNA AREA PUBLICA A UNA PRIVADA + if (!from.matched.some(record => record.meta.requiresAuth) && to.matched.some(record => record.meta.requiresAuth)) { + // REDIRIGIR A LOGIN + next('/') + } + } + })*/ + +} diff --git a/src/boot/local-storage.ts b/src/boot/local-storage.ts new file mode 100644 index 0000000..a7acbde --- /dev/null +++ b/src/boot/local-storage.ts @@ -0,0 +1,7 @@ +// import something here +import { _LocalStorage } from '../local-storage' +// leave the export, even if you don't use it +export default ({ app, router, Vue }) => { + // something to do + Vue.prototype.$_localStorage = _LocalStorage +} diff --git a/src/boot/myconfig.ts b/src/boot/myconfig.ts new file mode 100644 index 0000000..64d6adc --- /dev/null +++ b/src/boot/myconfig.ts @@ -0,0 +1,9 @@ +// import something here +import myconfig from '../myconfig' + +// leave the export, even if you don't use it +export default ({ Vue }) => { + //Vue.use(myconfig); + // something to do + Vue.prototype.$myconfig = myconfig +} diff --git a/src/boot/vee-validate.ts b/src/boot/vee-validate.ts new file mode 100644 index 0000000..d849de1 --- /dev/null +++ b/src/boot/vee-validate.ts @@ -0,0 +1,5 @@ +import VeeValidate from "vee-validate"; + +export default ({ Vue }) => { + Vue.use(VeeValidate, { inject: false }) +} diff --git a/src/boot/vue-i18n.ts b/src/boot/vue-i18n.ts new file mode 100644 index 0000000..952c7e3 --- /dev/null +++ b/src/boot/vue-i18n.ts @@ -0,0 +1,45 @@ +// src/boot/vue-i18n.js +import VueI18n from 'vue-i18n' +import messages from '../statics/i18n' +import { tools } from '../store/Modules/tools' + +export default ({ app, store, Vue }) => { + Vue.use(VueI18n) + // Vue.config.lang = process.env.LANG_DEFAULT; + + let mylang = tools.getItemLS(tools.localStorage.lang) + + if ((navigator) && (mylang === '')) { + mylang = navigator.language + // console.log(`LANG NAVIGATOR ${mylang}`) + } + + if (mylang === '') + mylang = process.env.LANG_DEFAULT; + + if (mylang.toLowerCase() === 'es-es') + mylang = 'es' + + // console.log('MYLANG2=', mylang) + // console.log('process.env.LANG_DEFAULT=', process.env.LANG_DEFAULT) + Vue.config.lang = mylang + + // import(`quasar/lang/${mylang}`).then(lang => { + // console.log(' ... LANGDEFAULT=', lang) + // this.$q.i18n.set(lang.default) + // import(`src/statics/i18n`).then(function () { + // }) + // }) + + // console.log("PLUGINS INIT...."); + + // console.log("LANG_DEFAULT: ") + // console.log(process.env.LANG_DEFAULT) + + // Set i18n instance on app + app.i18n = new VueI18n({ + fallbackLocale: mylang, + locale: mylang, + messages + }) +} diff --git a/src/boot/vue-idb.ts b/src/boot/vue-idb.ts new file mode 100644 index 0000000..3b1f502 --- /dev/null +++ b/src/boot/vue-idb.ts @@ -0,0 +1,20 @@ +import Vue from 'vue' +import VueIdb from 'vue-idb' + +export default ({ Vue }) => { + Vue.use(VueIdb) + +} + + +/* + + +export default new VueIdb({ + version: 1, + database: 'test', + schemas: [ + { categories: '++_id, sub_categ_id, descr_it' } + ] +}) +*/ diff --git a/src/boot/vuelidate.ts b/src/boot/vuelidate.ts new file mode 100644 index 0000000..30d04fc --- /dev/null +++ b/src/boot/vuelidate.ts @@ -0,0 +1,5 @@ +import Vuelidate from 'vuelidate' + +export default ({ Vue }) => { + Vue.use(Vuelidate) +} diff --git a/src/env.d.ts b/src/env.d.ts new file mode 100644 index 0000000..503c2b6 --- /dev/null +++ b/src/env.d.ts @@ -0,0 +1,7 @@ +declare namespace NodeJS { + interface ProcessEnv { + NODE_ENV: string + VUE_ROUTER_MODE: 'hash' | 'history' | 'abstract' | undefined + VUE_ROUTER_BASE: string | undefined + } +} diff --git a/src/error-handler/backend.ts b/src/error-handler/backend.ts new file mode 100644 index 0000000..24a1501 --- /dev/null +++ b/src/error-handler/backend.ts @@ -0,0 +1,24 @@ +// import { i18n } from '../boot/vue-i18n' +import { Notify } from 'quasar' +export default (error) => { +/* + let message = this.$i18n.t('errors.backend.undefined') + if (error.response.data.error && error.response.data.message) { + message = error.response.data.message + } + + if (error.response.data.errors) { + const errors = Object.keys(error.response.data.errors) + message = error.response.data.errors[errors[0]][0] + } + + Notify.create({ + message, + position: 'center' + }) + + if (message === this.$i18n.t('errors.backend.undefined')) { + console.log(error.response) + } +*/ +} diff --git a/src/error-handler/firebase.ts b/src/error-handler/firebase.ts new file mode 100644 index 0000000..3f058f6 --- /dev/null +++ b/src/error-handler/firebase.ts @@ -0,0 +1,24 @@ +import store from '../store' +// import { i18n } from '../boot/vue-i18n' +import { Notify } from 'quasar' + +export default (error) => { +/* + switch (error.code) { + case 'messaging/notifications-blocked': + case 'messaging/permission-blocked': + store.commit('session/fcmNotificationPromptShowed', true) + store.commit('session/fcmNotificationsBlocked', true) + break + case 'messaging/token-unsubscribe-failed': + Notify.create({ + message: i18n.t('errors.firebase.try_again'), + position: 'center' + }) + break + default: + console.error(error) + break + } +*/ +} diff --git a/src/error-handler/graphql.ts b/src/error-handler/graphql.ts new file mode 100644 index 0000000..9f9591c --- /dev/null +++ b/src/error-handler/graphql.ts @@ -0,0 +1,21 @@ +// import { i18n } from '../boot/vue-i18n' +import { Notify } from 'quasar' +export default (error) => { +/* + let message = this.$i18n.t('errors.graphql.undefined') + + if (error[0].validation) { + let errors = Object.keys(error[0].validation) + message = error[0].validation[errors[0]][0] + } + + Notify.create({ + message, + position: 'center' + }) + + if (message === this.$i18n.t('errors.graphql.undefined')) { + console.log(error.response) + } +*/ +} diff --git a/src/error-handler/index.ts b/src/error-handler/index.ts new file mode 100644 index 0000000..ebb18cc --- /dev/null +++ b/src/error-handler/index.ts @@ -0,0 +1,21 @@ +// import Backend from './backend' +// import Firebase from './firebase' +import Graphql from './graphql' + +export default (context, error) => { + /* + if (error.constructor.name === 'FirebaseError') { + Firebase(error) + return + } + if (error.response && error.response.data && error.response.data.backend) { + Backend(error) + return + }*/ + + if (error[0] && error[0].locations && error[0].validation) { + Graphql(error) + return + } + console.log('Error handler', error) +} diff --git a/src/globalroutines/index.ts b/src/globalroutines/index.ts new file mode 100644 index 0000000..1ae91ce --- /dev/null +++ b/src/globalroutines/index.ts @@ -0,0 +1,24 @@ +import indexdb from './indexdb' +import { GlobalStore } from "../store/Modules"; + +export default async (context, cmd, table, data = null, id = '') => { + const descr = data !== null ? data.descr : '' + // console.log('globalroutines', cmd, table, descr, id) + return await indexdb(context, cmd, table, data, id) + .then(ris => { + setTimeout(function () { + GlobalStore.state.connData.uploading_indexeddb = 0 + GlobalStore.state.connData.downloading_indexeddb = 0 + }, 1000) + return ris + } + + ).catch(err => { + setTimeout(function () { + GlobalStore.state.connData.uploading_indexeddb = (GlobalStore.state.connData.uploading_indexeddb === 1) ? -1 : GlobalStore.state.connData.uploading_indexeddb + GlobalStore.state.connData.downloading_indexeddb = (GlobalStore.state.connData.downloading_indexeddb === 1) ? -1 : GlobalStore.state.connData.downloading_indexeddb + }, 1000) + + console.log('ERROR INDEXEDDB: ', err) + }) +} diff --git a/src/globalroutines/indexdb.ts b/src/globalroutines/indexdb.ts new file mode 100644 index 0000000..d1d28cf --- /dev/null +++ b/src/globalroutines/indexdb.ts @@ -0,0 +1,123 @@ +import { Todos, UserStore } from '@store' +import _ from 'lodash' +import store, { GlobalStore } from '../store' + +import { idbKeyval as storage } from '../js/storage.js' +import { costanti } from '../store/Modules/costanti' +import { ICfgData } from '@src/model' + +function saveConfigIndexDb(context) { + + const data: ICfgData = {} + data._id = costanti.CONFIG_ID_CFG + data.lang = UserStore.state.lang + data.token = UserStore.state.x_auth_token + data.userId = UserStore.state.userId + + writeConfigIndexDb('config', data) +} + +function writeConfigIndexDb(context, data) { + // console.log('writeConfigIndexDb', data) + + storage.setdata('config', data) + .then((ris) => { + return true + }) + +} + +async function readfromIndexDbToStateTodos(context, table) { + console.log('*** readfromIndexDbToStateTodos ***') + + return await storage.getalldata(table) + .then((reccat) => { + // console.log('&&&&&&& readfromIndexDbToStateTodos OK: Num RECORD: ', records.length) + if (table === 'categories') { + console.log('reccat', reccat) + Todos.state.categories = [] + for (const indcat in reccat) { + Todos.state.categories.push(reccat[indcat].valore) + } + + console.log('ARRAY Categories', Todos.state.categories) + + return storage.getalldata('todos') + .then((records) => { + console.log('todos records', records) + // console.log('&&&&&&& readfromIndexDbToStateTodos OK: Num RECORD: ', records.length) + +/* + for (const myrec in records) { + const cat = myrec.category + const indcat = state.categories.indexOf(cat) + if (Todos.state.todos[indcat] === undefined) { + Todos.state.todos[indcat] = {} + } + + // add to the right array + Todos.state.todos[indcat].push(myrec) + + } +*/ + + console.log('************ ARRAYS SALVATI IN MEMORIA Todos.state.todos ', Todos.state.todos) + }) + } + + }).catch((error) => { + console.log('err: ', error) + }) + +} + +function consolelogpao(str, str2 = '', str3 = '') { + console.log(str, str2, str3) + // Todos.mutations.setTestpao(str + str2 + str3) +} + +function testfunc2() { + consolelogpao('testfunc2') + +} + +export default async (context, cmd, table, datakey = null, id = '') => { + + // console.log('TABLE', table, 'cmd', cmd) + if (cmd === 'loadapp') { + // ****** LOAD APP AL CARICAMENTO ! ******* + return saveConfigIndexDb(context) + + } else if (cmd === 'write') { + if (GlobalStore) { + GlobalStore.state.connData.uploading_indexeddb = 1 + } + return await storage.setdata(table, datakey) + } else if (cmd === 'updatefromIndexedDbToStateTodo') { + return await readfromIndexDbToStateTodos(context, table) + } else if (cmd === 'readall') { + if (GlobalStore) { + GlobalStore.state.connData.downloading_indexeddb = 1 + } + return await storage.getalldata(table) + } else if (cmd === 'count') { + return await storage.count(table) + } else if (cmd === 'read') { + if (GlobalStore) { + GlobalStore.state.connData.downloading_indexeddb = 1 + } + return await storage.getdata(table, id) + } else if (cmd === 'delete') { + if (GlobalStore) { + GlobalStore.state.connData.uploading_indexeddb = 1 + } + return await storage.deletedata(table, id) + } else if (cmd === 'clearalldata') { + if (GlobalStore) { + GlobalStore.state.connData.uploading_indexeddb = 1 + } + return await storage.clearalldata(table) + } else if (cmd === 'log') { + consolelogpao(table) + } +} diff --git a/src/globalroutines/util.ts b/src/globalroutines/util.ts new file mode 100644 index 0000000..565e4c5 --- /dev/null +++ b/src/globalroutines/util.ts @@ -0,0 +1,23 @@ +import { UserStore } from "../store/Modules"; +import messages from "../statics/i18n"; + +function translate(params) { + let msg = params.split('.') + let lang = UserStore.state.lang + + let stringa = messages[lang] + + let ris = stringa + if (ris !== undefined) { + msg.forEach(param => { + ris = ris[param] + }) + } else { + console.log('ERRORE IN TRANSLATE! ', params, ' NON ESISTE!') + return params + } + + return ris +} + +export default translate diff --git a/src/local-storage/index.ts b/src/local-storage/index.ts new file mode 100644 index 0000000..e5dfc21 --- /dev/null +++ b/src/local-storage/index.ts @@ -0,0 +1,66 @@ +import { LocalStorage } from 'quasar' +// import { onFail } from '../../_LOCALE/src/session/logout' +// import appSetup from './app-setup' +import config from '../config' + +let authorized = false + +export default () => { + + // #Todo: Fix localStorage security ... + /* + if (config.localStorage.enableListener) { + window.addEventListener('storage', (e) => { + if (!authorized) { + console.warn('Unauthorized local storage change') + switch (config.localStorage.unauthChange) { + case 'block': + if (e.key === 'null' || e.key === null) { + reload() + } else { + _LocalStorage.setNative(e.key, e.oldValue) + } + break + case 'clear': + reload() + break + default: + reload() + break + } + } + }, false) + } + */ + return Promise.resolve(true) +} + +const reload = () => { + // onFail().then(success => appSetup()) +} + +export const _LocalStorage = { + setNative(key, value) { + authorized = true + localStorage.setItem(key, value) + authorized = false + }, + set(key, value) { + authorized = true + LocalStorage.set(key, value) + authorized = false + }, + remove(key) { + authorized = true + LocalStorage.remove(key) + authorized = false + }, + clear() { + authorized = true + LocalStorage.clear() + authorized = false + }, + get(key) { + return LocalStorage.get.item(key) + } +} diff --git a/src/middleware/auth.ts b/src/middleware/auth.ts new file mode 100644 index 0000000..b01c5cf --- /dev/null +++ b/src/middleware/auth.ts @@ -0,0 +1,12 @@ +import { tools } from "../store/Modules/tools"; + +import { RouteNames } from '../router/route-names' + +export default function auth({ next, router }) { + const tok = tools.getItemLS(tools.localStorage.token) + if (!tok) { + return router.push({ name: RouteNames.login }); + } + + return next(); +} diff --git a/src/statics/css/dragula.css b/src/statics/css/dragula.css new file mode 100644 index 0000000..b18c16e --- /dev/null +++ b/src/statics/css/dragula.css @@ -0,0 +1,22 @@ +.gu-mirror { + position: fixed !important; + margin: 0 !important; + z-index: 9999 !important; + opacity: 0.8; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=80)"; + filter: alpha(opacity=80); +} +.gu-hide { + display: none !important; +} +.gu-unselectable { + -webkit-user-select: none !important; + -moz-user-select: none !important; + -ms-user-select: none !important; + user-select: none !important; +} +.gu-transit { + opacity: 0.2; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=20)"; + filter: alpha(opacity=20); +} diff --git a/src/statics/i18n.js b/src/statics/i18n.js new file mode 100644 index 0000000..57155bb --- /dev/null +++ b/src/statics/i18n.js @@ -0,0 +1,507 @@ +const messages = { + it: { + dialog: { + ok: 'Ok', + yes: 'Si', + no: 'No', + delete: 'Elimina', + cancel: 'Annulla', + msg: { + titledeleteTask: 'Elimina Task', + deleteTask: "Vuoi Eliminare {mytodo}?" + } + }, + comp: { + Conta: "Conta", + }, + msg: { + hello: 'Buongiorno', + myAppName: 'FreePlanet', + underconstruction: 'App in costruzione...', + myDescriz: '', + sottoTitoloApp: 'Il primo Vero Social', + sottoTitoloApp2: 'Libero, Equo e Solidale', + sottoTitoloApp3: 'dove Vive Consapevolezza e Aiuto Comunitario', + sottoTitoloApp4: 'Gratuito e senza Pubblicità', + }, + homepage: { + descrapp_title1: 'Uniti per Evolvere e Sperimentare', + descrapp_pag1: 'Riscopri come il valore della Condivisione e della Cooperazione, possa aiutarci a ritrovare il profondo ' + + 'senso della Vita, perduto in questa società consumista, e riporti quei Sani Pricìpi Naturali ed Umani di Fratellanza' + + ' che intere popolazioni antiche conoscevano bene.', + descrapp_pag2: 'E\' giunta l\'ora di utilizzare i nuovi strumenti Tecnologici a nostro favore, per Liberarci ' + + 'così piano piano dalla schiavitù del "Lavoro per generare Denaro" e trasformando le nostre Capacitá in ' + + 'Risorse Umane per poterci sostenere e vivere in Armonia con gli altri.', + freesocial: { + title: 'Free Social', + descr: 'Una Community organizzata per Categorie, dove potrai unirti a Gruppi Tematici, ' + + 'Condividere Esperienze e unire Competenze per organizzare e sostenere Progetti Innovativi per il Popolo. ' + + 'Verranno evidenziati sviluppi Etici come l\'Auto-Produzione, la Sostenibilitá, ' + + 'la Buona Salute Naturale e il Rispetto per l\'Ambiente e per tutti gli Esseri Viventi di questo ' + + 'Pianeta. Chiunque potrá esprimere il proprio Consenso o Dissenso partecipando a Sondaggi Interattivi' + + ' e realizzare insieme i Cambiamenti necessari alla nostra Società.', + }, + freetalent: { + title: 'Free Talent', + descr: 'Condividi i tuoi Talenti e Abilità, ' + + 'al posto del denaro guadagnagnerai Tempo.
' + + '"1 ora" diventa moneta di scambio, uguale per tutti, per adulti ed anziani, uomini e donne.
' + + 'Potrai utilizzare questi tuoi "Crediti Tempo" per soddisfare le tue necessità, cercando nelle Competenze Disponibili.
' + + 'Nel Dare e Ricevere, si creeranno così legami di Amicizia, Solidarietà e Cooperazione di cui l\'Essere Umano ha bisogno per potersi esprimere ' + + 'pienamente.

' + + 'Questo progetto vuole diffondere, in maniera informatizzata, questa realtà che gia esiste da tanti anni, e viene chiamata "Banca del Tempo". ' + + 'Le segreterie sparse in tutto il mondo, serviranno a dare maggiore affidabilità e fiducia negli scambi di talenti tra persone sconosciute. ' + + 'Creeremo così una rete di fiducia nel vicinato, come giá viene praticato in numerosi Ecovillaggi e Comunità del mondo. ', + }, + freegas: { + title: 'Free G.A.S.', + descr: 'Ti piacerebbe utilizzare una App che ti permetta facilmente di acquistare Prodotti Locali direttamente dal Produttore?
' + + 'Con i Gruppi di Acquisto Solidale si evitano intermediazioni inutili, ottenendo parecchi benefici tra cui:
' + + '', + }, + freeliving: { + title: 'Free Living', + descr: 'Una soluzione meravigliosa per unire 2 realtà:
' + + '1) Il Vivere soli in una casa grande.
' + + '2) l\'avere bisogno di un alloggio temporaneo.
' + + 'Tante persone Anziane (e non) abitano da sole e vorrebbero continuare a vivere nella propria abitazione, ma hanno difficoltà a farlo.
' + + 'Altre persone invece hanno bisogno di una stanza, per scelta o per necessita, ed in cambio sono disponibili a ' + + 'contribuire alle spese per le utenze domestiche o magari aiutare la persona anziana a fare la spesa, cucinare, pulire casa oppure offrendogli semplicemente compagnia.
' + + 'Tramite questo strumento, le persone potranno mettersi in contatto e decidere in che forma co-abitare. Le recensioni rilasciate ed il dettaglio dei profili utenti, ' + + 'aiuterà nella scelta della persona più in sintonia.' + + }, + freecollabora: { + title: 'Chi può Collaborare con Noi?', + descr: 'Tutti coloro che sono in linea con Princìpi Etici e ricerca del Benessere Globale del Pianeta
' + + 'Sono i benvenuti:' + + '', + }, + freesostieni: { + title: 'Come Sostenere il progetto?', + descr: '', + }, + multiplatform: { + title: 'Multi-piattaforma', + descr: 'E\' Compatibile con Google Chrome, Firefox, Safari, iOS, Android. La stessa Applicazione ha le stesse funzionalitá sia sul cellulare che sul PC. ' + + 'basta condividere il nome del sito www.freeplanet.app', + }, + free: { + title: 'Gratuita, Open Source e Niente Pubblicità', + descr: 'Vedo un futuro dove non si utilizzerà più denaro. Dove le persone si aiuteranno a vicenda e non avranno bisogno di "possedere" cose, ma le condivideranno con gli altri.
' + + 'Questa App non è in vendita, non ha scopi commerciali, non ha prezzo ed appartiene solo al Popolo. A me il compito di gestirla e proteggerla. ' + + 'Sono certo che arriveranno solo i contributi necessari per sostenerla. Grazie a Tutti per il sostegno. ' + }, + }, + pages: { + home: 'Principale', + SignUp: 'Registrazione', + SignIn: 'Login', + vreg: 'Verifica Reg', + Test: 'Test', + Category: 'Categorie', + Todo: 'Todo', + personal: 'Personale', + work: 'Lavoro', + shopping: 'Spesa', + Admin: 'Admin', + Test1: 'Test1', + Test2: 'Test2', + }, + components: { + authentication: { + login: { + facebook: 'Facebook' + }, + email_verification: { + title: 'Inizia la tua registrazione', + introduce_email: 'inserisci la tua email', + email: 'Email', + invalid_email: 'La tua email è invalida', + verify_email: 'Verifica la tua email', + go_login: 'Torna al Login', + incorrect_input: 'Inserimento incorretto.', + link_sent: 'Per confermare la Registrazione, leggi la tua casella di posta e Clicca su "Verifica Email".\nSe non la trovi, cerca nella cartella Spam.' + } + } + }, + fetch: { + errore_generico: 'Errore Generico', + errore_server: 'Impossibile accedere al Server. Riprovare Grazie', + error_doppiologin: 'Rieseguire il Login. Accesso aperto da un altro dispositivo.', + }, + user: { + notregistered: 'Devi registrarti al servizio prima di porter memorizzare i dati' + }, + reg: { + incorso: 'Registrazione in corso...', + richiesto: 'Campo Richiesto', + email: 'Email', + username: 'Nome Utente', + username_login: 'Nome Utente o email', + password: 'Password', + repeatPassword: 'Ripeti password', + terms: "Accetto i termini e le condizioni", + submit: "Registrati", + title_verif_reg: "Verifica Registrazione", + verificato: "Verificato", + non_verificato: "Non Verificato", + forgetpassword: "Password dimenticata?", + err: { + required: 'è richiesto', + email: 'dev\'essere una email valida', + errore_generico: 'Si prega di compilare correttamente i campi', + atleast: 'dev\'essere lungo almeno di', + complexity: 'deve contenere almeno 1 carattere minuscolo, 1 maiuscola e 1 cifra', + notmore: 'non dev\'essere lungo più di', + char: 'caratteri', + terms: 'Devi accettare le condizioni, per continuare.', + duplicate_email: 'l\'Email è già stata registrata', + duplicate_username: 'L\'Username è stato già utilizzato', + sameaspassword: 'Le password devono essere identiche', + } + }, + login: { + incorso: 'Login in corso', + enter: 'Login', + errato: "Username o password errata. Riprovare", + completato: 'Login effettuato!', + }, + reset: { + title_reset_pwd: "Reimposta la tua Password", + send_reset_pwd: 'Invia Reimposta la password', + incorso: 'Richiesta Nuova Email...', + email_sent: 'Email inviata', + check_email: 'Controlla la tua email, ti arriverà un messaggio con un link per reimpostare la tua password. Questo link, per sicurezza, scadrà dopo 4 ore.', + title_update_pwd: 'Aggiorna la tua password', + update_password: 'Aggiorna Password', + }, + logout: { + uscito: 'Sei Uscito', + }, + errors: { + graphql: { + undefined: 'non definito' + } + }, + todo: { + titleprioritymenu: 'Priorità:', + inserttop: 'Inserisci il Task in alto', + insertbottom: 'Inserisci il Task in basso', + edit: 'Descrizione Task:', + completed: 'Ultimi Completati', + usernotdefined: 'Attenzione, occorre essere Loggati per poter aggiungere un Todo' + }, + notification: { + status: 'Stato', + ask: 'Attiva le Notifiche', + waitingconfirm: 'Conferma la richiesta di Notifica', + confirmed: 'Notifiche Attivate!', + denied: 'Notifiche Disabilitate! Attenzione così non vedrai arrivarti i messaggi. Riabilitali per vederli.', + titlegranted: 'Permesso Notifiche Abilitato!', + statusnot: 'Stato Notifiche', + titledenied: 'Permesso Notifiche Disabilitato!', + title_subscribed: 'Sottoscrizione a FreePlanet.app!', + subscribed: 'Ora potrai ricevere i messaggi e le notifiche.', + newVersionAvailable: 'Nuova Versione!' + }, + connection: 'Connessione', + }, + 'es': { + dialog: { + ok: 'Vale', + yes: 'Sí', + no: 'No', + delete: 'Borrar', + cancel: 'Cancelar', + msg: { + titledeleteTask: 'Borrar Tarea', + deleteTask: 'Quieres borrar {mytodo}?' + } + }, + comp: { + Conta: "Conta", + }, + msg: { + hello: 'Buenos Días', + myAppName: 'FreePlanet', + underconstruction: 'App en construcción...', + myDescriz: '', + sottoTitoloApp: 'El primer Verdadero Social', + sottoTitoloApp2: 'Libre, justo y Solidario', + sottoTitoloApp3: 'Donde vive Conciencia y Ayuda comunitaria' + }, + pages: { + home: 'Principal', + SignUp: 'Nueva Cuenta', + SignIn: 'Entrar', + vreg: 'Verifica Reg', + Test: 'Test', + Category: 'Categorías', + Todo: 'Tareas', + personal: 'Personal', + work: 'Trabajo', + shopping: 'Compras', + Admin: 'Administración', + Test1: 'Test1', + Test2: 'Test2', + }, + components: { + authentication: { + login: { + facebook: 'Facebook' + }, + email_verification: { + title: 'Crea una cuenta', + introduce_email: 'ingrese su dirección de correo electrónico', + email: 'Email', + invalid_email: 'Tu correo electrónico no es válido', + verify_email: 'Revisa tu email', + go_login: 'Vuelve al Login', + incorrect_input: 'Entrada correcta.', + link_sent: 'Para confirmar el registro, lea su buzón y haga clic en "Verificar correo electrónico".\n' + 'Si no lo encuentras, busca en la carpeta Spam.' + } + } + }, + fetch: { + errore_generico: 'Error genérico', + errore_server: 'No se puede acceder al Servidor. Inténtalo de nuevo, Gracias', + error_doppiologin: 'Vuelva a iniciar sesión. Acceso abierto por otro dispositivo.', + }, + user: { + notregistered: 'Debe registrarse en el servicio antes de poder almacenar los datos' + }, + reg: { + incorso: 'Registro en curso...', + richiesto: 'Campo requerido', + email: 'Email', + username: 'Nombre usuario', + username_login: 'Nombre usuario o email', + password: 'contraseña', + repeatPassword: 'Repetir contraseña', + terms: "Acepto los términos y condiciones", + submit: "Registrarse", + title_verif_reg: "Verifica registro", + verificato: "Verificado", + non_verificato: "No Verificado", + forgetpassword: "¿Olvidaste tu contraseña?", + err: { + required: 'se requiere', + email: 'Debe ser una email válida.', + errore_generico: 'Por favor, rellene los campos correctamente', + atleast: 'debe ser al menos largo', + complexity: 'debe contener al menos 1 minúscula, 1 mayúscula y 1 dígito', + notmore: 'no tiene que ser más largo que', + char: 'caracteres', + terms: 'Debes aceptar las condiciones, para continuar..', + duplicate_email: 'La email ya ha sido registrada', + duplicate_username: 'El nombre de usuario ya ha sido utilizado', + sameaspassword: 'Las contraseñas deben ser idénticas', + } + }, + login: { + incorso: 'Login en curso', + enter: 'Entra', + errato: "Nombre de usuario, correo o contraseña incorrectos. inténtelo de nuevo", + completato: 'Login realizado!', + }, + reset: { + title_reset_pwd: "Restablece tu contraseña", + send_reset_pwd: 'Enviar restablecer contraseña', + incorso: 'Solicitar nueva Email...', + email_sent: 'Email enviada', + check_email: 'Revise su correo electrónico, recibirá un mensaje con un enlace para restablecer su contraseña. Este enlace, por razones de seguridad, expirará después de 4 horas.', + title_update_pwd: 'Actualiza tu contraseña', + update_password: 'Actualizar contraseña', + }, + logout: { + uscito: 'Estás desconectado', + }, + errors: { + graphql: { + undefined: 'no definido' + } + }, + todo: { + titleprioritymenu: 'Prioridad:', + inserttop: 'Ingrese una nueva Tarea arriba', + insertbottom: 'Ingrese una nueva Tarea abajo', + edit: 'Descripción Tarea:', + completed: 'Ultimos Completados', + usernotdefined: 'Atención, debes iniciar sesión para agregar una Tarea' + }, + notification: { + status: 'Estado', + ask: 'Activar notificaciones', + waitingconfirm: 'Confirmar la solicitud de notificación.', + confirmed: 'Notificaciones activadas!', + denied: 'Notificaciones deshabilitadas! Ten cuidado, así no verás llegar los mensajes. Rehabilítalos para verlos.', + titlegranted: 'Notificaciones permitidas habilitadas!', + statusnot: 'Estado Notificaciones', + titledenied: 'Notificaciones permitidas deshabilitadas!', + title_subscribed: 'Suscripción a FreePlanet.app!', + subscribed: 'Ahora puedes recibir mensajes y notificaciones.', + newVersionAvailable: 'Nueva Versión!' + }, + connection: 'Connection', + }, + 'enUs': { + dialog: { + ok: 'Ok', + yes: 'Yes', + no: 'No', + delete: 'Delete', + cancel: 'Cancel', + msg: { + titledeleteTask: 'Delete Task', + deleteTask: 'Delete Task {mytodo}?' + } + }, + comp: { + Conta: "Count", + }, + msg: { + hello: 'Hello!', + myAppName: 'FreePlanet', + underconstruction: 'App in constuction...', + myDescriz: '', + sottoTitoloApp: 'The first Real Social', + sottoTitoloApp2: 'Free, Fair and solidarity', + sottoTitoloApp3: 'Where the conscience and community help live' + }, + pages: { + home: 'Dashboard', + SignUp: 'SignUp', + SignIn: 'SignIn', + vreg: 'Verify Reg', + Test: 'Test', + Category: 'Category', + Todo: 'Todo', + personal: 'Personal', + work: 'Work', + shopping: 'Shopping', + Admin: 'Admin', + Test1: 'Test1', + Test2: 'Test2', + }, + components: { + authentication: { + login: { + facebook: 'Facebook' + }, + email_verification: { + title: 'Begin your registration', + introduce_email: 'Enter your email', + email: 'Email', + invalid_email: 'Your email is invalid', + verify_email: 'Verify your email', + go_login: 'Back to Login', + incorrect_input: 'Incorrect input.', + link_sent: 'To confirm the Registration, read your mailbox and click on "Verify email".\nIf you can not find it check your junk mail or spam.' + } + } + }, + fetch: { + errore_generico: 'Generic Error', + errore_server: 'Unable to access to the Server. Retry. Thank you.', + error_doppiologin: 'Signup again. Another access was made with another device.', + }, + user: { + notregistered: 'You need first to SignUp before storing data' + }, + reg: { + incorso: 'Registration please wait...', + richiesto: 'Field Required', + email: 'Email', + username_login: 'Username or email', + username: 'Username', + password: 'Password', + repeatPassword: 'Repeat password', + terms: "I agree with the terms and conditions", + submit: "Submit", + title_verif_reg: "Verify Registration", + verificato: "Verified", + non_verificato: "Not Verified", + forgetpassword: "Forget Password?", + err: { + required: 'is required', + email: 'must be a valid email', + errore_generico: 'Please review fields again', + atleast: 'must be at least', + complexity: 'must contains at least 1 lowercase letter, 1 uppercase letter, and 1 digit', + notmore: 'must not be more than', + char: 'characters long', + terms: 'You need to agree with the terms & conditions.', + duplicate_email: 'Email was already registered', + duplicate_username: 'Username is already taken', + sameaspassword: 'Passwords must be identical', + } + }, + login: { + incorso: 'Login...', + enter: 'Login', + errato: "Username or password wrong. Please retry again", + completato: 'Login successfully!', + }, + reset: { + title_reset_pwd: "Reset your Password", + send_reset_pwd: 'Send password request', + incorso: 'Request New Email...', + email_sent: 'Email sent', + check_email: 'Check your email for a message with a link to update your password. This link will expire in 4 hours for security reasons.', + title_update_pwd: 'Update your password', + update_password: 'Update Password', + }, + logout: { + uscito: 'Logout successfully', + }, + errors: { + graphql: { + undefined: 'undefined' + } + }, + todo: { + titleprioritymenu: 'Priority:', + inserttop: 'Insert Task at the top', + insertbottom: 'Insert Task at the bottom', + edit: 'Task Description:', + completed: 'Lasts Completed', + usernotdefined: 'Attention, you need to be Signed In to add a new Task' + }, + notification: { + status: 'Status', + ask: 'Enable Notification', + waitingconfirm: 'Confirm the Request Notification', + confirmed: 'Notifications Enabled!', + denied: 'Notifications Disabled! Attention, you will not see your messages incoming. Reenable it for see it', + titlegranted: 'Notification Permission Granted!', + statusnot: 'status Notification', + titledenied: 'Notification Permission Denied!', + title_subscribed: 'Subscribed to FreePlanet.app!', + subscribed: 'You can now receive Notification and Messages.', + newVersionAvailable: 'New Version!' + }, + connection: 'Conexión', + }, +}; + +export default messages; diff --git a/src/statics/images/.directory b/src/statics/images/.directory new file mode 100644 index 0000000..7f0268a --- /dev/null +++ b/src/statics/images/.directory @@ -0,0 +1,5 @@ +[Dolphin] +PreviewsShown=true +Timestamp=2019,2,24,19,27,34 +Version=4 +ViewMode=1 diff --git a/src/statics/images/all_together.jpg b/src/statics/images/all_together.jpg new file mode 100644 index 0000000..07667df Binary files /dev/null and b/src/statics/images/all_together.jpg differ diff --git a/src/statics/images/cover.jpg b/src/statics/images/cover.jpg new file mode 100644 index 0000000..af6d14f Binary files /dev/null and b/src/statics/images/cover.jpg differ diff --git a/src/statics/images/de.png b/src/statics/images/de.png new file mode 100644 index 0000000..97cb239 Binary files /dev/null and b/src/statics/images/de.png differ diff --git a/src/statics/images/es.png b/src/statics/images/es.png new file mode 100644 index 0000000..d66a950 Binary files /dev/null and b/src/statics/images/es.png differ diff --git a/src/statics/images/freeplanet-logo-full-prec.svg b/src/statics/images/freeplanet-logo-full-prec.svg new file mode 100644 index 0000000..6c9eaaa --- /dev/null +++ b/src/statics/images/freeplanet-logo-full-prec.svg @@ -0,0 +1,1186 @@ + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/statics/images/freeplanet-logo-full.odg b/src/statics/images/freeplanet-logo-full.odg new file mode 100644 index 0000000..854da65 Binary files /dev/null and b/src/statics/images/freeplanet-logo-full.odg differ diff --git a/src/statics/images/freeplanet-logo-full.svg b/src/statics/images/freeplanet-logo-full.svg new file mode 100644 index 0000000..b9c10df --- /dev/null +++ b/src/statics/images/freeplanet-logo-full.svg @@ -0,0 +1,1186 @@ + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/statics/images/freeplanet-logo-full_old2.svg b/src/statics/images/freeplanet-logo-full_old2.svg new file mode 100644 index 0000000..db5a6b4 --- /dev/null +++ b/src/statics/images/freeplanet-logo-full_old2.svg @@ -0,0 +1,726 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + FreePlanet + FreePlanet + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/statics/images/freeplanet.png b/src/statics/images/freeplanet.png new file mode 100644 index 0000000..b5e10db Binary files /dev/null and b/src/statics/images/freeplanet.png differ diff --git a/src/statics/images/gb.png b/src/statics/images/gb.png new file mode 100644 index 0000000..fa38aaa Binary files /dev/null and b/src/statics/images/gb.png differ diff --git a/src/statics/images/group-together.jpg b/src/statics/images/group-together.jpg new file mode 100644 index 0000000..58cafa4 Binary files /dev/null and b/src/statics/images/group-together.jpg differ diff --git a/src/statics/images/it.png b/src/statics/images/it.png new file mode 100644 index 0000000..3db1442 Binary files /dev/null and b/src/statics/images/it.png differ diff --git a/src/statics/images/landing_first_section.png b/src/statics/images/landing_first_section.png new file mode 100644 index 0000000..328d9af Binary files /dev/null and b/src/statics/images/landing_first_section.png differ diff --git a/src/statics/images/old.freeplanet-logo-full.svg b/src/statics/images/old.freeplanet-logo-full.svg new file mode 100644 index 0000000..763ad10 --- /dev/null +++ b/src/statics/images/old.freeplanet-logo-full.svg @@ -0,0 +1,482 @@ + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Free Planet + Free Planet + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/statics/js/fetch.js b/src/statics/js/fetch.js new file mode 100644 index 0000000..6bac6b3 --- /dev/null +++ b/src/statics/js/fetch.js @@ -0,0 +1,461 @@ +(function(self) { + 'use strict'; + + if (self.fetch) { + return + } + + var support = { + searchParams: 'URLSearchParams' in self, + iterable: 'Symbol' in self && 'iterator' in Symbol, + blob: 'FileReader' in self && 'Blob' in self && (function() { + try { + new Blob() + return true + } catch(e) { + return false + } + })(), + formData: 'FormData' in self, + arrayBuffer: 'ArrayBuffer' in self + } + + if (support.arrayBuffer) { + var viewClasses = [ + '[object Int8Array]', + '[object Uint8Array]', + '[object Uint8ClampedArray]', + '[object Int16Array]', + '[object Uint16Array]', + '[object Int32Array]', + '[object Uint32Array]', + '[object Float32Array]', + '[object Float64Array]' + ] + + var isDataView = function(obj) { + return obj && DataView.prototype.isPrototypeOf(obj) + } + + var isArrayBufferView = ArrayBuffer.isView || function(obj) { + return obj && viewClasses.indexOf(Object.prototype.toString.call(obj)) > -1 + } + } + + function normalizeName(name) { + if (typeof name !== 'string') { + name = String(name) + } + if (/[^a-z0-9\-#$%&'*+.\^_`|~]/i.test(name)) { + throw new TypeError('Invalid character in header field name') + } + return name.toLowerCase() + } + + function normalizeValue(value) { + if (typeof value !== 'string') { + value = String(value) + } + return value + } + + // Build a destructive iterator for the value list + function iteratorFor(items) { + var iterator = { + next: function() { + var value = items.shift() + return {done: value === undefined, value: value} + } + } + + if (support.iterable) { + iterator[Symbol.iterator] = function() { + return iterator + } + } + + return iterator + } + + function Headers(headers) { + this.map = {} + + if (headers instanceof Headers) { + headers.forEach(function(value, name) { + this.append(name, value) + }, this) + } else if (Array.isArray(headers)) { + headers.forEach(function(header) { + this.append(header[0], header[1]) + }, this) + } else if (headers) { + Object.getOwnPropertyNames(headers).forEach(function(name) { + this.append(name, headers[name]) + }, this) + } + } + + Headers.prototype.append = function(name, value) { + name = normalizeName(name) + value = normalizeValue(value) + var oldValue = this.map[name] + this.map[name] = oldValue ? oldValue+','+value : value + } + + Headers.prototype['delete'] = function(name) { + delete this.map[normalizeName(name)] + } + + Headers.prototype.get = function(name) { + name = normalizeName(name) + return this.has(name) ? this.map[name] : null + } + + Headers.prototype.has = function(name) { + return this.map.hasOwnProperty(normalizeName(name)) + } + + Headers.prototype.set = function(name, value) { + this.map[normalizeName(name)] = normalizeValue(value) + } + + Headers.prototype.forEach = function(callback, thisArg) { + for (var name in this.map) { + if (this.map.hasOwnProperty(name)) { + callback.call(thisArg, this.map[name], name, this) + } + } + } + + Headers.prototype.keys = function() { + var items = [] + this.forEach(function(value, name) { items.push(name) }) + return iteratorFor(items) + } + + Headers.prototype.values = function() { + var items = [] + this.forEach(function(value) { items.push(value) }) + return iteratorFor(items) + } + + Headers.prototype.entries = function() { + var items = [] + this.forEach(function(value, name) { items.push([name, value]) }) + return iteratorFor(items) + } + + if (support.iterable) { + Headers.prototype[Symbol.iterator] = Headers.prototype.entries + } + + function consumed(body) { + if (body.bodyUsed) { + return Promise.reject(new TypeError('Already read')) + } + body.bodyUsed = true + } + + function fileReaderReady(reader) { + return new Promise(function(resolve, reject) { + reader.onload = function() { + resolve(reader.result) + } + reader.onerror = function() { + reject(reader.error) + } + }) + } + + function readBlobAsArrayBuffer(blob) { + var reader = new FileReader() + var promise = fileReaderReady(reader) + reader.readAsArrayBuffer(blob) + return promise + } + + function readBlobAsText(blob) { + var reader = new FileReader() + var promise = fileReaderReady(reader) + reader.readAsText(blob) + return promise + } + + function readArrayBufferAsText(buf) { + var view = new Uint8Array(buf) + var chars = new Array(view.length) + + for (var i = 0; i < view.length; i++) { + chars[i] = String.fromCharCode(view[i]) + } + return chars.join('') + } + + function bufferClone(buf) { + if (buf.slice) { + return buf.slice(0) + } else { + var view = new Uint8Array(buf.byteLength) + view.set(new Uint8Array(buf)) + return view.buffer + } + } + + function Body() { + this.bodyUsed = false + + this._initBody = function(body) { + this._bodyInit = body + if (!body) { + this._bodyText = '' + } else if (typeof body === 'string') { + this._bodyText = body + } else if (support.blob && Blob.prototype.isPrototypeOf(body)) { + this._bodyBlob = body + } else if (support.formData && FormData.prototype.isPrototypeOf(body)) { + this._bodyFormData = body + } else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) { + this._bodyText = body.toString() + } else if (support.arrayBuffer && support.blob && isDataView(body)) { + this._bodyArrayBuffer = bufferClone(body.buffer) + // IE 10-11 can't handle a DataView body. + this._bodyInit = new Blob([this._bodyArrayBuffer]) + } else if (support.arrayBuffer && (ArrayBuffer.prototype.isPrototypeOf(body) || isArrayBufferView(body))) { + this._bodyArrayBuffer = bufferClone(body) + } else { + throw new Error('unsupported BodyInit type') + } + + if (!this.headers.get('content-type')) { + if (typeof body === 'string') { + this.headers.set('content-type', 'text/plain;charset=UTF-8') + } else if (this._bodyBlob && this._bodyBlob.type) { + this.headers.set('content-type', this._bodyBlob.type) + } else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) { + this.headers.set('content-type', 'application/x-www-form-urlencoded;charset=UTF-8') + } + } + } + + if (support.blob) { + this.blob = function() { + var rejected = consumed(this) + if (rejected) { + return rejected + } + + if (this._bodyBlob) { + return Promise.resolve(this._bodyBlob) + } else if (this._bodyArrayBuffer) { + return Promise.resolve(new Blob([this._bodyArrayBuffer])) + } else if (this._bodyFormData) { + throw new Error('could not read FormData body as blob') + } else { + return Promise.resolve(new Blob([this._bodyText])) + } + } + + this.arrayBuffer = function() { + if (this._bodyArrayBuffer) { + return consumed(this) || Promise.resolve(this._bodyArrayBuffer) + } else { + return this.blob().then(readBlobAsArrayBuffer) + } + } + } + + this.text = function() { + var rejected = consumed(this) + if (rejected) { + return rejected + } + + if (this._bodyBlob) { + return readBlobAsText(this._bodyBlob) + } else if (this._bodyArrayBuffer) { + return Promise.resolve(readArrayBufferAsText(this._bodyArrayBuffer)) + } else if (this._bodyFormData) { + throw new Error('could not read FormData body as text') + } else { + return Promise.resolve(this._bodyText) + } + } + + if (support.formData) { + this.formData = function() { + return this.text().then(decode) + } + } + + this.json = function() { + return this.text().then(JSON.parse) + } + + return this + } + + // HTTP methods whose capitalization should be normalized + var methods = ['DELETE', 'GET', 'HEAD', 'OPTIONS', 'POST', 'PUT'] + + function normalizeMethod(method) { + var upcased = method.toUpperCase() + return (methods.indexOf(upcased) > -1) ? upcased : method + } + + function Request(input, options) { + options = options || {} + var body = options.body + + if (input instanceof Request) { + if (input.bodyUsed) { + throw new TypeError('Already read') + } + this.url = input.url + this.credentials = input.credentials + if (!options.headers) { + this.headers = new Headers(input.headers) + } + this.method = input.method + this.mode = input.mode + if (!body && input._bodyInit != null) { + body = input._bodyInit + input.bodyUsed = true + } + } else { + this.url = String(input) + } + + this.credentials = options.credentials || this.credentials || 'omit' + if (options.headers || !this.headers) { + this.headers = new Headers(options.headers) + } + this.method = normalizeMethod(options.method || this.method || 'GET') + this.mode = options.mode || this.mode || null + this.referrer = null + + if ((this.method === 'GET' || this.method === 'HEAD') && body) { + throw new TypeError('Body not allowed for GET or HEAD requests') + } + this._initBody(body) + } + + Request.prototype.clone = function() { + return new Request(this, { body: this._bodyInit }) + } + + function decode(body) { + var form = new FormData() + body.trim().split('&').forEach(function(bytes) { + if (bytes) { + var split = bytes.split('=') + var name = split.shift().replace(/\+/g, ' ') + var value = split.join('=').replace(/\+/g, ' ') + form.append(decodeURIComponent(name), decodeURIComponent(value)) + } + }) + return form + } + + function parseHeaders(rawHeaders) { + var headers = new Headers() + rawHeaders.split(/\r?\n/).forEach(function(line) { + var parts = line.split(':') + var key = parts.shift().trim() + if (key) { + var value = parts.join(':').trim() + headers.append(key, value) + } + }) + return headers + } + + Body.call(Request.prototype) + + function Response(bodyInit, options) { + if (!options) { + options = {} + } + + this.type = 'default' + this.status = 'status' in options ? options.status : 200 + this.ok = this.status >= 200 && this.status < 300 + this.statusText = 'statusText' in options ? options.statusText : 'OK' + this.headers = new Headers(options.headers) + this.url = options.url || '' + this._initBody(bodyInit) + } + + Body.call(Response.prototype) + + Response.prototype.clone = function() { + return new Response(this._bodyInit, { + status: this.status, + statusText: this.statusText, + headers: new Headers(this.headers), + url: this.url + }) + } + + Response.error = function() { + var response = new Response(null, {status: 0, statusText: ''}) + response.type = 'error' + return response + } + + var redirectStatuses = [301, 302, 303, 307, 308] + + Response.redirect = function(url, status) { + if (redirectStatuses.indexOf(status) === -1) { + throw new RangeError('Invalid status code') + } + + return new Response(null, {status: status, headers: {location: url}}) + } + + self.Headers = Headers + self.Request = Request + self.Response = Response + + self.fetch = function(input, init) { + return new Promise(function(resolve, reject) { + var request = new Request(input, init) + var xhr = new XMLHttpRequest() + + xhr.onload = function() { + var options = { + status: xhr.status, + statusText: xhr.statusText, + headers: parseHeaders(xhr.getAllResponseHeaders() || '') + } + options.url = 'responseURL' in xhr ? xhr.responseURL : options.headers.get('X-Request-URL') + var body = 'response' in xhr ? xhr.response : xhr.responseText + resolve(new Response(body, options)) + } + + xhr.onerror = function() { + reject(new TypeError('Network request failed')) + } + + xhr.ontimeout = function() { + reject(new TypeError('Network request failed')) + } + + xhr.open(request.method, request.url, true) + + if (request.credentials === 'include') { + xhr.withCredentials = true + } + + if ('responseType' in xhr && support.blob) { + xhr.responseType = 'blob' + } + + request.headers.forEach(function(value, name) { + xhr.setRequestHeader(name, value) + }) + + xhr.send(typeof request._bodyInit === 'undefined' ? null : request._bodyInit) + }) + } + self.fetch.polyfill = true +})(typeof self !== 'undefined' ? self : this); diff --git a/src/statics/js/globalenv.js b/src/statics/js/globalenv.js new file mode 100644 index 0000000..df39dab --- /dev/null +++ b/src/statics/js/globalenv.js @@ -0,0 +1,26 @@ + +// importScripts('/statics/js/immortal-db.min.js'); + +/* +const cfgenv = { + website: 'http://localhost:8081', + serverweb: 'http://localhost:3000', + dbname: 'mydb3', + dbversion: 10, +} +*/ + +/* +async function clearAllDataImmortal(table) { + console.log('clearAllDataImmortal', table) + const db = ImmortalDB.ImmortalDB + await db.remove(table) +} + +async function writeDataImmortal(table, datavalue) { + console.log('writeDataImmortal', table, datavalue) + const db = ImmortalDB.ImmortalDB + await db.set(table, datavalue) +} + +*/ diff --git a/src/statics/js/idb.js b/src/statics/js/idb.js new file mode 100644 index 0000000..9835513 --- /dev/null +++ b/src/statics/js/idb.js @@ -0,0 +1,311 @@ +'use strict'; + +(function() { + function toArray(arr) { + return Array.prototype.slice.call(arr); + } + + function promisifyRequest(request) { + return new Promise(function(resolve, reject) { + request.onsuccess = function() { + resolve(request.result); + }; + + request.onerror = function() { + reject(request.error); + }; + }); + } + + function promisifyRequestCall(obj, method, args) { + var request; + var p = new Promise(function(resolve, reject) { + request = obj[method].apply(obj, args); + promisifyRequest(request).then(resolve, reject); + }); + + p.request = request; + return p; + } + + function promisifyCursorRequestCall(obj, method, args) { + var p = promisifyRequestCall(obj, method, args); + return p.then(function(value) { + if (!value) return; + return new Cursor(value, p.request); + }); + } + + function proxyProperties(ProxyClass, targetProp, properties) { + properties.forEach(function(prop) { + Object.defineProperty(ProxyClass.prototype, prop, { + get: function() { + return this[targetProp][prop]; + }, + set: function(val) { + this[targetProp][prop] = val; + } + }); + }); + } + + function proxyRequestMethods(ProxyClass, targetProp, Constructor, properties) { + properties.forEach(function(prop) { + if (!(prop in Constructor.prototype)) return; + ProxyClass.prototype[prop] = function() { + return promisifyRequestCall(this[targetProp], prop, arguments); + }; + }); + } + + function proxyMethods(ProxyClass, targetProp, Constructor, properties) { + properties.forEach(function(prop) { + if (!(prop in Constructor.prototype)) return; + ProxyClass.prototype[prop] = function() { + return this[targetProp][prop].apply(this[targetProp], arguments); + }; + }); + } + + function proxyCursorRequestMethods(ProxyClass, targetProp, Constructor, properties) { + properties.forEach(function(prop) { + if (!(prop in Constructor.prototype)) return; + ProxyClass.prototype[prop] = function() { + return promisifyCursorRequestCall(this[targetProp], prop, arguments); + }; + }); + } + + function Index(index) { + this._index = index; + } + + proxyProperties(Index, '_index', [ + 'name', + 'keyPath', + 'multiEntry', + 'unique' + ]); + + proxyRequestMethods(Index, '_index', IDBIndex, [ + 'get', + 'getKey', + 'getAll', + 'getAllKeys', + 'count' + ]); + + proxyCursorRequestMethods(Index, '_index', IDBIndex, [ + 'openCursor', + 'openKeyCursor' + ]); + + function Cursor(cursor, request) { + this._cursor = cursor; + this._request = request; + } + + proxyProperties(Cursor, '_cursor', [ + 'direction', + 'key', + 'primaryKey', + 'value' + ]); + + proxyRequestMethods(Cursor, '_cursor', IDBCursor, [ + 'update', + 'delete' + ]); + + // proxy 'next' methods + ['advance', 'continue', 'continuePrimaryKey'].forEach(function(methodName) { + if (!(methodName in IDBCursor.prototype)) return; + Cursor.prototype[methodName] = function() { + var cursor = this; + var args = arguments; + return Promise.resolve().then(function() { + cursor._cursor[methodName].apply(cursor._cursor, args); + return promisifyRequest(cursor._request).then(function(value) { + if (!value) return; + return new Cursor(value, cursor._request); + }); + }); + }; + }); + + function ObjectStore(store) { + this._store = store; + } + + ObjectStore.prototype.createIndex = function() { + return new Index(this._store.createIndex.apply(this._store, arguments)); + }; + + ObjectStore.prototype.index = function() { + return new Index(this._store.index.apply(this._store, arguments)); + }; + + proxyProperties(ObjectStore, '_store', [ + 'name', + 'keyPath', + 'indexNames', + 'autoIncrement' + ]); + + proxyRequestMethods(ObjectStore, '_store', IDBObjectStore, [ + 'put', + 'add', + 'delete', + 'clear', + 'get', + 'getAll', + 'getKey', + 'getAllKeys', + 'count' + ]); + + proxyCursorRequestMethods(ObjectStore, '_store', IDBObjectStore, [ + 'openCursor', + 'openKeyCursor' + ]); + + proxyMethods(ObjectStore, '_store', IDBObjectStore, [ + 'deleteIndex' + ]); + + function Transaction(idbTransaction) { + this._tx = idbTransaction; + this.complete = new Promise(function(resolve, reject) { + idbTransaction.oncomplete = function() { + resolve(); + }; + idbTransaction.onerror = function() { + reject(idbTransaction.error); + }; + idbTransaction.onabort = function() { + reject(idbTransaction.error); + }; + }); + } + + Transaction.prototype.objectStore = function() { + return new ObjectStore(this._tx.objectStore.apply(this._tx, arguments)); + }; + + proxyProperties(Transaction, '_tx', [ + 'objectStoreNames', + 'mode' + ]); + + proxyMethods(Transaction, '_tx', IDBTransaction, [ + 'abort' + ]); + + function UpgradeDB(db, oldVersion, transaction) { + this._db = db; + this.oldVersion = oldVersion; + this.transaction = new Transaction(transaction); + } + + UpgradeDB.prototype.createObjectStore = function() { + return new ObjectStore(this._db.createObjectStore.apply(this._db, arguments)); + }; + + proxyProperties(UpgradeDB, '_db', [ + 'name', + 'version', + 'objectStoreNames' + ]); + + proxyMethods(UpgradeDB, '_db', IDBDatabase, [ + 'deleteObjectStore', + 'close' + ]); + + function DB(db) { + this._db = db; + } + + DB.prototype.transaction = function() { + return new Transaction(this._db.transaction.apply(this._db, arguments)); + }; + + proxyProperties(DB, '_db', [ + 'name', + 'version', + 'objectStoreNames' + ]); + + proxyMethods(DB, '_db', IDBDatabase, [ + 'close' + ]); + + // Add cursor iterators + // TODO: remove this once browsers do the right thing with promises + ['openCursor', 'openKeyCursor'].forEach(function(funcName) { + [ObjectStore, Index].forEach(function(Constructor) { + Constructor.prototype[funcName.replace('open', 'iterate')] = function() { + var args = toArray(arguments); + var callback = args[args.length - 1]; + var nativeObject = this._store || this._index; + var request = nativeObject[funcName].apply(nativeObject, args.slice(0, -1)); + request.onsuccess = function() { + callback(request.result); + }; + }; + }); + }); + + // polyfill getAll + [Index, ObjectStore].forEach(function(Constructor) { + if (Constructor.prototype.getAll) return; + Constructor.prototype.getAll = function(query, count) { + var instance = this; + var items = []; + + return new Promise(function(resolve) { + instance.iterateCursor(query, function(cursor) { + if (!cursor) { + resolve(items); + return; + } + items.push(cursor.value); + + if (count !== undefined && items.length == count) { + resolve(items); + return; + } + cursor.continue(); + }); + }); + }; + }); + + var exp = { + open: function(name, version, upgradeCallback) { + var p = promisifyRequestCall(indexedDB, 'open', [name, version]); + var request = p.request; + + request.onupgradeneeded = function(event) { + if (upgradeCallback) { + upgradeCallback(new UpgradeDB(request.result, event.oldVersion, request.transaction)); + } + }; + + return p.then(function(db) { + return new DB(db); + }); + }, + delete: function(name) { + return promisifyRequestCall(indexedDB, 'deleteDatabase', [name]); + } + }; + + if (typeof module !== 'undefined') { + module.exports = exp; + module.exports.default = module.exports; + } + else { + self.idb = exp; + } +}()); diff --git a/src/statics/js/material.min.js b/src/statics/js/material.min.js new file mode 100644 index 0000000..46524fb --- /dev/null +++ b/src/statics/js/material.min.js @@ -0,0 +1,10 @@ +/** + * material-design-lite - Material Design Components in CSS, JS and HTML + * @version v1.3.0 + * @license Apache-2.0 + * @copyright 2015 Google, Inc. + * @link https://github.com/google/material-design-lite + */ +!function(){"use strict";function e(e,t){if(e){if(t.element_.classList.contains(t.CssClasses_.MDL_JS_RIPPLE_EFFECT)){var s=document.createElement("span");s.classList.add(t.CssClasses_.MDL_RIPPLE_CONTAINER),s.classList.add(t.CssClasses_.MDL_JS_RIPPLE_EFFECT);var i=document.createElement("span");i.classList.add(t.CssClasses_.MDL_RIPPLE),s.appendChild(i),e.appendChild(s)}e.addEventListener("click",function(s){if("#"===e.getAttribute("href").charAt(0)){s.preventDefault();var i=e.href.split("#")[1],n=t.element_.querySelector("#"+i);t.resetTabState_(),t.resetPanelState_(),e.classList.add(t.CssClasses_.ACTIVE_CLASS),n.classList.add(t.CssClasses_.ACTIVE_CLASS)}})}}function t(e,t,s,i){function n(){var n=e.href.split("#")[1],a=i.content_.querySelector("#"+n);i.resetTabState_(t),i.resetPanelState_(s),e.classList.add(i.CssClasses_.IS_ACTIVE),a.classList.add(i.CssClasses_.IS_ACTIVE)}if(i.tabBar_.classList.contains(i.CssClasses_.JS_RIPPLE_EFFECT)){var a=document.createElement("span");a.classList.add(i.CssClasses_.RIPPLE_CONTAINER),a.classList.add(i.CssClasses_.JS_RIPPLE_EFFECT);var l=document.createElement("span");l.classList.add(i.CssClasses_.RIPPLE),a.appendChild(l),e.appendChild(a)}i.tabBar_.classList.contains(i.CssClasses_.TAB_MANUAL_SWITCH)||e.addEventListener("click",function(t){"#"===e.getAttribute("href").charAt(0)&&(t.preventDefault(),n())}),e.show=n}var s={upgradeDom:function(e,t){},upgradeElement:function(e,t){},upgradeElements:function(e){},upgradeAllRegistered:function(){},registerUpgradedCallback:function(e,t){},register:function(e){},downgradeElements:function(e){}};s=function(){function e(e,t){for(var s=0;s0&&l(t.children))}function o(t){var s="undefined"==typeof t.widget&&"undefined"==typeof t.widget,i=!0;s||(i=t.widget||t.widget);var n={classConstructor:t.constructor||t.constructor,className:t.classAsString||t.classAsString,cssClass:t.cssClass||t.cssClass,widget:i,callbacks:[]};if(c.forEach(function(e){if(e.cssClass===n.cssClass)throw new Error("The provided cssClass has already been registered: "+e.cssClass);if(e.className===n.className)throw new Error("The provided className has already been registered")}),t.constructor.prototype.hasOwnProperty(C))throw new Error("MDL component classes must not have "+C+" defined as a property.");var a=e(t.classAsString,n);a||c.push(n)}function r(t,s){var i=e(t);i&&i.callbacks.push(s)}function _(){for(var e=0;e0&&this.container_.classList.contains(this.CssClasses_.IS_VISIBLE)&&(e.keyCode===this.Keycodes_.UP_ARROW?(e.preventDefault(),t[t.length-1].focus()):e.keyCode===this.Keycodes_.DOWN_ARROW&&(e.preventDefault(),t[0].focus()))}},d.prototype.handleItemKeyboardEvent_=function(e){if(this.element_&&this.container_){var t=this.element_.querySelectorAll("."+this.CssClasses_.ITEM+":not([disabled])");if(t&&t.length>0&&this.container_.classList.contains(this.CssClasses_.IS_VISIBLE)){var s=Array.prototype.slice.call(t).indexOf(e.target);if(e.keyCode===this.Keycodes_.UP_ARROW)e.preventDefault(),s>0?t[s-1].focus():t[t.length-1].focus();else if(e.keyCode===this.Keycodes_.DOWN_ARROW)e.preventDefault(),t.length>s+1?t[s+1].focus():t[0].focus();else if(e.keyCode===this.Keycodes_.SPACE||e.keyCode===this.Keycodes_.ENTER){e.preventDefault();var i=new MouseEvent("mousedown");e.target.dispatchEvent(i),i=new MouseEvent("mouseup"),e.target.dispatchEvent(i),e.target.click()}else e.keyCode===this.Keycodes_.ESCAPE&&(e.preventDefault(),this.hide())}}},d.prototype.handleItemClick_=function(e){e.target.hasAttribute("disabled")?e.stopPropagation():(this.closing_=!0,window.setTimeout(function(e){this.hide(),this.closing_=!1}.bind(this),this.Constant_.CLOSE_TIMEOUT))},d.prototype.applyClip_=function(e,t){this.element_.classList.contains(this.CssClasses_.UNALIGNED)?this.element_.style.clip="":this.element_.classList.contains(this.CssClasses_.BOTTOM_RIGHT)?this.element_.style.clip="rect(0 "+t+"px 0 "+t+"px)":this.element_.classList.contains(this.CssClasses_.TOP_LEFT)?this.element_.style.clip="rect("+e+"px 0 "+e+"px 0)":this.element_.classList.contains(this.CssClasses_.TOP_RIGHT)?this.element_.style.clip="rect("+e+"px "+t+"px "+e+"px "+t+"px)":this.element_.style.clip=""},d.prototype.removeAnimationEndListener_=function(e){e.target.classList.remove(d.prototype.CssClasses_.IS_ANIMATING)},d.prototype.addAnimationEndListener_=function(){this.element_.addEventListener("transitionend",this.removeAnimationEndListener_),this.element_.addEventListener("webkitTransitionEnd",this.removeAnimationEndListener_)},d.prototype.show=function(e){if(this.element_&&this.container_&&this.outline_){var t=this.element_.getBoundingClientRect().height,s=this.element_.getBoundingClientRect().width;this.container_.style.width=s+"px",this.container_.style.height=t+"px",this.outline_.style.width=s+"px",this.outline_.style.height=t+"px";for(var i=this.Constant_.TRANSITION_DURATION_SECONDS*this.Constant_.TRANSITION_DURATION_FRACTION,n=this.element_.querySelectorAll("."+this.CssClasses_.ITEM),a=0;a0&&this.showSnackbar(this.queuedNotifications_.shift())},C.prototype.cleanup_=function(){this.element_.classList.remove(this.cssClasses_.ACTIVE),setTimeout(function(){this.element_.setAttribute("aria-hidden","true"),this.textElement_.textContent="",Boolean(this.actionElement_.getAttribute("aria-hidden"))||(this.setActionHidden_(!0),this.actionElement_.textContent="",this.actionElement_.removeEventListener("click",this.actionHandler_)),this.actionHandler_=void 0,this.message_=void 0,this.actionText_=void 0,this.active=!1,this.checkQueue_()}.bind(this),this.Constant_.ANIMATION_LENGTH)},C.prototype.setActionHidden_=function(e){e?this.actionElement_.setAttribute("aria-hidden","true"):this.actionElement_.removeAttribute("aria-hidden")},s.register({constructor:C,classAsString:"MaterialSnackbar",cssClass:"mdl-js-snackbar",widget:!0});var u=function(e){this.element_=e,this.init()};window.MaterialSpinner=u,u.prototype.Constant_={MDL_SPINNER_LAYER_COUNT:4},u.prototype.CssClasses_={MDL_SPINNER_LAYER:"mdl-spinner__layer",MDL_SPINNER_CIRCLE_CLIPPER:"mdl-spinner__circle-clipper",MDL_SPINNER_CIRCLE:"mdl-spinner__circle",MDL_SPINNER_GAP_PATCH:"mdl-spinner__gap-patch",MDL_SPINNER_LEFT:"mdl-spinner__left",MDL_SPINNER_RIGHT:"mdl-spinner__right"},u.prototype.createLayer=function(e){var t=document.createElement("div");t.classList.add(this.CssClasses_.MDL_SPINNER_LAYER),t.classList.add(this.CssClasses_.MDL_SPINNER_LAYER+"-"+e);var s=document.createElement("div");s.classList.add(this.CssClasses_.MDL_SPINNER_CIRCLE_CLIPPER),s.classList.add(this.CssClasses_.MDL_SPINNER_LEFT);var i=document.createElement("div");i.classList.add(this.CssClasses_.MDL_SPINNER_GAP_PATCH);var n=document.createElement("div");n.classList.add(this.CssClasses_.MDL_SPINNER_CIRCLE_CLIPPER),n.classList.add(this.CssClasses_.MDL_SPINNER_RIGHT);for(var a=[s,i,n],l=0;l=this.maxRows&&e.preventDefault()},L.prototype.onFocus_=function(e){this.element_.classList.add(this.CssClasses_.IS_FOCUSED)},L.prototype.onBlur_=function(e){this.element_.classList.remove(this.CssClasses_.IS_FOCUSED)},L.prototype.onReset_=function(e){this.updateClasses_()},L.prototype.updateClasses_=function(){this.checkDisabled(),this.checkValidity(),this.checkDirty(),this.checkFocus()},L.prototype.checkDisabled=function(){this.input_.disabled?this.element_.classList.add(this.CssClasses_.IS_DISABLED):this.element_.classList.remove(this.CssClasses_.IS_DISABLED)},L.prototype.checkDisabled=L.prototype.checkDisabled,L.prototype.checkFocus=function(){Boolean(this.element_.querySelector(":focus"))?this.element_.classList.add(this.CssClasses_.IS_FOCUSED):this.element_.classList.remove(this.CssClasses_.IS_FOCUSED)},L.prototype.checkFocus=L.prototype.checkFocus,L.prototype.checkValidity=function(){this.input_.validity&&(this.input_.validity.valid?this.element_.classList.remove(this.CssClasses_.IS_INVALID):this.element_.classList.add(this.CssClasses_.IS_INVALID))},L.prototype.checkValidity=L.prototype.checkValidity,L.prototype.checkDirty=function(){this.input_.value&&this.input_.value.length>0?this.element_.classList.add(this.CssClasses_.IS_DIRTY):this.element_.classList.remove(this.CssClasses_.IS_DIRTY)},L.prototype.checkDirty=L.prototype.checkDirty,L.prototype.disable=function(){this.input_.disabled=!0,this.updateClasses_()},L.prototype.disable=L.prototype.disable,L.prototype.enable=function(){this.input_.disabled=!1,this.updateClasses_()},L.prototype.enable=L.prototype.enable,L.prototype.change=function(e){this.input_.value=e||"",this.updateClasses_()},L.prototype.change=L.prototype.change,L.prototype.init=function(){if(this.element_&&(this.label_=this.element_.querySelector("."+this.CssClasses_.LABEL),this.input_=this.element_.querySelector("."+this.CssClasses_.INPUT),this.input_)){this.input_.hasAttribute(this.Constant_.MAX_ROWS_ATTRIBUTE)&&(this.maxRows=parseInt(this.input_.getAttribute(this.Constant_.MAX_ROWS_ATTRIBUTE),10),isNaN(this.maxRows)&&(this.maxRows=this.Constant_.NO_MAX_ROWS)),this.input_.hasAttribute("placeholder")&&this.element_.classList.add(this.CssClasses_.HAS_PLACEHOLDER),this.boundUpdateClassesHandler=this.updateClasses_.bind(this),this.boundFocusHandler=this.onFocus_.bind(this),this.boundBlurHandler=this.onBlur_.bind(this),this.boundResetHandler=this.onReset_.bind(this),this.input_.addEventListener("input",this.boundUpdateClassesHandler),this.input_.addEventListener("focus",this.boundFocusHandler),this.input_.addEventListener("blur",this.boundBlurHandler),this.input_.addEventListener("reset",this.boundResetHandler),this.maxRows!==this.Constant_.NO_MAX_ROWS&&(this.boundKeyDownHandler=this.onKeyDown_.bind(this),this.input_.addEventListener("keydown",this.boundKeyDownHandler));var e=this.element_.classList.contains(this.CssClasses_.IS_INVALID);this.updateClasses_(),this.element_.classList.add(this.CssClasses_.IS_UPGRADED),e&&this.element_.classList.add(this.CssClasses_.IS_INVALID),this.input_.hasAttribute("autofocus")&&(this.element_.focus(),this.checkFocus())}},s.register({constructor:L,classAsString:"MaterialTextfield",cssClass:"mdl-js-textfield",widget:!0});var I=function(e){this.element_=e,this.init()};window.MaterialTooltip=I,I.prototype.Constant_={},I.prototype.CssClasses_={IS_ACTIVE:"is-active",BOTTOM:"mdl-tooltip--bottom",LEFT:"mdl-tooltip--left",RIGHT:"mdl-tooltip--right",TOP:"mdl-tooltip--top"},I.prototype.handleMouseEnter_=function(e){var t=e.target.getBoundingClientRect(),s=t.left+t.width/2,i=t.top+t.height/2,n=-1*(this.element_.offsetWidth/2),a=-1*(this.element_.offsetHeight/2);this.element_.classList.contains(this.CssClasses_.LEFT)||this.element_.classList.contains(this.CssClasses_.RIGHT)?(s=t.width/2,i+a<0?(this.element_.style.top="0",this.element_.style.marginTop="0"):(this.element_.style.top=i+"px",this.element_.style.marginTop=a+"px")):s+n<0?(this.element_.style.left="0",this.element_.style.marginLeft="0"):(this.element_.style.left=s+"px",this.element_.style.marginLeft=n+"px"),this.element_.classList.contains(this.CssClasses_.TOP)?this.element_.style.top=t.top-this.element_.offsetHeight-10+"px":this.element_.classList.contains(this.CssClasses_.RIGHT)?this.element_.style.left=t.left+t.width+10+"px":this.element_.classList.contains(this.CssClasses_.LEFT)?this.element_.style.left=t.left-this.element_.offsetWidth-10+"px":this.element_.style.top=t.top+t.height+10+"px",this.element_.classList.add(this.CssClasses_.IS_ACTIVE)},I.prototype.hideTooltip_=function(){this.element_.classList.remove(this.CssClasses_.IS_ACTIVE)},I.prototype.init=function(){if(this.element_){var e=this.element_.getAttribute("for")||this.element_.getAttribute("data-mdl-for");e&&(this.forElement_=document.getElementById(e)),this.forElement_&&(this.forElement_.hasAttribute("tabindex")||this.forElement_.setAttribute("tabindex","0"),this.boundMouseEnterHandler=this.handleMouseEnter_.bind(this),this.boundMouseLeaveAndScrollHandler=this.hideTooltip_.bind(this),this.forElement_.addEventListener("mouseenter",this.boundMouseEnterHandler,!1),this.forElement_.addEventListener("touchend",this.boundMouseEnterHandler,!1),this.forElement_.addEventListener("mouseleave",this.boundMouseLeaveAndScrollHandler,!1),window.addEventListener("scroll",this.boundMouseLeaveAndScrollHandler,!0),window.addEventListener("touchstart",this.boundMouseLeaveAndScrollHandler))}},s.register({constructor:I,classAsString:"MaterialTooltip",cssClass:"mdl-tooltip"});var f=function(e){this.element_=e,this.init()};window.MaterialLayout=f,f.prototype.Constant_={MAX_WIDTH:"(max-width: 1024px)",TAB_SCROLL_PIXELS:100,RESIZE_TIMEOUT:100,MENU_ICON:"",CHEVRON_LEFT:"chevron_left",CHEVRON_RIGHT:"chevron_right"},f.prototype.Keycodes_={ENTER:13,ESCAPE:27,SPACE:32},f.prototype.Mode_={STANDARD:0,SEAMED:1,WATERFALL:2,SCROLL:3},f.prototype.CssClasses_={CONTAINER:"mdl-layout__container",HEADER:"mdl-layout__header",DRAWER:"mdl-layout__drawer",CONTENT:"mdl-layout__content",DRAWER_BTN:"mdl-layout__drawer-button",ICON:"material-icons",JS_RIPPLE_EFFECT:"mdl-js-ripple-effect",RIPPLE_CONTAINER:"mdl-layout__tab-ripple-container",RIPPLE:"mdl-ripple",RIPPLE_IGNORE_EVENTS:"mdl-js-ripple-effect--ignore-events",HEADER_SEAMED:"mdl-layout__header--seamed",HEADER_WATERFALL:"mdl-layout__header--waterfall",HEADER_SCROLL:"mdl-layout__header--scroll",FIXED_HEADER:"mdl-layout--fixed-header",OBFUSCATOR:"mdl-layout__obfuscator",TAB_BAR:"mdl-layout__tab-bar",TAB_CONTAINER:"mdl-layout__tab-bar-container",TAB:"mdl-layout__tab",TAB_BAR_BUTTON:"mdl-layout__tab-bar-button",TAB_BAR_LEFT_BUTTON:"mdl-layout__tab-bar-left-button",TAB_BAR_RIGHT_BUTTON:"mdl-layout__tab-bar-right-button",TAB_MANUAL_SWITCH:"mdl-layout__tab-manual-switch",PANEL:"mdl-layout__tab-panel",HAS_DRAWER:"has-drawer",HAS_TABS:"has-tabs",HAS_SCROLLING_HEADER:"has-scrolling-header",CASTING_SHADOW:"is-casting-shadow",IS_COMPACT:"is-compact",IS_SMALL_SCREEN:"is-small-screen",IS_DRAWER_OPEN:"is-visible",IS_ACTIVE:"is-active",IS_UPGRADED:"is-upgraded",IS_ANIMATING:"is-animating",ON_LARGE_SCREEN:"mdl-layout--large-screen-only",ON_SMALL_SCREEN:"mdl-layout--small-screen-only"},f.prototype.contentScrollHandler_=function(){if(!this.header_.classList.contains(this.CssClasses_.IS_ANIMATING)){var e=!this.element_.classList.contains(this.CssClasses_.IS_SMALL_SCREEN)||this.element_.classList.contains(this.CssClasses_.FIXED_HEADER);this.content_.scrollTop>0&&!this.header_.classList.contains(this.CssClasses_.IS_COMPACT)?(this.header_.classList.add(this.CssClasses_.CASTING_SHADOW),this.header_.classList.add(this.CssClasses_.IS_COMPACT),e&&this.header_.classList.add(this.CssClasses_.IS_ANIMATING)):this.content_.scrollTop<=0&&this.header_.classList.contains(this.CssClasses_.IS_COMPACT)&&(this.header_.classList.remove(this.CssClasses_.CASTING_SHADOW),this.header_.classList.remove(this.CssClasses_.IS_COMPACT),e&&this.header_.classList.add(this.CssClasses_.IS_ANIMATING))}},f.prototype.keyboardEventHandler_=function(e){e.keyCode===this.Keycodes_.ESCAPE&&this.drawer_.classList.contains(this.CssClasses_.IS_DRAWER_OPEN)&&this.toggleDrawer()},f.prototype.screenSizeHandler_=function(){this.screenSizeMediaQuery_.matches?this.element_.classList.add(this.CssClasses_.IS_SMALL_SCREEN):(this.element_.classList.remove(this.CssClasses_.IS_SMALL_SCREEN),this.drawer_&&(this.drawer_.classList.remove(this.CssClasses_.IS_DRAWER_OPEN),this.obfuscator_.classList.remove(this.CssClasses_.IS_DRAWER_OPEN)))},f.prototype.drawerToggleHandler_=function(e){if(e&&"keydown"===e.type){if(e.keyCode!==this.Keycodes_.SPACE&&e.keyCode!==this.Keycodes_.ENTER)return;e.preventDefault()}this.toggleDrawer()},f.prototype.headerTransitionEndHandler_=function(){this.header_.classList.remove(this.CssClasses_.IS_ANIMATING)},f.prototype.headerClickHandler_=function(){this.header_.classList.contains(this.CssClasses_.IS_COMPACT)&&(this.header_.classList.remove(this.CssClasses_.IS_COMPACT),this.header_.classList.add(this.CssClasses_.IS_ANIMATING))},f.prototype.resetTabState_=function(e){for(var t=0;t0?c.classList.add(this.CssClasses_.IS_ACTIVE):c.classList.remove(this.CssClasses_.IS_ACTIVE),this.tabBar_.scrollLeft0)return;this.setFrameCount(1);var i,n,a=e.currentTarget.getBoundingClientRect();if(0===e.clientX&&0===e.clientY)i=Math.round(a.width/2),n=Math.round(a.height/2);else{var l=void 0!==e.clientX?e.clientX:e.touches[0].clientX,o=void 0!==e.clientY?e.clientY:e.touches[0].clientY;i=Math.round(l-a.left),n=Math.round(o-a.top)}this.setRippleXY(i,n),this.setRippleStyles(!0),window.requestAnimationFrame(this.animFrameHandler.bind(this))}},S.prototype.upHandler_=function(e){e&&2!==e.detail&&window.setTimeout(function(){this.rippleElement_.classList.remove(this.CssClasses_.IS_VISIBLE)}.bind(this),0)},S.prototype.init=function(){if(this.element_){var e=this.element_.classList.contains(this.CssClasses_.RIPPLE_CENTER);this.element_.classList.contains(this.CssClasses_.RIPPLE_EFFECT_IGNORE_EVENTS)||(this.rippleElement_=this.element_.querySelector("."+this.CssClasses_.RIPPLE),this.frameCount_=0,this.rippleSize_=0,this.x_=0,this.y_=0,this.ignoringMouseDown_=!1,this.boundDownHandler=this.downHandler_.bind(this),this.element_.addEventListener("mousedown",this.boundDownHandler),this.element_.addEventListener("touchstart",this.boundDownHandler),this.boundUpHandler=this.upHandler_.bind(this),this.element_.addEventListener("mouseup",this.boundUpHandler),this.element_.addEventListener("mouseleave",this.boundUpHandler),this.element_.addEventListener("touchend",this.boundUpHandler),this.element_.addEventListener("blur",this.boundUpHandler),this.getFrameCount=function(){return this.frameCount_},this.setFrameCount=function(e){this.frameCount_=e},this.getRippleElement=function(){return this.rippleElement_},this.setRippleXY=function(e,t){this.x_=e,this.y_=t},this.setRippleStyles=function(t){if(null!==this.rippleElement_){var s,i,n,a="translate("+this.x_+"px, "+this.y_+"px)";t?(i=this.Constant_.INITIAL_SCALE,n=this.Constant_.INITIAL_SIZE):(i=this.Constant_.FINAL_SCALE,n=this.rippleSize_+"px",e&&(a="translate("+this.boundWidth/2+"px, "+this.boundHeight/2+"px)")),s="translate(-50%, -50%) "+a+i,this.rippleElement_.style.webkitTransform=s,this.rippleElement_.style.msTransform=s,this.rippleElement_.style.transform=s,t?this.rippleElement_.classList.remove(this.CssClasses_.IS_ANIMATING):this.rippleElement_.classList.add(this.CssClasses_.IS_ANIMATING)}},this.animFrameHandler=function(){this.frameCount_-- >0?window.requestAnimationFrame(this.animFrameHandler.bind(this)):this.setRippleStyles(!1)})}},s.register({constructor:S,classAsString:"MaterialRipple",cssClass:"mdl-js-ripple-effect",widget:!1})}(); +//# sourceMappingURL=material.min.js.map diff --git a/src/statics/js/promise.js b/src/statics/js/promise.js new file mode 100644 index 0000000..dd5e735 --- /dev/null +++ b/src/statics/js/promise.js @@ -0,0 +1,372 @@ +/** + * setImmediate polyfill v1.0.1, supports IE9+ + * © 2014–2015 Dmitry Korobkin + * Released under the MIT license + * github.com/Octane/setImmediate + */ +window.setImmediate || function () {'use strict'; + + var uid = 0; + var storage = {}; + var firstCall = true; + var slice = Array.prototype.slice; + var message = 'setImmediatePolyfillMessage'; + + function fastApply(args) { + var func = args[0]; + switch (args.length) { + case 1: + return func(); + case 2: + return func(args[1]); + case 3: + return func(args[1], args[2]); + } + return func.apply(window, slice.call(args, 1)); + } + + function callback(event) { + var key = event.data; + var data; + if (typeof key == 'string' && key.indexOf(message) == 0) { + data = storage[key]; + if (data) { + delete storage[key]; + fastApply(data); + } + } + } + + window.setImmediate = function setImmediate() { + var id = uid++; + var key = message + id; + var i = arguments.length; + var args = new Array(i); + while (i--) { + args[i] = arguments[i]; + } + storage[key] = args; + if (firstCall) { + firstCall = false; + window.addEventListener('message', callback); + } + window.postMessage(key, '*'); + return id; + }; + + window.clearImmediate = function clearImmediate(id) { + delete storage[message + id]; + }; + +}(); + +/** + * Promise polyfill v1.0.10 + * requires setImmediate + * + * © 2014–2015 Dmitry Korobkin + * Released under the MIT license + * github.com/Octane/Promise + */ +(function (global) {'use strict'; + + var STATUS = '[[PromiseStatus]]'; + var VALUE = '[[PromiseValue]]'; + var ON_FUlFILLED = '[[OnFulfilled]]'; + var ON_REJECTED = '[[OnRejected]]'; + var ORIGINAL_ERROR = '[[OriginalError]]'; + var PENDING = 'pending'; + var INTERNAL_PENDING = 'internal pending'; + var FULFILLED = 'fulfilled'; + var REJECTED = 'rejected'; + var NOT_ARRAY = 'not an array.'; + var REQUIRES_NEW = 'constructor Promise requires "new".'; + var CHAINING_CYCLE = 'then() cannot return same Promise that it resolves.'; + + var setImmediate = global.setImmediate || require('timers').setImmediate; + var isArray = Array.isArray || function (anything) { + return Object.prototype.toString.call(anything) == '[object Array]'; + }; + + function InternalError(originalError) { + this[ORIGINAL_ERROR] = originalError; + } + + function isInternalError(anything) { + return anything instanceof InternalError; + } + + function isObject(anything) { + //Object.create(null) instanceof Object → false + return Object(anything) === anything; + } + + function isCallable(anything) { + return typeof anything == 'function'; + } + + function isPromise(anything) { + return anything instanceof Promise; + } + + function identity(value) { + return value; + } + + function thrower(reason) { + throw reason; + } + + function enqueue(promise, onFulfilled, onRejected) { + if (!promise[ON_FUlFILLED]) { + promise[ON_FUlFILLED] = []; + promise[ON_REJECTED] = []; + } + promise[ON_FUlFILLED].push(onFulfilled); + promise[ON_REJECTED].push(onRejected); + } + + function clearAllQueues(promise) { + delete promise[ON_FUlFILLED]; + delete promise[ON_REJECTED]; + } + + function callEach(queue) { + var i; + var length = queue.length; + for (i = 0; i < length; i++) { + queue[i](); + } + } + + function call(resolve, reject, value) { + var anything = toPromise(value); + if (isPromise(anything)) { + anything.then(resolve, reject); + } else if (isInternalError(anything)) { + reject(anything[ORIGINAL_ERROR]); + } else { + resolve(value); + } + } + + function toPromise(anything) { + var then; + if (isPromise(anything)) { + return anything; + } + if(isObject(anything)) { + try { + then = anything.then; + } catch (error) { + return new InternalError(error); + } + if (isCallable(then)) { + return new Promise(function (resolve, reject) { + setImmediate(function () { + try { + then.call(anything, resolve, reject); + } catch (error) { + reject(error); + } + }); + }); + } + } + return null; + } + + function resolvePromise(promise, resolver) { + function resolve(value) { + if (promise[STATUS] == PENDING) { + fulfillPromise(promise, value); + } + } + function reject(reason) { + if (promise[STATUS] == PENDING) { + rejectPromise(promise, reason); + } + } + try { + resolver(resolve, reject); + } catch(error) { + reject(error); + } + } + + function fulfillPromise(promise, value) { + var queue; + var anything = toPromise(value); + if (isPromise(anything)) { + promise[STATUS] = INTERNAL_PENDING; + anything.then( + function (value) { + fulfillPromise(promise, value); + }, + function (reason) { + rejectPromise(promise, reason); + } + ); + } else if (isInternalError(anything)) { + rejectPromise(promise, anything[ORIGINAL_ERROR]); + } else { + promise[STATUS] = FULFILLED; + promise[VALUE] = value; + queue = promise[ON_FUlFILLED]; + if (queue && queue.length) { + clearAllQueues(promise); + callEach(queue); + } + } + } + + function rejectPromise(promise, reason) { + var queue = promise[ON_REJECTED]; + promise[STATUS] = REJECTED; + promise[VALUE] = reason; + if (queue && queue.length) { + clearAllQueues(promise); + callEach(queue); + } + } + + function Promise(resolver) { + var promise = this; + if (!isPromise(promise)) { + throw new TypeError(REQUIRES_NEW); + } + promise[STATUS] = PENDING; + promise[VALUE] = undefined; + resolvePromise(promise, resolver); + } + + Promise.prototype.then = function (onFulfilled, onRejected) { + var promise = this; + var nextPromise; + onFulfilled = isCallable(onFulfilled) ? onFulfilled : identity; + onRejected = isCallable(onRejected) ? onRejected : thrower; + nextPromise = new Promise(function (resolve, reject) { + function tryCall(func) { + var value; + try { + value = func(promise[VALUE]); + } catch (error) { + reject(error); + return; + } + if (value === nextPromise) { + reject(new TypeError(CHAINING_CYCLE)); + } else { + call(resolve, reject, value); + } + } + function asyncOnFulfilled() { + setImmediate(tryCall, onFulfilled); + } + function asyncOnRejected() { + setImmediate(tryCall, onRejected); + } + switch (promise[STATUS]) { + case FULFILLED: + asyncOnFulfilled(); + break; + case REJECTED: + asyncOnRejected(); + break; + default: + enqueue(promise, asyncOnFulfilled, asyncOnRejected); + } + }); + return nextPromise; + }; + + Promise.prototype['catch'] = function (onRejected) { + return this.then(identity, onRejected); + }; + + Promise.resolve = function (value) { + var anything = toPromise(value); + if (isPromise(anything)) { + return anything; + } + return new Promise(function (resolve, reject) { + if (isInternalError(anything)) { + reject(anything[ORIGINAL_ERROR]); + } else { + resolve(value); + } + }); + }; + + Promise.reject = function (reason) { + return new Promise(function (resolve, reject) { + reject(reason); + }); + }; + + Promise.race = function (values) { + return new Promise(function (resolve, reject) { + var i; + var length; + if (isArray(values)) { + length = values.length; + for (i = 0; i < length; i++) { + call(resolve, reject, values[i]); + } + } else { + reject(new TypeError(NOT_ARRAY)); + } + }); + }; + + Promise.all = function (values) { + return new Promise(function (resolve, reject) { + var fulfilledCount = 0; + var promiseCount = 0; + var anything; + var length; + var value; + var i; + if (isArray(values)) { + values = values.slice(0); + length = values.length; + for (i = 0; i < length; i++) { + value = values[i]; + anything = toPromise(value); + if (isPromise(anything)) { + promiseCount++; + anything.then( + function (index) { + return function (value) { + values[index] = value; + fulfilledCount++; + if (fulfilledCount == promiseCount) { + resolve(values); + } + }; + }(i), + reject + ); + } else if (isInternalError(anything)) { + reject(anything[ORIGINAL_ERROR]); + } else { + //[1, , 3] → [1, undefined, 3] + values[i] = value; + } + } + if (!promiseCount) { + resolve(values); + } + } else { + reject(new TypeError(NOT_ARRAY)); + } + }); + }; + + if (typeof module != 'undefined' && module.exports) { + module.exports = global.Promise || Promise; + } else if (!global.Promise) { + global.Promise = Promise; + } + +}(this)); \ No newline at end of file diff --git a/src/statics/js/storage.js b/src/statics/js/storage.js new file mode 100644 index 0000000..5dc436f --- /dev/null +++ b/src/statics/js/storage.js @@ -0,0 +1,126 @@ +let idbKeyval = (() => { + let db; + // console.log('idbKeyval...') + + function getDB() { + if (!db) { + // console.log('CREO DB STORAGE JS !') + db = new Promise((resolve, reject) => { + const openreq = indexedDB.open('mydb3', 11); + + openreq.onerror = () => { + reject(openreq.error); + }; + + openreq.onupgradeneeded = () => { + // First time setup: create an empty object store + openreq.result.createObjectStore('todos', { keyPath: '_id' }); + openreq.result.createObjectStore('categories', { keyPath: '_id' }); + openreq.result.createObjectStore('sync_todos', { keyPath: '_id' }); + openreq.result.createObjectStore('sync_todos_patch', { keyPath: '_id' }); + openreq.result.createObjectStore('delete_todos', { keyPath: '_id' }); + openreq.result.createObjectStore('config', { keyPath: '_id' }); + openreq.result.createObjectStore('swmsg', { keyPath: '_id' }); + }; + + openreq.onsuccess = () => { + resolve(openreq.result); + }; + }); + } + return db; + } + + async function withStore(type, table, callback, ) { + const db = await getDB(); + return new Promise((resolve, reject) => { + const transaction = db.transaction(table, type); + transaction.oncomplete = () => resolve(); + transaction.onerror = () => reject(transaction.error); + callback(transaction.objectStore(table)); + }); + } + + return { + async get(key) { + let req; + await withStore('readonly', 'keyval', store => { + req = store.get(key); + }); + return req.result; + }, + + // jsonCopy(src) { + // return JSON.parse(JSON.stringify(src)); + // }, + + // contains(a, b) { + // // array matches + // if (Array.isArray(b)) { + // return b.some(x => a.indexOf(x) > -1); + // } + // // string match + // return a.indexOf(b) > -1; + // }, + + async getdata(table, key) { + let req; + + await withStore('readonly', table, store => { + // console.log('store', store, 'key', key) + req = store.get(key); + }); + + return req.result; + }, + async getalldata(table) { + let req; + await withStore('readonly', table, store => { + req = store.getAll(); + }); + return req.result; + }, + async set(key, value) { + let req; + await withStore('readwrite', 'keyval', store => { + req = store.put(value, key); + }); + return req.result; + }, + async setdata(table, value) { + let req; + // console.log('setdata', table, value) + + await withStore('readwrite', table, store => { + req = store.put(value); + }); + return req.result; + }, + async delete(key) { + return await withStore('readwrite', 'keyval', store => { + store.delete(key); + }); + }, + async deletedata(table, key) { + return await withStore('readwrite', table, store => { + store.delete(key); + }); + }, + async clearalldata(table) { + // console.log('clearalldata', table) + return await withStore('readwrite', table, store => { + store.clear(); + }); + } + }; +})(); + +// iOS add-to-homescreen is missing IDB, or at least it used to. +// I haven't tested this in a while. +if (!self.indexedDB) { + idbKeyval = { + get: key => Promise.resolve(localStorage.getItem(key)), + set: (key, val) => Promise.resolve(localStorage.setItem(key, val)), + delete: key => Promise.resolve(localStorage.removeItem(key)) + }; +}