PageSourceSearch

https://www.flickgame.org/gallery_store.js

js flickgame.org collected 2026-10-03 19:39:40 UTC 5,063 bytes, 148 lines download raw bytes

1// IndexedDB-backed gallery storage for Flickgame projects.
2(function () {
3  'use strict';
4
5  var DB_NAME = 'flickgame';
6  var DB_VERSION = 1;
7  var STORE_PROJECTS = 'projects';
8  var dbPromise = null;
9
10  function nowMs() { return Date.now(); }
11
12  function uuid() {
13    if (typeof crypto !== 'undefined' && crypto.randomUUID) return crypto.randomUUID();
14    // Fallback UUIDv4-ish
15    var s = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
16      var r = Math.random() * 16 | 0;
17      var v = c === 'x' ? r : (r & 0x3 | 0x8);
18      return v.toString(16);
19    });
20    return s;
21  }
22
23  function openDb() {
24    if (dbPromise) return dbPromise;
25    dbPromise = new Promise(function (resolve, reject) {
26      if (typeof indexedDB === 'undefined') {
27        reject(new Error('IndexedDB not available'));
28        return;
29      }
30      var req = indexedDB.open(DB_NAME, DB_VERSION);
31      req.onerror = function () { reject(req.error || new Error('Failed to open IndexedDB')); };
32      req.onupgradeneeded = function () {
33        var db = req.result;
34        if (!db.objectStoreNames.contains(STORE_PROJECTS)) {
35          var store = db.createObjectStore(STORE_PROJECTS, { keyPath: 'id' });
36          store.createIndex('updatedAt', 'updatedAt', { unique: false });
37          store.createIndex('createdAt', 'createdAt', { unique: false });
38        }
39      };
40      req.onsuccess = function () { resolve(req.result); };
41    });
42    return dbPromise;
43  }
44
45  function withStore(mode, fn) {
46    return openDb().then(function (db) {
47      return new Promise(function (resolve, reject) {
48        var tx = db.transaction(STORE_PROJECTS, mode);
49        var store = tx.objectStore(STORE_PROJECTS);
50        var result;
51        try { result = fn(store, tx); } catch (e) { reject(e); return; }
52
53        var txDone = false;
54        var txError = null;
55        tx.oncomplete = function () { txDone = true; maybeResolve(); };
56        tx.onerror = function () { reject(tx.error || new Error('IndexedDB transaction failed')); };
57        tx.onabort = function () { reject(tx.error || new Error('IndexedDB transaction aborted')); };
58
59        var resultPromise = Promise.resolve(result);
60        var resultDone = false;
61        var resultValue;
62        resultPromise.then(function (v) { resultDone = true; resultValue = v; maybeResolve(); }, function (e) {
63          // If the caller's request promise fails, surface that error (tx may still abort too).
64          txError = e || new Error('IndexedDB request failed');
65          reject(txError);
66        });
67
68        function maybeResolve() {
69          if (!txDone) return;
70          if (txError) return;
71          if (!resultDone) return;
72          resolve(resultValue);
73        }
74      });
75    });
76  }
77
78  function reqToPromise(req) {
79    return new Promise(function (resolve, reject) {
80      req.onsuccess = function () { resolve(req.result); };
81      req.onerror = function () { reject(req.error || new Error('IndexedDB request failed')); };
82    });
83  }
84
85  function listProjects() {
86    return openDb().then(function (db) {
87      return new Promise(function (resolve, reject) {
88        var tx = db.transaction(STORE_PROJECTS, 'readonly');
89        var store = tx.objectStore(STORE_PROJECTS);
90        var idx = store.index('updatedAt');
91        var out = [];
92        var cursorReq = idx.openCursor(null, 'prev');
93        cursorReq.onerror = function () { reject(cursorReq.error || new Error('Failed to iterate projects')); };
94        cursorReq.onsuccess = function () {
95          var cur = cursorReq.result;
96          if (!cur) { resolve(out); return; }
97          out.push(cur.value);
98          cur.continue();
99        };
100      });
101    });
102  }
103
104  function getProject(id) {
105    return withStore('readonly', function (store) {
106      return reqToPromise(store.get(id));
107    });
108  }
109
110  function putProject(project) {
111    var p = Object.assign({}, project);
112    if (!p.id) p.id = uuid();
113    if (!p.title) p.title = 'Untitled';
114    if (!p.createdAt) p.createdAt = nowMs();
115    p.updatedAt = nowMs();
116    return withStore('readwrite', function (store) {
117      return reqToPromise(store.put(p)).then(function () { return p; });
118    });
119  }
120
121  function deleteProject(id) {
122    return withStore('readwrite', function (store) {
123      return reqToPromise(store.delete(id)).then(function () { return true; });
124    });
125  }
126
127  function isEmpty() {
128    return openDb().then(function (db) {
129      return new Promise(function (resolve, reject) {
130        var tx = db.transaction(STORE_PROJECTS, 'readonly');
131        var store = tx.objectStore(STORE_PROJECTS);
132        var countReq = store.count();
133        countReq.onerror = function () { reject(countReq.error || new Error('Failed to count projects')); };
134        countReq.onsuccess = function () { resolve((countReq.result || 0) === 0); };
135      });
136    });
137  }
138
139  window.FlickGalleryStore = {
140    openDb: openDb,
141    listProjects: listProjects,
142    getProject: getProject,
143    putProject: putProject,
144    deleteProject: deleteProject,
145    isEmpty: isEmpty,
146    _uuid: uuid
147  };
148})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.