| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488 |
- // TODO: This file was created by bulk-decaffeinate.
- // Sanity-check the conversion and remove this comment.
- /*
- * decaffeinate suggestions:
- * DS101: Remove unnecessary use of Array.from
- * DS102: Remove unnecessary code created because of implicit returns
- * DS205: Consider reworking code to avoid use of IIFEs
- * DS206: Consider reworking classes to avoid initClass
- * DS207: Consider shorter variations of null checks
- * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md
- */
- (function() {
- let NAME = undefined;
- let VERSION = undefined;
- const Cls = (app.DB = class DB {
- static initClass() {
- NAME = 'docs';
- VERSION = 15;
- }
- constructor() {
- this.onOpenSuccess = this.onOpenSuccess.bind(this);
- this.onOpenError = this.onOpenError.bind(this);
- this.checkForCorruptedDocs = this.checkForCorruptedDocs.bind(this);
- this.deleteCorruptedDocs = this.deleteCorruptedDocs.bind(this);
- this.versionMultipler = $.isIE() ? 1e5 : 1e9;
- this.useIndexedDB = this.useIndexedDB();
- this.callbacks = [];
- }
- db(fn) {
- if (!this.useIndexedDB) { return fn(); }
- if (fn) { this.callbacks.push(fn); }
- if (this.open) { return; }
- try {
- this.open = true;
- const req = indexedDB.open(NAME, (VERSION * this.versionMultipler) + this.userVersion());
- req.onsuccess = this.onOpenSuccess;
- req.onerror = this.onOpenError;
- req.onupgradeneeded = this.onUpgradeNeeded;
- } catch (error) {
- this.fail('exception', error);
- }
- }
- onOpenSuccess(event) {
- let error;
- const db = event.target.result;
- if (db.objectStoreNames.length === 0) {
- try { db.close(); } catch (error1) {}
- this.open = false;
- this.fail('empty');
- } else if (error = this.buggyIDB(db)) {
- try { db.close(); } catch (error2) {}
- this.open = false;
- this.fail('buggy', error);
- } else {
- this.runCallbacks(db);
- this.open = false;
- db.close();
- }
- }
- onOpenError(event) {
- event.preventDefault();
- this.open = false;
- const {
- error
- } = event.target;
- switch (error.name) {
- case 'QuotaExceededError':
- this.onQuotaExceededError();
- break;
- case 'VersionError':
- this.onVersionError();
- break;
- case 'InvalidStateError':
- this.fail('private_mode');
- break;
- default:
- this.fail('cant_open', error);
- }
- }
- fail(reason, error) {
- this.cachedDocs = null;
- this.useIndexedDB = false;
- if (!this.reason) { this.reason = reason; }
- if (!this.error) { this.error = error; }
- if (error) { if (typeof console.error === 'function') {
- console.error('IDB error', error);
- } }
- this.runCallbacks();
- if (error && (reason === 'cant_open')) {
- Raven.captureMessage(`${error.name}: ${error.message}`, {level: 'warning', fingerprint: [error.name]});
- }
- }
- onQuotaExceededError() {
- this.reset();
- this.db();
- app.onQuotaExceeded();
- Raven.captureMessage('QuotaExceededError', {level: 'warning'});
- }
- onVersionError() {
- const req = indexedDB.open(NAME);
- req.onsuccess = event => {
- return this.handleVersionMismatch(event.target.result.version);
- };
- req.onerror = function(event) {
- event.preventDefault();
- return this.fail('cant_open', error);
- };
- }
- handleVersionMismatch(actualVersion) {
- if (Math.floor(actualVersion / this.versionMultipler) !== VERSION) {
- this.fail('version');
- } else {
- this.setUserVersion(actualVersion - (VERSION * this.versionMultipler));
- this.db();
- }
- }
- buggyIDB(db) {
- if (this.checkedBuggyIDB) { return; }
- this.checkedBuggyIDB = true;
- try {
- this.idbTransaction(db, {stores: $.makeArray(db.objectStoreNames).slice(0, 2), mode: 'readwrite'}).abort(); // https://bugs.webkit.org/show_bug.cgi?id=136937
- return;
- } catch (error) {
- return error;
- }
- }
- runCallbacks(db) {
- let fn;
- while ((fn = this.callbacks.shift())) { fn(db); }
- }
- onUpgradeNeeded(event) {
- let db;
- if (!(db = event.target.result)) { return; }
- const objectStoreNames = $.makeArray(db.objectStoreNames);
- if (!$.arrayDelete(objectStoreNames, 'docs')) {
- try { db.createObjectStore('docs'); } catch (error) {}
- }
- for (var doc of Array.from(app.docs.all())) {
- if (!$.arrayDelete(objectStoreNames, doc.slug)) {
- try { db.createObjectStore(doc.slug); } catch (error1) {}
- }
- }
- for (var name of Array.from(objectStoreNames)) {
- try { db.deleteObjectStore(name); } catch (error2) {}
- }
- }
- store(doc, data, onSuccess, onError, _retry) {
- if (_retry == null) { _retry = true; }
- this.db(db => {
- if (!db) {
- onError();
- return;
- }
- const txn = this.idbTransaction(db, {stores: ['docs', doc.slug], mode: 'readwrite', ignoreError: false});
- txn.oncomplete = () => {
- if (this.cachedDocs != null) {
- this.cachedDocs[doc.slug] = doc.mtime;
- }
- onSuccess();
- };
- txn.onerror = event => {
- event.preventDefault();
- if (((txn.error != null ? txn.error.name : undefined) === 'NotFoundError') && _retry) {
- this.migrate();
- setTimeout(() => {
- return this.store(doc, data, onSuccess, onError, false);
- }
- , 0);
- } else {
- onError(event);
- }
- };
- let store = txn.objectStore(doc.slug);
- store.clear();
- for (var path in data) { var content = data[path]; store.add(content, path); }
- store = txn.objectStore('docs');
- store.put(doc.mtime, doc.slug);
- });
- }
- unstore(doc, onSuccess, onError, _retry) {
- if (_retry == null) { _retry = true; }
- this.db(db => {
- if (!db) {
- onError();
- return;
- }
- const txn = this.idbTransaction(db, {stores: ['docs', doc.slug], mode: 'readwrite', ignoreError: false});
- txn.oncomplete = () => {
- if (this.cachedDocs != null) {
- delete this.cachedDocs[doc.slug];
- }
- onSuccess();
- };
- txn.onerror = function(event) {
- event.preventDefault();
- if (((txn.error != null ? txn.error.name : undefined) === 'NotFoundError') && _retry) {
- this.migrate();
- setTimeout(() => {
- return this.unstore(doc, onSuccess, onError, false);
- }
- , 0);
- } else {
- onError(event);
- }
- };
- let store = txn.objectStore('docs');
- store.delete(doc.slug);
- store = txn.objectStore(doc.slug);
- store.clear();
- });
- }
- version(doc, fn) {
- let version;
- if ((version = this.cachedVersion(doc)) != null) {
- fn(version);
- return;
- }
- this.db(db => {
- if (!db) {
- fn(false);
- return;
- }
- const txn = this.idbTransaction(db, {stores: ['docs'], mode: 'readonly'});
- const store = txn.objectStore('docs');
- const req = store.get(doc.slug);
- req.onsuccess = function() {
- fn(req.result);
- };
- req.onerror = function(event) {
- event.preventDefault();
- fn(false);
- };
- });
- }
- cachedVersion(doc) {
- if (!this.cachedDocs) { return; }
- return this.cachedDocs[doc.slug] || false;
- }
- versions(docs, fn) {
- let versions;
- if (versions = this.cachedVersions(docs)) {
- fn(versions);
- return;
- }
- return this.db(db => {
- if (!db) {
- fn(false);
- return;
- }
- const txn = this.idbTransaction(db, {stores: ['docs'], mode: 'readonly'});
- txn.oncomplete = function() {
- fn(result);
- };
- const store = txn.objectStore('docs');
- var result = {};
- docs.forEach(function(doc) {
- const req = store.get(doc.slug);
- req.onsuccess = function() {
- result[doc.slug] = req.result;
- };
- req.onerror = function(event) {
- event.preventDefault();
- result[doc.slug] = false;
- };
- });
- });
- }
- cachedVersions(docs) {
- if (!this.cachedDocs) { return; }
- const result = {};
- for (var doc of Array.from(docs)) { result[doc.slug] = this.cachedVersion(doc); }
- return result;
- }
- load(entry, onSuccess, onError) {
- if (this.shouldLoadWithIDB(entry)) {
- onError = this.loadWithXHR.bind(this, entry, onSuccess, onError);
- return this.loadWithIDB(entry, onSuccess, onError);
- } else {
- return this.loadWithXHR(entry, onSuccess, onError);
- }
- }
- loadWithXHR(entry, onSuccess, onError) {
- return ajax({
- url: entry.fileUrl(),
- dataType: 'html',
- success: onSuccess,
- error: onError
- });
- }
- loadWithIDB(entry, onSuccess, onError) {
- return this.db(db => {
- if (!db) {
- onError();
- return;
- }
- if (!db.objectStoreNames.contains(entry.doc.slug)) {
- onError();
- this.loadDocsCache(db);
- return;
- }
- const txn = this.idbTransaction(db, {stores: [entry.doc.slug], mode: 'readonly'});
- const store = txn.objectStore(entry.doc.slug);
- const req = store.get(entry.dbPath());
- req.onsuccess = function() {
- if (req.result) { onSuccess(req.result); } else { onError(); }
- };
- req.onerror = function(event) {
- event.preventDefault();
- onError();
- };
- this.loadDocsCache(db);
- });
- }
- loadDocsCache(db) {
- if (this.cachedDocs) { return; }
- this.cachedDocs = {};
- const txn = this.idbTransaction(db, {stores: ['docs'], mode: 'readonly'});
- txn.oncomplete = () => {
- setTimeout(this.checkForCorruptedDocs, 50);
- };
- const req = txn.objectStore('docs').openCursor();
- req.onsuccess = event => {
- let cursor;
- if (!(cursor = event.target.result)) { return; }
- this.cachedDocs[cursor.key] = cursor.value;
- cursor.continue();
- };
- req.onerror = function(event) {
- event.preventDefault();
- };
- }
- checkForCorruptedDocs() {
- this.db(db => {
- let slug;
- this.corruptedDocs = [];
- const docs = ((() => {
- const result = [];
- for (var key in this.cachedDocs) {
- var value = this.cachedDocs[key];
- if (value) {
- result.push(key);
- }
- }
- return result;
- })());
- if (docs.length === 0) { return; }
- for (slug of Array.from(docs)) {
- if (!app.docs.findBy('slug', slug)) {
- this.corruptedDocs.push(slug);
- }
- }
- for (slug of Array.from(this.corruptedDocs)) {
- $.arrayDelete(docs, slug);
- }
- if (docs.length === 0) {
- setTimeout(this.deleteCorruptedDocs, 0);
- return;
- }
- const txn = this.idbTransaction(db, {stores: docs, mode: 'readonly', ignoreError: false});
- txn.oncomplete = () => {
- if (this.corruptedDocs.length > 0) { setTimeout(this.deleteCorruptedDocs, 0); }
- };
- for (var doc of Array.from(docs)) {
- txn.objectStore(doc).get('index').onsuccess = event => {
- if (!event.target.result) { this.corruptedDocs.push(event.target.source.name); }
- };
- }
- });
- }
- deleteCorruptedDocs() {
- this.db(db => {
- let doc;
- const txn = this.idbTransaction(db, {stores: ['docs'], mode: 'readwrite', ignoreError: false});
- const store = txn.objectStore('docs');
- while ((doc = this.corruptedDocs.pop())) {
- this.cachedDocs[doc] = false;
- store.delete(doc);
- }
- });
- Raven.captureMessage('corruptedDocs', {level: 'info', extra: { docs: this.corruptedDocs.join(',') }});
- }
- shouldLoadWithIDB(entry) {
- return this.useIndexedDB && (!this.cachedDocs || this.cachedDocs[entry.doc.slug]);
- }
- idbTransaction(db, options) {
- app.lastIDBTransaction = [options.stores, options.mode];
- const txn = db.transaction(options.stores, options.mode);
- if (options.ignoreError !== false) {
- txn.onerror = function(event) {
- event.preventDefault();
- };
- }
- if (options.ignoreAbort !== false) {
- txn.onabort = function(event) {
- event.preventDefault();
- };
- }
- return txn;
- }
- reset() {
- try { if (typeof indexedDB !== 'undefined' && indexedDB !== null) {
- indexedDB.deleteDatabase(NAME);
- } } catch (error) {}
- }
- useIndexedDB() {
- try {
- if (!app.isSingleDoc() && window.indexedDB) {
- return true;
- } else {
- this.reason = 'not_supported';
- return false;
- }
- } catch (error) {
- return false;
- }
- }
- migrate() {
- app.settings.set('schema', this.userVersion() + 1);
- }
- setUserVersion(version) {
- app.settings.set('schema', version);
- }
- userVersion() {
- return app.settings.get('schema');
- }
- });
- Cls.initClass();
- return Cls;
- })();
|