First alpha
This commit is contained in:
55
src/helper/base64.ts
Executable file
55
src/helper/base64.ts
Executable file
@ -0,0 +1,55 @@
|
||||
var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
|
||||
// Use a lookup table to find the index.
|
||||
var lookup = new Uint8Array(256);
|
||||
for (var i = 0; i < chars.length; i++) {
|
||||
lookup[chars.charCodeAt(i)] = i;
|
||||
}
|
||||
export function encode(arraybuffer: ArrayBuffer | Uint8Array) {
|
||||
var bytes = new Uint8Array(arraybuffer),
|
||||
i, len = bytes.length, base64 = "";
|
||||
|
||||
for (i = 0; i < len; i += 3) {
|
||||
base64 += chars[bytes[i] >> 2];
|
||||
base64 += chars[((bytes[i] & 3) << 4) | (bytes[i + 1] >> 4)];
|
||||
base64 += chars[((bytes[i + 1] & 15) << 2) | (bytes[i + 2] >> 6)];
|
||||
base64 += chars[bytes[i + 2] & 63];
|
||||
}
|
||||
|
||||
if ((len % 3) === 2) {
|
||||
base64 = base64.substring(0, base64.length - 1) + "=";
|
||||
} else if (len % 3 === 1) {
|
||||
base64 = base64.substring(0, base64.length - 2) + "==";
|
||||
}
|
||||
|
||||
return base64;
|
||||
};
|
||||
|
||||
export function decode(base64: string) {
|
||||
var bufferLength = base64.length * 0.75,
|
||||
len = base64.length, i, p = 0,
|
||||
encoded1, encoded2, encoded3, encoded4;
|
||||
|
||||
if (base64[base64.length - 1] === "=") {
|
||||
bufferLength--;
|
||||
if (base64[base64.length - 2] === "=") {
|
||||
bufferLength--;
|
||||
}
|
||||
}
|
||||
|
||||
var arraybuffer = new ArrayBuffer(bufferLength),
|
||||
bytes = new Uint8Array(arraybuffer);
|
||||
|
||||
for (i = 0; i < len; i += 4) {
|
||||
encoded1 = lookup[base64.charCodeAt(i)];
|
||||
encoded2 = lookup[base64.charCodeAt(i + 1)];
|
||||
encoded3 = lookup[base64.charCodeAt(i + 2)];
|
||||
encoded4 = lookup[base64.charCodeAt(i + 3)];
|
||||
|
||||
bytes[p++] = (encoded1 << 2) | (encoded2 >> 4);
|
||||
bytes[p++] = ((encoded2 & 15) << 4) | (encoded3 >> 2);
|
||||
bytes[p++] = ((encoded3 & 3) << 6) | (encoded4 & 63);
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
104
src/helper/indexeddb.ts
Executable file
104
src/helper/indexeddb.ts
Executable file
@ -0,0 +1,104 @@
|
||||
import Lock from "./lock";
|
||||
|
||||
import { DB, openDb, Transaction } from "idb";
|
||||
|
||||
export default class IDB {
|
||||
initLock = new Lock();
|
||||
db: DB;
|
||||
constructor(database: string, private stores: string[]) {
|
||||
let lock = this.initLock.getLock();
|
||||
lock.then(async l => {
|
||||
|
||||
let v = localStorage.getItem(database + "_version");
|
||||
let version = 0;
|
||||
if (v) version = Number(v)
|
||||
let lastStoresS = localStorage.getItem(database + "_stores");
|
||||
if (!lastStoresS) lastStoresS = "";
|
||||
let lastStores = lastStoresS.split(",").filter(e => e !== "");
|
||||
|
||||
if (!stores.every(e => lastStores.indexOf(e) >= 0) || !lastStores.every(e => stores.indexOf(e) >= 0)) version++;
|
||||
|
||||
localStorage.setItem(database + "_version", version.toString());
|
||||
localStorage.setItem(database + "_stores", stores.join(","));
|
||||
|
||||
this.db = await openDb(database, version, db => {
|
||||
console.log("IndexedDB need update")
|
||||
stores.forEach(store => {
|
||||
if (!db.objectStoreNames.contains(store))
|
||||
db.createObjectStore(store);
|
||||
})
|
||||
})
|
||||
console.log("Got DATABASE", this.db)
|
||||
l.release();
|
||||
})
|
||||
}
|
||||
|
||||
transaction(...stores: string[] | { name: string }[]) {
|
||||
if (stores.length < 1) stores = this.stores;
|
||||
let s: string[];
|
||||
if (typeof stores[0] === "string")
|
||||
s = <any>stores
|
||||
else
|
||||
s = (<any>stores).map(e => e.name)
|
||||
|
||||
return this.db.transaction(s, "readwrite")
|
||||
}
|
||||
|
||||
getStore<T = any>(name: string) {
|
||||
return {
|
||||
name: name,
|
||||
transaction: () => {
|
||||
return this.db.transaction(name, "readwrite")
|
||||
},
|
||||
get: async (key: string, transaction?: Transaction): Promise<T> => {
|
||||
(await this.initLock.getLock()).release()
|
||||
return (transaction || this.db.transaction(name))
|
||||
.objectStore(name).get(key);
|
||||
},
|
||||
|
||||
getAll: async (transaction?: Transaction): Promise<T[]> => {
|
||||
(await this.initLock.getLock()).release()
|
||||
return (transaction || this.db.transaction(name))
|
||||
.objectStore(name).getAll();
|
||||
},
|
||||
|
||||
set: async (key: string, val: T, transaction?: Transaction) => {
|
||||
(await this.initLock.getLock()).release()
|
||||
const tx = (transaction || this.db.transaction(name, "readwrite"));
|
||||
tx.objectStore(name).put(val, key);
|
||||
return tx.complete;
|
||||
},
|
||||
|
||||
delete: async (key: string, transaction?: Transaction) => {
|
||||
(await this.initLock.getLock()).release()
|
||||
const tx = (transaction || this.db.transaction(name, "readwrite"));
|
||||
tx.objectStore(name).delete(key);
|
||||
return tx.complete;
|
||||
},
|
||||
|
||||
clear: async (transaction?: Transaction) => {
|
||||
(await this.initLock.getLock()).release()
|
||||
const tx = (transaction || this.db.transaction(name, "readwrite"));
|
||||
tx.objectStore(name).clear();
|
||||
return tx.complete;
|
||||
},
|
||||
|
||||
keys: async (transaction?: Transaction): Promise<string[]> => {
|
||||
(await this.initLock.getLock()).release()
|
||||
const tx = (transaction || this.db.transaction(name));
|
||||
const keys: string[] = [];
|
||||
const store = tx.objectStore(name);
|
||||
|
||||
// This would be store.getAllKeys(), but it isn't supported by Edge or Safari.
|
||||
// openKeyCursor isn't supported by Safari, so we fall back
|
||||
(store.iterateKeyCursor || store.iterateCursor).call(store, (cursor: any) => {
|
||||
if (!cursor) return;
|
||||
keys.push(cursor.key);
|
||||
cursor.continue();
|
||||
});
|
||||
|
||||
return tx.complete.then(() => keys);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
36
src/helper/lock.ts
Executable file
36
src/helper/lock.ts
Executable file
@ -0,0 +1,36 @@
|
||||
export type Release = { release: () => void };
|
||||
export default class Lock {
|
||||
private _locked: boolean = false;
|
||||
get locked() {
|
||||
return this._locked;
|
||||
}
|
||||
private toCome: (() => void)[] = [];
|
||||
|
||||
constructor() {
|
||||
this.release = this.release.bind(this);
|
||||
}
|
||||
|
||||
async getLock(): Promise<Release> {
|
||||
if (!this._locked) return { release: this.lock() };
|
||||
else {
|
||||
return new Promise<Release>((resolve) => {
|
||||
this.toCome.push(() => {
|
||||
resolve({ release: this.lock() });
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private lock() {
|
||||
this._locked = true;
|
||||
return this.release;
|
||||
}
|
||||
|
||||
private async release() {
|
||||
if (this.toCome.length > 0) {
|
||||
this.toCome.shift()();
|
||||
} else {
|
||||
this._locked = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
46
src/helper/observable.ts
Executable file
46
src/helper/observable.ts
Executable file
@ -0,0 +1,46 @@
|
||||
export type ObserverCallback<T> = (data: T[]) => void;
|
||||
|
||||
export default class Observable<T = any> {
|
||||
private subscriber: { callback: ObserverCallback<T>, one: boolean }[] = [];
|
||||
private events: T[] = [];
|
||||
private timeout = undefined;
|
||||
|
||||
constructor(private collect: boolean = true, private collect_intervall: number = 100) { }
|
||||
|
||||
getPublicApi() {
|
||||
return {
|
||||
subscribe: (callback: ObserverCallback<T>, one: boolean = false) => {
|
||||
let oldcb = this.subscriber.find(e => e.callback === callback);
|
||||
if (oldcb)
|
||||
oldcb.one = one
|
||||
else
|
||||
this.subscriber.push({ callback, one })
|
||||
},
|
||||
unsubscribe: (callback: ObserverCallback<T>) => {
|
||||
let idx = this.subscriber.findIndex(e => e.callback === callback);
|
||||
if (idx >= 0) {
|
||||
this.subscriber.splice(idx, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
send(data: T) {
|
||||
if (!this.collect)
|
||||
this.subscriber.forEach(e => e.callback([data]));
|
||||
else {
|
||||
this.events.push(data);
|
||||
if (!this.timeout) {
|
||||
this.timeout = setTimeout(() => {
|
||||
this.subscriber.forEach(cb => {
|
||||
if (cb.one)
|
||||
this.events.forEach(e => cb.callback([e]));
|
||||
else
|
||||
cb.callback(this.events)
|
||||
});
|
||||
this.timeout = 0;
|
||||
}, this.collect_intervall);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
77
src/helper/swipe.tsx
Executable file
77
src/helper/swipe.tsx
Executable file
@ -0,0 +1,77 @@
|
||||
import { Component, h, cloneElement } from 'preact';
|
||||
|
||||
export default class SwipeRecognizer extends Component<{ onSwipe?: (direction: string) => void }, { swipe: string }> {
|
||||
private tolerance = 100;
|
||||
private gesture = { x: [], y: [], match: '' };
|
||||
|
||||
componentDidMount() {
|
||||
this.base.addEventListener('touchstart', this.capture);
|
||||
this.base.addEventListener('touchmove', this.capture);
|
||||
this.base.addEventListener('touchend', this.compute)
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.base.removeEventListener('touchstart', this.capture);
|
||||
this.base.removeEventListener('touchmove', this.capture);
|
||||
this.base.removeEventListener('touchend', this.compute);
|
||||
}
|
||||
|
||||
private capture = (event: TouchEvent) => {
|
||||
// event.preventDefault()
|
||||
this.gesture.x.push(event.touches[0].clientX)
|
||||
this.gesture.y.push(event.touches[0].clientY)
|
||||
};
|
||||
|
||||
private compute = (event: TouchEvent) => {
|
||||
// event.preventDefault();
|
||||
let xStart = this.gesture.x[0];
|
||||
let yStart = this.gesture.y[0];
|
||||
let xEnd = this.gesture.x.pop();
|
||||
let yEnd = this.gesture.y.pop();
|
||||
let xTravel = xEnd - xStart;
|
||||
let yTravel = yEnd - yStart;
|
||||
|
||||
console.log(xTravel, yTravel);
|
||||
let xTravel_b = xTravel < 0 ? xTravel * (-1) : xTravel;
|
||||
let yTravel_b = yTravel < 0 ? yTravel * (-1) : yTravel;
|
||||
console.log(xTravel_b, yTravel_b);
|
||||
if (xTravel_b > yTravel_b) {
|
||||
if (xTravel_b > this.tolerance) {
|
||||
if (xTravel > 0) {
|
||||
this.gesture.match = "right";
|
||||
} else {
|
||||
this.gesture.match = "left";
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (yTravel_b > this.tolerance) {
|
||||
if (yTravel > 0) {
|
||||
this.gesture.match = "down";
|
||||
} else {
|
||||
this.gesture.match = "up";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log(this.gesture.match);
|
||||
if (this.gesture.match !== '') {
|
||||
this.onSwipe(this.gesture.match);
|
||||
}
|
||||
|
||||
this.gesture.x = []
|
||||
this.gesture.y = []
|
||||
this.gesture.match = '';
|
||||
};
|
||||
|
||||
onSwipe = (direction: string) => {
|
||||
if (this.props.onSwipe) {
|
||||
this.props.onSwipe(direction);
|
||||
}
|
||||
this.setState({ swipe: direction });
|
||||
};
|
||||
|
||||
|
||||
render({ children }, state) {
|
||||
return cloneElement(children[0], state);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user