Advertisement
Uno-Dan

DBMagic Improved

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