Uno-Dan

DBMagic

Mar 4th, 2021
875
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1.    
  2. const log = console.log;
  3.  
  4. const indexedDB = window.indexedDB || window.mozIndexedDB ||
  5.     window.webkitIndexedDB || window.msIndexedDB;
  6.    
  7. class Table {
  8.   constructor( db, name) {
  9.     this.db = db;
  10.     this.name = name;
  11.     this.debug = db.debug;
  12.     this.limitSize = 0;
  13.   }
  14.  
  15.   limit( limit ) {
  16.     this.limitSize = limit;
  17.     return this;
  18.   }
  19.  
  20.   order( mode = 'asc' ) {
  21.     this.mode = ( mode === 'asc' ? true : false );
  22.     return this;
  23.   }
  24.  
  25.   orderBy( ...keys ) {
  26.     this.orderby = keys;
  27.     return this;
  28.   }
  29.  
  30.   async bound( ...args ) {
  31.     return IDBKeyRange.lowerBound( ...args );
  32.   }
  33.  
  34.   async add( ...args ) {
  35.     const db = this.db.name;
  36.    
  37.     if ( ! Array.isArray( args ) ) args = [ args ];
  38.    
  39.     const stores = await this.db.stores();
  40.     const version = await this.db.version();
  41.     const create_store = ! stores.contains( this.name );
  42.    
  43.     let promise = false;
  44.    
  45.     if ( create_store ) {
  46.       promise = new Promise( ( resolve, reject ) => {
  47.         let req = indexedDB.open( db, version + 1 );
  48.        
  49.         req.onerror = e => { reject( e ); };
  50.        
  51.         req.onsuccess = () => { req.result.close(); resolve(); };
  52.        
  53.         req.onupgradeneeded = () => {
  54.           req.result.createObjectStore( this.name, { keyPath: 'id' } );
  55.         };
  56.       } );
  57.     }
  58.    
  59.     if ( promise ) await promise;
  60.    
  61.     let promises = [];
  62.    
  63.     const tx = await this.transaction( this.name, 'readwrite' );
  64.    
  65.     return new Promise( ( resolve, reject ) => {
  66.       args.forEach( item => {
  67.         promises.push( this.do_add( tx, item ) );
  68.       } );
  69.       Promise.all( promises )
  70.       .then( () => resolve( this ) )
  71.       .catch( err => reject( err ) );
  72.     } );
  73.   }
  74.  
  75.   async do_add( tx, args) {
  76.     const db = this.db.name;
  77.    
  78.     return new Promise(( resolve, reject ) => {
  79.       let addReq = tx.objectStore( this.name ).add( args );
  80.  
  81.       addReq.onerror = e => {
  82.         if ( this.debug ) {  
  83.           const err = this.db.haltErrors ? 'ERROR' : 'WARN';
  84.          
  85.           const msg = `[ ${ err }:add ], could not add key [${ args.id
  86.           }] in store [${  addReq.source.name }] in database [${ db }], ` +
  87.           e.target.error;
  88.           log( msg );
  89.         }
  90.         this.db.haltErrors ? reject( e ) : resolve( this );  
  91.       };
  92.  
  93.       addReq.onsuccess = () => {  
  94.         if ( this.debug )
  95.           log( `[ INFO:add ], Added key [${ args.id }] in store [${
  96.           addReq.source.name }] in database [${ db }]` );
  97.         resolve( this );  
  98.       };
  99.     } );
  100.   }
  101.  
  102.   async get( ...args ) {
  103.     const sortColumns = ( arr, props, mode = true ) => {
  104.       if ( ! Array.isArray( props ) ) props = [ props ];
  105.      
  106.       let sorted = arr.sort( ( a, b ) => {
  107.         let prop = props[ 0 ];
  108.         for ( let idx = 0; idx < props.length; idx++ ) {
  109.           const curProp = props[ idx ];
  110.           if ( a[ curProp ] !== b[ curProp ] ) {
  111.             prop = curProp;
  112.             break;
  113.           }  
  114.         }  
  115.         if ( a[ prop ] === b[ prop ] ) return 0;
  116.           return ( a[ prop ] > b[ prop ] ? -1 : 1 ) * ( mode ? -1 : 1 );
  117.       } );  
  118.       return sorted;
  119.     };
  120.    
  121.     switch ( typeof args) {
  122.       case 'string': args = parseInt( args );
  123.       case 'number': args = [ args ];
  124.       case 'object':
  125.         if ( Array.isArray( args ) ) {
  126.           args.forEach( ( item, idx ) => {
  127.             if ( typeof item === 'number' )
  128.               args[ idx ] = { id: item };
  129.             else if ( typeof item === 'string' )
  130.               args[ idx ] = { id: parseInt( item) };
  131.           } );
  132.         }
  133.       break;
  134.       case 'undefined': args = false; break;
  135.     }
  136.     if ( ! args.length ) args = false;
  137.    
  138.     const db = this.db.name;
  139.     const stores = await this.db.stores();
  140.     if ( ! stores.contains( this.name ) ) return false;
  141.     if ( args && ! Array.isArray( args ) ) args = [ args ];
  142.    
  143.     let promises = [];
  144.     const tx = await this.transaction( this.name, 'readonly' );
  145.    
  146.     return new Promise( ( resolve, reject ) => {
  147.       if ( args )
  148.         args.forEach( item => { promises.push( this.do_get( tx, item ) ); } );
  149.       else
  150.         promises.push( this.do_get( tx ) );
  151.            
  152.       Promise.all( promises )      
  153.       .then( results => {
  154.        
  155.         let results_copy = [];
  156.         if ( args ) {
  157.           results.forEach( item => results_copy.push( item[0] ) );
  158.           results = results_copy;
  159.          
  160.         } else results = results[ 0 ];
  161.        
  162.         if ( this.limitSize && results.length > 1 ) {
  163.           results = results.slice( 0, this.limitSize );
  164.           this.limitSize = 0;
  165.         }
  166.        
  167.         if ( this.orderby ) {
  168.           results = sortColumns( results, this.orderby, this.mode );
  169.         }
  170.         resolve( results );
  171.       } )
  172.       .catch( err => reject( err ) );
  173.     } );
  174.   }
  175.  
  176.   async do_get( tx, args ) {
  177.     const db = this.db.name;
  178.    
  179.     return new Promise( ( resolve, reject ) => {
  180.       let obj = tx.objectStore( this.name );
  181.      
  182.       let req = args ? obj.get( parseInt( args.id ) ) : obj.getAll();
  183.       req.onerror = e => {
  184.         if ( this.debug )
  185.           log( `[ ERROR:getAll ], could not retrieve keys from store [${
  186.           req.source.name }] in database [${ db }]` );
  187.         reject( e );  
  188.       };
  189.  
  190.       req.onsuccess = () => {
  191.         if ( this.debug )
  192.           log( `[ INFO:getAll ], Retrieved all keys from store [${
  193.           req.source.name }] in database [${ db }]` );
  194.  
  195.         const result = ! Array.isArray( req.result ) ?
  196.         [ req.result ] : req.result;
  197.         resolve( result );  
  198.       };
  199.     } );
  200.   }
  201.          
  202.   async update( ...args ) {
  203.     const db = this.db.name;
  204.     const stores = await this.db.stores();
  205.     if ( ! stores.contains( this.name ) ) return this;
  206.        
  207.     const values = await this.get( ...args );
  208.     args.forEach( ( item, idx ) => {
  209.       let id = item.id;
  210.       let defaults = values.find( x => x.id === id );
  211.       if ( defaults ) {
  212.         item = { ...defaults, ...item };
  213.         args[ idx ] = item;
  214.       }
  215.     } );
  216.    
  217.     let promises = [];
  218.     const tx = await this.transaction( this.name, 'readwrite' );
  219.     return new Promise( ( resolve, reject ) => {
  220.      
  221.       args.forEach( item => { promises.push( this.do_update( tx, item ) ); } );
  222.       Promise.all( promises )
  223.       .then( () => resolve( this ) )
  224.       .catch( err => reject( err ) );
  225.     } );
  226.   }
  227.  
  228.   async do_update( tx, args ) {
  229.     const db = this.db.name;
  230.     return new Promise(( resolve, reject ) => {
  231.       let addReq = tx.objectStore( this.name ).put( args );
  232.  
  233.       addReq.onerror = e => {
  234.         if ( this.debug )
  235.           log( `[ ERROR:add ], could not update key [${ args.id
  236.           }] in store [${ addReq.source.name }] in database [${ db }]` );
  237.         reject( e );  
  238.       };
  239.  
  240.       addReq.onsuccess = () => {  
  241.         if ( this.debug )
  242.           log( `[ INFO:update ], Updated key [${ args.id }] in store [${
  243.           addReq.source.name }] in database [${ db }]` );
  244.         resolve( this );  
  245.       };
  246.     } );
  247.   }
  248.  
  249.   async delete( ...args ) {
  250.     const db = this.db.name;
  251.     const stores = await this.db.stores();
  252.     const version = await this.db.version();
  253.    
  254.     switch ( typeof args) {
  255.       case 'string': args = parseInt( args );
  256.       case 'number': args = [ args ];
  257.       case 'object':
  258.         if ( Array.isArray( args ) ) {
  259.           args.forEach( ( item, idx ) => {
  260.             if ( typeof item === 'number' )
  261.               args[ idx ] = { id: item };
  262.             else if ( typeof item === 'string' )
  263.               args[ idx ] = { id: parseInt( item) };
  264.           } );
  265.         }
  266.       break;
  267.       case 'undefined': args = false; break;
  268.     }
  269.     if ( ! args.length ) args = false;
  270.    
  271.     let promises = [];
  272.    
  273.     if ( ! args )
  274.       return new Promise( ( resolve, reject ) => {
  275.         var req = indexedDB.open( this.db.name, version + 1  );
  276.  
  277.         req.onerror = e => reject( e );
  278.  
  279.         req.onsuccess = ( ) => { req.result.close(); resolve( this ); };
  280.  
  281.         req.onupgradeneeded = () => {
  282.           req.result.deleteObjectStore( this.name );
  283.         };
  284.       } );
  285.    
  286.     else return new Promise( ( resolve, reject ) => {
  287.       args.forEach( item => { promises.push( this.do_delete( item ) ); } );
  288.       Promise.all( promises )
  289.       .then( () => resolve( this ) )
  290.       .catch( err => reject( err ) );
  291.     } );
  292.    
  293.   }  
  294.  
  295.   async do_delete( args ) {
  296.     const db = this.db.name;
  297.     const tx = await this.transaction( this.name, 'readwrite' );
  298.    
  299.     return new Promise(( resolve, reject ) => {
  300.       let req = tx.objectStore( this.name ).delete( args.id );
  301.  
  302.       req.onerror = e => {
  303.         if ( this.debug ) {  
  304.           const err = this.db.haltErrors ? 'ERROR' : 'WARN';
  305.           const msg = `[ ${ err }:add ], could not add key [${ args.id
  306.           }] in store [${  req.source.name }] in database [${ db }], ` +
  307.           e.target.error;
  308.           log( msg );
  309.         }
  310.         this.db.haltErrors ? reject( e ) : resolve( this );
  311.       };
  312.  
  313.       req.onsuccess = () => {  
  314.         if ( this.debug )
  315.           log( `[ INFO:add ], Deleted key [${ args.id }] from store [${
  316.           req.source.name }] in database [${ db }]` );
  317.         resolve( this );  
  318.       };
  319.     } );
  320.   }
  321.  
  322.   async addIndex( ...keys ) {
  323.     const db = this.db.name;
  324.     let version = await this.db.version();
  325.        
  326.     return new Promise( ( resolve, reject ) => {
  327.       var req = indexedDB.open( this.db.name, version + 1  );
  328.  
  329.       req.onerror = e => reject( e );
  330.  
  331.       req.onsuccess = ( ) => { req.result.close(); resolve( this ); };
  332.  
  333.       req.onupgradeneeded = ( e ) => {
  334.         let db = e.target.result;
  335.         let tx = event.target.transaction;
  336.        
  337.         let objectStore = ( ! db.objectStoreNames.contains( this.name ) ) ?
  338.         db.createObjectStore( this.name ) : tx.objectStore( this.name );
  339.  
  340.         keys.forEach( key => {
  341.           if ( ! objectStore.indexNames.contains( `${ key }IDX` ) ) {
  342.             objectStore.createIndex( `${ key }IDX`, key, {  unique: false } );
  343.           }
  344.         } );
  345.       };
  346.     } );
  347.   }
  348.  
  349.   async getIndex( index ) {
  350.     let tx = await this.transaction( this.name );
  351.     let store = tx.objectStore( this.name );
  352.     return store.index( `${index}IDX` );
  353.   }
  354.  
  355.   async transaction( store, mode ) {    
  356.     const db = this.db.name;  
  357.     if ( ! Array.isArray( store ) ) store = [ store ];
  358.    
  359.     return new Promise(( resolve, reject ) => {
  360.       let openReq = indexedDB.open( db );
  361.  
  362.       openReq.onerror = e => reject( e );
  363.  
  364.       openReq.onsuccess = () => {
  365.         const tx = openReq.result.transaction ( store, mode );
  366.         resolve( tx );
  367.         openReq.result.close();
  368.       };
  369.     } );
  370.   }
  371. }
  372.  
  373. export default class DBMagic {
  374.   constructor( name ) {
  375.     this.name = name;
  376.     this.debug = false;
  377.     this.tables = {};
  378.     this.init();
  379.   }
  380.  
  381.   init() {
  382.     Object.values( this.stores() ).forEach( name => this.table( name ) );
  383.   }
  384.  
  385.   table( name ) {
  386.     let args = {}
  387.     if ( ! ( name in this.tables ) ) {
  388.       this.tables[ name ] = new Table( this, name, args );
  389.     }
  390.     return this.tables[ name ];
  391.   }
  392.  
  393.   config( args ) {
  394.     for (var key in args )
  395.       this[ key ] = args[ key ];
  396.     return this;
  397.   }
  398.  
  399.   delete( args ) {
  400.     if ( args ) {
  401.       if ( ! Array.isArray( args ) ) args = [ args ];
  402.    
  403.       args.forEach( store => {
  404.         if ( store in this.tables ) {
  405.           this.tables[ store ].delete();
  406.           delete this.tables[ store ];
  407.         }
  408.       } );
  409.     } else {
  410.       return new Promise( ( resolve, reject ) => {
  411.         var req = indexedDB.deleteDatabase( this.name );
  412.  
  413.         req.onerror = err => { reject( err ); };
  414.  
  415.         req.onsuccess = () => {
  416.           if( this.debug )
  417.             log( '[INFO:delete], Deleted database [' + this.name + ']');
  418.          
  419.           this.tables = {};
  420.           resolve( this );
  421.         };
  422.       } );
  423.     }
  424.   }
  425.  
  426.   stores() {
  427.     return new Promise( ( resolve, reject ) => {
  428.       var req = indexedDB.open( this.name );
  429.  
  430.       req.onerror = err => { reject( err ); };
  431.  
  432.       req.onsuccess = () => {
  433.         let names = req.result.objectStoreNames;
  434.         if( this.debug )
  435.           log( '[INFO:db_version], Database [' + this.name +
  436.           '], version []');
  437.  
  438.         resolve( names );
  439.         req.result.close();
  440.       };
  441.      
  442.       req.onupgradeneeded = () => { return true; };
  443.     } );
  444.   }
  445.  
  446.   version() {
  447.     const db = this.name;
  448.     return new Promise( ( resolve, reject ) => {
  449.       var req = indexedDB.open( db );
  450.  
  451.       req.onerror = err => { reject( err ); };
  452.  
  453.       req.onsuccess = () => {
  454.         let version = req.result.version;
  455.         if( this.debug )
  456.           log( '[INFO:db_version], Database [' + db +
  457.           '], version [' + version + ']');
  458.  
  459.         req.result.close();
  460.         resolve( version );
  461.       };
  462.       req.onupgradeneeded = () => { return true; };
  463.     } );
  464.   }
  465. }
  466.  
RAW Paste Data

Adblocker detected! Please consider disabling it...

We've detected AdBlock Plus or some other adblocking software preventing Pastebin.com from fully loading.

We don't have any obnoxious sound, or popup ads, we actively block these annoying types of ads!

Please add Pastebin.com to your ad blocker whitelist or disable your adblocking software.

×