Repositorio del curso CCOM4030 el semestre B91 del proyecto Artesanías con el Instituto de Cultura

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610
  1. import isWebSQLValid from '../utils/isWebSQLValid';
  2. import serializer from '../utils/serializer';
  3. import Promise from '../utils/promise';
  4. import executeCallback from '../utils/executeCallback';
  5. import normalizeKey from '../utils/normalizeKey';
  6. import getCallback from '../utils/getCallback';
  7. /*
  8. * Includes code from:
  9. *
  10. * base64-arraybuffer
  11. * https://github.com/niklasvh/base64-arraybuffer
  12. *
  13. * Copyright (c) 2012 Niklas von Hertzen
  14. * Licensed under the MIT license.
  15. */
  16. function createDbTable(t, dbInfo, callback, errorCallback) {
  17. t.executeSql(
  18. `CREATE TABLE IF NOT EXISTS ${dbInfo.storeName} ` +
  19. '(id INTEGER PRIMARY KEY, key unique, value)',
  20. [],
  21. callback,
  22. errorCallback
  23. );
  24. }
  25. // Open the WebSQL database (automatically creates one if one didn't
  26. // previously exist), using any options set in the config.
  27. function _initStorage(options) {
  28. var self = this;
  29. var dbInfo = {
  30. db: null
  31. };
  32. if (options) {
  33. for (var i in options) {
  34. dbInfo[i] =
  35. typeof options[i] !== 'string'
  36. ? options[i].toString()
  37. : options[i];
  38. }
  39. }
  40. var dbInfoPromise = new Promise(function(resolve, reject) {
  41. // Open the database; the openDatabase API will automatically
  42. // create it for us if it doesn't exist.
  43. try {
  44. dbInfo.db = openDatabase(
  45. dbInfo.name,
  46. String(dbInfo.version),
  47. dbInfo.description,
  48. dbInfo.size
  49. );
  50. } catch (e) {
  51. return reject(e);
  52. }
  53. // Create our key/value table if it doesn't exist.
  54. dbInfo.db.transaction(function(t) {
  55. createDbTable(
  56. t,
  57. dbInfo,
  58. function() {
  59. self._dbInfo = dbInfo;
  60. resolve();
  61. },
  62. function(t, error) {
  63. reject(error);
  64. }
  65. );
  66. }, reject);
  67. });
  68. dbInfo.serializer = serializer;
  69. return dbInfoPromise;
  70. }
  71. function tryExecuteSql(t, dbInfo, sqlStatement, args, callback, errorCallback) {
  72. t.executeSql(
  73. sqlStatement,
  74. args,
  75. callback,
  76. function(t, error) {
  77. if (error.code === error.SYNTAX_ERR) {
  78. t.executeSql(
  79. 'SELECT name FROM sqlite_master ' +
  80. "WHERE type='table' AND name = ?",
  81. [dbInfo.storeName],
  82. function(t, results) {
  83. if (!results.rows.length) {
  84. // if the table is missing (was deleted)
  85. // re-create it table and retry
  86. createDbTable(
  87. t,
  88. dbInfo,
  89. function() {
  90. t.executeSql(
  91. sqlStatement,
  92. args,
  93. callback,
  94. errorCallback
  95. );
  96. },
  97. errorCallback
  98. );
  99. } else {
  100. errorCallback(t, error);
  101. }
  102. },
  103. errorCallback
  104. );
  105. } else {
  106. errorCallback(t, error);
  107. }
  108. },
  109. errorCallback
  110. );
  111. }
  112. function getItem(key, callback) {
  113. var self = this;
  114. key = normalizeKey(key);
  115. var promise = new Promise(function(resolve, reject) {
  116. self
  117. .ready()
  118. .then(function() {
  119. var dbInfo = self._dbInfo;
  120. dbInfo.db.transaction(function(t) {
  121. tryExecuteSql(
  122. t,
  123. dbInfo,
  124. `SELECT * FROM ${
  125. dbInfo.storeName
  126. } WHERE key = ? LIMIT 1`,
  127. [key],
  128. function(t, results) {
  129. var result = results.rows.length
  130. ? results.rows.item(0).value
  131. : null;
  132. // Check to see if this is serialized content we need to
  133. // unpack.
  134. if (result) {
  135. result = dbInfo.serializer.deserialize(result);
  136. }
  137. resolve(result);
  138. },
  139. function(t, error) {
  140. reject(error);
  141. }
  142. );
  143. });
  144. })
  145. .catch(reject);
  146. });
  147. executeCallback(promise, callback);
  148. return promise;
  149. }
  150. function iterate(iterator, callback) {
  151. var self = this;
  152. var promise = new Promise(function(resolve, reject) {
  153. self
  154. .ready()
  155. .then(function() {
  156. var dbInfo = self._dbInfo;
  157. dbInfo.db.transaction(function(t) {
  158. tryExecuteSql(
  159. t,
  160. dbInfo,
  161. `SELECT * FROM ${dbInfo.storeName}`,
  162. [],
  163. function(t, results) {
  164. var rows = results.rows;
  165. var length = rows.length;
  166. for (var i = 0; i < length; i++) {
  167. var item = rows.item(i);
  168. var result = item.value;
  169. // Check to see if this is serialized content
  170. // we need to unpack.
  171. if (result) {
  172. result = dbInfo.serializer.deserialize(
  173. result
  174. );
  175. }
  176. result = iterator(result, item.key, i + 1);
  177. // void(0) prevents problems with redefinition
  178. // of `undefined`.
  179. if (result !== void 0) {
  180. resolve(result);
  181. return;
  182. }
  183. }
  184. resolve();
  185. },
  186. function(t, error) {
  187. reject(error);
  188. }
  189. );
  190. });
  191. })
  192. .catch(reject);
  193. });
  194. executeCallback(promise, callback);
  195. return promise;
  196. }
  197. function _setItem(key, value, callback, retriesLeft) {
  198. var self = this;
  199. key = normalizeKey(key);
  200. var promise = new Promise(function(resolve, reject) {
  201. self
  202. .ready()
  203. .then(function() {
  204. // The localStorage API doesn't return undefined values in an
  205. // "expected" way, so undefined is always cast to null in all
  206. // drivers. See: https://github.com/mozilla/localForage/pull/42
  207. if (value === undefined) {
  208. value = null;
  209. }
  210. // Save the original value to pass to the callback.
  211. var originalValue = value;
  212. var dbInfo = self._dbInfo;
  213. dbInfo.serializer.serialize(value, function(value, error) {
  214. if (error) {
  215. reject(error);
  216. } else {
  217. dbInfo.db.transaction(
  218. function(t) {
  219. tryExecuteSql(
  220. t,
  221. dbInfo,
  222. `INSERT OR REPLACE INTO ${
  223. dbInfo.storeName
  224. } ` + '(key, value) VALUES (?, ?)',
  225. [key, value],
  226. function() {
  227. resolve(originalValue);
  228. },
  229. function(t, error) {
  230. reject(error);
  231. }
  232. );
  233. },
  234. function(sqlError) {
  235. // The transaction failed; check
  236. // to see if it's a quota error.
  237. if (sqlError.code === sqlError.QUOTA_ERR) {
  238. // We reject the callback outright for now, but
  239. // it's worth trying to re-run the transaction.
  240. // Even if the user accepts the prompt to use
  241. // more storage on Safari, this error will
  242. // be called.
  243. //
  244. // Try to re-run the transaction.
  245. if (retriesLeft > 0) {
  246. resolve(
  247. _setItem.apply(self, [
  248. key,
  249. originalValue,
  250. callback,
  251. retriesLeft - 1
  252. ])
  253. );
  254. return;
  255. }
  256. reject(sqlError);
  257. }
  258. }
  259. );
  260. }
  261. });
  262. })
  263. .catch(reject);
  264. });
  265. executeCallback(promise, callback);
  266. return promise;
  267. }
  268. function setItem(key, value, callback) {
  269. return _setItem.apply(this, [key, value, callback, 1]);
  270. }
  271. function removeItem(key, callback) {
  272. var self = this;
  273. key = normalizeKey(key);
  274. var promise = new Promise(function(resolve, reject) {
  275. self
  276. .ready()
  277. .then(function() {
  278. var dbInfo = self._dbInfo;
  279. dbInfo.db.transaction(function(t) {
  280. tryExecuteSql(
  281. t,
  282. dbInfo,
  283. `DELETE FROM ${dbInfo.storeName} WHERE key = ?`,
  284. [key],
  285. function() {
  286. resolve();
  287. },
  288. function(t, error) {
  289. reject(error);
  290. }
  291. );
  292. });
  293. })
  294. .catch(reject);
  295. });
  296. executeCallback(promise, callback);
  297. return promise;
  298. }
  299. // Deletes every item in the table.
  300. // TODO: Find out if this resets the AUTO_INCREMENT number.
  301. function clear(callback) {
  302. var self = this;
  303. var promise = new Promise(function(resolve, reject) {
  304. self
  305. .ready()
  306. .then(function() {
  307. var dbInfo = self._dbInfo;
  308. dbInfo.db.transaction(function(t) {
  309. tryExecuteSql(
  310. t,
  311. dbInfo,
  312. `DELETE FROM ${dbInfo.storeName}`,
  313. [],
  314. function() {
  315. resolve();
  316. },
  317. function(t, error) {
  318. reject(error);
  319. }
  320. );
  321. });
  322. })
  323. .catch(reject);
  324. });
  325. executeCallback(promise, callback);
  326. return promise;
  327. }
  328. // Does a simple `COUNT(key)` to get the number of items stored in
  329. // localForage.
  330. function length(callback) {
  331. var self = this;
  332. var promise = new Promise(function(resolve, reject) {
  333. self
  334. .ready()
  335. .then(function() {
  336. var dbInfo = self._dbInfo;
  337. dbInfo.db.transaction(function(t) {
  338. // Ahhh, SQL makes this one soooooo easy.
  339. tryExecuteSql(
  340. t,
  341. dbInfo,
  342. `SELECT COUNT(key) as c FROM ${dbInfo.storeName}`,
  343. [],
  344. function(t, results) {
  345. var result = results.rows.item(0).c;
  346. resolve(result);
  347. },
  348. function(t, error) {
  349. reject(error);
  350. }
  351. );
  352. });
  353. })
  354. .catch(reject);
  355. });
  356. executeCallback(promise, callback);
  357. return promise;
  358. }
  359. // Return the key located at key index X; essentially gets the key from a
  360. // `WHERE id = ?`. This is the most efficient way I can think to implement
  361. // this rarely-used (in my experience) part of the API, but it can seem
  362. // inconsistent, because we do `INSERT OR REPLACE INTO` on `setItem()`, so
  363. // the ID of each key will change every time it's updated. Perhaps a stored
  364. // procedure for the `setItem()` SQL would solve this problem?
  365. // TODO: Don't change ID on `setItem()`.
  366. function key(n, callback) {
  367. var self = this;
  368. var promise = new Promise(function(resolve, reject) {
  369. self
  370. .ready()
  371. .then(function() {
  372. var dbInfo = self._dbInfo;
  373. dbInfo.db.transaction(function(t) {
  374. tryExecuteSql(
  375. t,
  376. dbInfo,
  377. `SELECT key FROM ${
  378. dbInfo.storeName
  379. } WHERE id = ? LIMIT 1`,
  380. [n + 1],
  381. function(t, results) {
  382. var result = results.rows.length
  383. ? results.rows.item(0).key
  384. : null;
  385. resolve(result);
  386. },
  387. function(t, error) {
  388. reject(error);
  389. }
  390. );
  391. });
  392. })
  393. .catch(reject);
  394. });
  395. executeCallback(promise, callback);
  396. return promise;
  397. }
  398. function keys(callback) {
  399. var self = this;
  400. var promise = new Promise(function(resolve, reject) {
  401. self
  402. .ready()
  403. .then(function() {
  404. var dbInfo = self._dbInfo;
  405. dbInfo.db.transaction(function(t) {
  406. tryExecuteSql(
  407. t,
  408. dbInfo,
  409. `SELECT key FROM ${dbInfo.storeName}`,
  410. [],
  411. function(t, results) {
  412. var keys = [];
  413. for (var i = 0; i < results.rows.length; i++) {
  414. keys.push(results.rows.item(i).key);
  415. }
  416. resolve(keys);
  417. },
  418. function(t, error) {
  419. reject(error);
  420. }
  421. );
  422. });
  423. })
  424. .catch(reject);
  425. });
  426. executeCallback(promise, callback);
  427. return promise;
  428. }
  429. // https://www.w3.org/TR/webdatabase/#databases
  430. // > There is no way to enumerate or delete the databases available for an origin from this API.
  431. function getAllStoreNames(db) {
  432. return new Promise(function(resolve, reject) {
  433. db.transaction(
  434. function(t) {
  435. t.executeSql(
  436. 'SELECT name FROM sqlite_master ' +
  437. "WHERE type='table' AND name <> '__WebKitDatabaseInfoTable__'",
  438. [],
  439. function(t, results) {
  440. var storeNames = [];
  441. for (var i = 0; i < results.rows.length; i++) {
  442. storeNames.push(results.rows.item(i).name);
  443. }
  444. resolve({
  445. db,
  446. storeNames
  447. });
  448. },
  449. function(t, error) {
  450. reject(error);
  451. }
  452. );
  453. },
  454. function(sqlError) {
  455. reject(sqlError);
  456. }
  457. );
  458. });
  459. }
  460. function dropInstance(options, callback) {
  461. callback = getCallback.apply(this, arguments);
  462. var currentConfig = this.config();
  463. options = (typeof options !== 'function' && options) || {};
  464. if (!options.name) {
  465. options.name = options.name || currentConfig.name;
  466. options.storeName = options.storeName || currentConfig.storeName;
  467. }
  468. var self = this;
  469. var promise;
  470. if (!options.name) {
  471. promise = Promise.reject('Invalid arguments');
  472. } else {
  473. promise = new Promise(function(resolve) {
  474. var db;
  475. if (options.name === currentConfig.name) {
  476. // use the db reference of the current instance
  477. db = self._dbInfo.db;
  478. } else {
  479. db = openDatabase(options.name, '', '', 0);
  480. }
  481. if (!options.storeName) {
  482. // drop all database tables
  483. resolve(getAllStoreNames(db));
  484. } else {
  485. resolve({
  486. db,
  487. storeNames: [options.storeName]
  488. });
  489. }
  490. }).then(function(operationInfo) {
  491. return new Promise(function(resolve, reject) {
  492. operationInfo.db.transaction(
  493. function(t) {
  494. function dropTable(storeName) {
  495. return new Promise(function(resolve, reject) {
  496. t.executeSql(
  497. `DROP TABLE IF EXISTS ${storeName}`,
  498. [],
  499. function() {
  500. resolve();
  501. },
  502. function(t, error) {
  503. reject(error);
  504. }
  505. );
  506. });
  507. }
  508. var operations = [];
  509. for (
  510. var i = 0, len = operationInfo.storeNames.length;
  511. i < len;
  512. i++
  513. ) {
  514. operations.push(
  515. dropTable(operationInfo.storeNames[i])
  516. );
  517. }
  518. Promise.all(operations)
  519. .then(function() {
  520. resolve();
  521. })
  522. .catch(function(e) {
  523. reject(e);
  524. });
  525. },
  526. function(sqlError) {
  527. reject(sqlError);
  528. }
  529. );
  530. });
  531. });
  532. }
  533. executeCallback(promise, callback);
  534. return promise;
  535. }
  536. var webSQLStorage = {
  537. _driver: 'webSQLStorage',
  538. _initStorage: _initStorage,
  539. _support: isWebSQLValid(),
  540. iterate: iterate,
  541. getItem: getItem,
  542. setItem: setItem,
  543. removeItem: removeItem,
  544. clear: clear,
  545. length: length,
  546. key: key,
  547. keys: keys,
  548. dropInstance: dropInstance
  549. };
  550. export default webSQLStorage;