home *** CD-ROM | disk | FTP | other *** search
/ Orthodox Religion Class (2nd Semester) / ReligieCls1Sem2.iso / js / lib / localstoragedb.js < prev    next >
Encoding:
JavaScript  |  2014-06-29  |  15.5 KB  |  630 lines

  1. /*
  2.     Kailash Nadh (http://nadh.in)
  3.     
  4.     localStorageDB
  5.     September 2011
  6.     A simple database layer for localStorage
  7.  
  8.     v 1.9 November 2012
  9.     v 2.0 June 2013
  10.     v 2.1 Nov 2013
  11.     v 2.2 Jan 2014 Contribution: Andy Hawkins (http://a904guy.com) 
  12.  
  13.     License    :    MIT License
  14. */
  15.  
  16. function localStorageDB(db_name, engine) {
  17.  
  18.     var db_prefix = 'db_',
  19.         db_id = db_prefix + db_name,
  20.         db_new = false,    // this flag determines whether a new database was created during an object initialisation
  21.         db = null;
  22.  
  23.         try {
  24.             var storage = (engine == sessionStorage ? sessionStorage: localStorage);
  25.         } catch(e) { // ie8 hack
  26.             var storage = engine;
  27.         }
  28.  
  29.     // if the database doesn't exist, create it
  30.     db = storage[ db_id ];
  31.     if( !( db && (db = JSON.parse(db)) && db.tables && db.data ) ) {
  32.         if(!validateName(db_name)) {
  33.             error("The name '" + db_name + "'" + " contains invalid characters.");
  34.         } else {
  35.             db = {tables: {}, data: {}};
  36.             commit();
  37.             db_new = true;
  38.         }
  39.     }
  40.     
  41.     
  42.     // ______________________ private methods
  43.     
  44.     // _________ database functions
  45.     // drop the database
  46.     function drop() {
  47.         delete storage[db_id];
  48.         db = null;
  49.     }
  50.             
  51.     // number of tables in the database
  52.     function tableCount() {
  53.         var count = 0;
  54.         for(var table in db.tables) {
  55.             if( db.tables.hasOwnProperty(table) ) {
  56.                 count++;
  57.             }
  58.         }
  59.         return count;
  60.     }
  61.  
  62.     // _________ table functions
  63.     
  64.     // returns all fields in a table.
  65.     function tableFields(table_name) {
  66.         return db.tables[table_name].fields;
  67.     }
  68.  
  69.     // check whether a table exists
  70.     function tableExists(table_name) {
  71.         return db.tables[table_name] ? true : false;
  72.     }
  73.     
  74.     // check whether a table exists, and if not, throw an error
  75.     function tableExistsWarn(table_name) {
  76.         if(!tableExists(table_name)) {
  77.             error("The table '" + table_name + "' does not exist.");
  78.         }
  79.     }
  80.  
  81.     // check whether a table column exists
  82.     function columnExists(table_name, field_name) {
  83.         var exists = false;
  84.         var table_fields = db.tables[table_name].fields;
  85.         for(var field in table_fields){
  86.             if(table_fields[field] == field_name)
  87.             {
  88.                 exists = true;
  89.                 break;
  90.             }
  91.         }
  92.         return exists;
  93.     }
  94.         
  95.     // create a table
  96.     function createTable(table_name, fields) {
  97.         db.tables[table_name] = {fields: fields, auto_increment: 1};
  98.         db.data[table_name] = {};
  99.     }
  100.  
  101.     // drop a table
  102.     function dropTable(table_name) {
  103.         delete db.tables[table_name];
  104.         delete db.data[table_name];
  105.     }
  106.     
  107.     // empty a table
  108.     function truncate(table_name) {
  109.         db.tables[table_name].auto_increment = 1;
  110.         db.data[table_name] = {};
  111.     }
  112.  
  113.     //alter a table
  114.     function alterTable(table_name, new_fields, default_values){
  115.         db.tables[table_name].fields = db.tables[table_name].fields.concat(new_fields);
  116.  
  117.         // insert default values in existing table
  118.         if(typeof default_values != "undefined")
  119.         {
  120.             // loop through all the records in the table
  121.             for(var ID in db.data[table_name]) {
  122.                 if( !db.data[table_name].hasOwnProperty(ID) ) {
  123.                     continue;
  124.                 }
  125.                 for(var field in new_fields)
  126.                 {
  127.                     if(typeof default_values == "object")
  128.                         db.data[table_name][ID][new_fields[field]] = default_values[new_fields[field]];
  129.                     else
  130.                         db.data[table_name][ID][new_fields[field]] = default_values;
  131.                 }
  132.             }
  133.         }    
  134.     }
  135.     
  136.     // number of rows in a table
  137.     function rowCount(table_name) {
  138.         var count = 0;
  139.         for(var ID in db.data[table_name]) {
  140.             if( db.data[table_name].hasOwnProperty(ID) ) {
  141.                 count++;
  142.             }
  143.         }
  144.         return count;
  145.     }
  146.     
  147.     // insert a new row
  148.     function insert(table_name, data) {
  149.         data.ID = db.tables[table_name].auto_increment;
  150.         db.data[table_name][ db.tables[table_name].auto_increment ] = data;
  151.         db.tables[table_name].auto_increment++;
  152.         return data.ID;
  153.     }
  154.     
  155.     // select rows, given a list of IDs of rows in a table
  156.     function select(table_name, ids) {
  157.         var ID = null, results = [], row = null;
  158.         for(var i=0; i<ids.length; i++) {
  159.             ID = ids[i];
  160.             row = db.data[table_name][ID];
  161.             results.push( clone(row) );
  162.         }
  163.         return results;
  164.     }
  165.     
  166.     // select rows in a table by field-value pairs, returns the IDs of matches
  167.     function queryByValues(table_name, data, limit, start) {
  168.         var result_ids = [],
  169.             exists = false,
  170.             row = null;
  171.             start_n = 0;
  172.  
  173.         // loop through all the records in the table, looking for matches
  174.         for(var ID in db.data[table_name]) {
  175.             if( !db.data[table_name].hasOwnProperty(ID) ) {
  176.                 continue;
  177.             }
  178.  
  179.             row = db.data[table_name][ID];
  180.             exists = true;
  181.  
  182.             for(var field in data) {
  183.                 if( !data.hasOwnProperty(field) ) {
  184.                     continue;
  185.                 }
  186.  
  187.                 if(typeof data[field] == 'string') {    // if the field is a string, do a case insensitive comparison
  188.                     if( row[field].toString().toLowerCase() != data[field].toString().toLowerCase() ) {
  189.                         exists = false;
  190.                         break;
  191.                     }
  192.                 } else {
  193.                     if( row[field] != data[field] ) {
  194.                         exists = false;
  195.                         break;
  196.                     }
  197.                 }
  198.             }
  199.             if(exists) {
  200.                 if(typeof start === 'number' && start_n < start) {
  201.                     start_n++;
  202.                     continue;
  203.                 }
  204.                 result_ids.push(ID);
  205.             }
  206.             if(result_ids.length == limit) {
  207.                 break;
  208.             }
  209.         }
  210.         return result_ids;
  211.     }
  212.     
  213.     // select rows in a table by a function, returns the IDs of matches
  214.     function queryByFunction(table_name, query_function, limit, start) {
  215.         var result_ids = [],
  216.             exists = false,
  217.             row = null,
  218.             start_n = 0;
  219.  
  220.         // loop through all the records in the table, looking for matches
  221.         for(var ID in db.data[table_name]) {
  222.             if( !db.data[table_name].hasOwnProperty(ID) ) {
  223.                 continue;
  224.             }
  225.  
  226.             row = db.data[table_name][ID];
  227.  
  228.             if( query_function( clone(row) ) == true ) {    // it's a match if the supplied conditional function is satisfied
  229.                 if(typeof start === 'number' && start_n < start) {
  230.                     start_n++;
  231.                     continue;
  232.                 }
  233.                 result_ids.push(ID);
  234.             }
  235.             if(result_ids.length == limit) {
  236.                 break;
  237.             }
  238.         }
  239.         return result_ids;
  240.     }
  241.     
  242.     // return all the IDs in a table
  243.     function getIDs(table_name, limit, start) {
  244.         var result_ids = [],
  245.             start_n = 0;
  246.  
  247.         for(var ID in db.data[table_name]) {
  248.             if( db.data[table_name].hasOwnProperty(ID) ) {
  249.                 if(typeof start === 'number' && start_n < start) {
  250.                     start_n++;
  251.                     continue;
  252.                 }
  253.  
  254.                 result_ids.push(ID);
  255.  
  256.                 if(result_ids.length == limit) {
  257.                     break;
  258.                 }
  259.             }
  260.         }
  261.         return result_ids;
  262.     }
  263.     
  264.     // delete rows, given a list of their IDs in a table
  265.     function deleteRows(table_name, ids) {
  266.         for(var i=0; i<ids.length; i++) {
  267.             if( db.data[table_name].hasOwnProperty(ids[i]) ) {
  268.                 delete db.data[table_name][ ids[i] ];
  269.             }
  270.         }
  271.         return ids.length;
  272.     }
  273.     
  274.     // update rows
  275.     function update(table_name, ids, update_function) {
  276.         var ID = '', num = 0;
  277.  
  278.         for(var i=0; i<ids.length; i++) {
  279.             ID = ids[i];
  280.  
  281.             var updated_data = update_function( clone(db.data[table_name][ID]) );
  282.  
  283.             if(updated_data) {
  284.                 delete updated_data['ID']; // no updates possible to ID
  285.  
  286.                 var new_data = db.data[table_name][ID];                
  287.                 // merge updated data with existing data
  288.                 for(var field in updated_data) {
  289.                     if( updated_data.hasOwnProperty(field) ) {
  290.                         new_data[field] = updated_data[field];
  291.                     }
  292.                 }
  293.  
  294.                 db.data[table_name][ID] = validFields(table_name, new_data);
  295.                 num++;
  296.             }
  297.         }
  298.         return num;
  299.     }
  300.     
  301.  
  302.  
  303.     // commit the database to localStorage
  304.     function commit() {
  305.         try {
  306.             storage[db_id] = JSON.stringify(db);
  307.             return true;
  308.         } catch(e) {
  309.             return false;
  310.         }
  311.     }
  312.     
  313.     // serialize the database
  314.     function serialize() {
  315.         return JSON.stringify(db);
  316.     }
  317.     
  318.     // throw an error
  319.     function error(msg) {
  320.         throw new Error(msg);
  321.     }
  322.     
  323.     // clone an object
  324.     function clone(obj) {
  325.         var new_obj = {};
  326.         for(var key in obj) {
  327.             if( obj.hasOwnProperty(key) ) {
  328.                 new_obj[key] = obj[key];
  329.             }
  330.         }
  331.         return new_obj;
  332.     }
  333.     
  334.     // validate db, table, field names (alpha-numeric only)
  335.     function validateName(name) {
  336.         return name.toString().match(/[^a-z_0-9]/ig) ? false : true;
  337.     }
  338.     
  339.     // given a data list, only retain valid fields in a table
  340.     function validFields(table_name, data) {
  341.         var field = '', new_data = {};
  342.  
  343.         for(var i=0; i<db.tables[table_name].fields.length; i++) {
  344.             field = db.tables[table_name].fields[i];
  345.             
  346.             if ((data[field] !== null && data[field] !== undefined)) {
  347.                 new_data[field] = data[field];
  348.             }
  349.         }
  350.         return new_data;
  351.     }
  352.     
  353.     // given a data list, populate with valid field names of a table
  354.     function validateData(table_name, data) {
  355.         var field = '', new_data = {};
  356.         for(var i=0; i<db.tables[table_name].fields.length; i++) {
  357.             field = db.tables[table_name].fields[i];
  358.             new_data[field] = (data[field] === null || data[field] === undefined) ? null : data[field];
  359.         }
  360.         return new_data;
  361.     }
  362.     
  363.  
  364.  
  365.     // ______________________ public methods
  366.  
  367.     return {
  368.         // commit the database to localStorage
  369.         commit: function() {
  370.             return commit();
  371.         },
  372.         
  373.         // is this instance a newly created database?
  374.         isNew: function() {
  375.             return db_new;
  376.         },
  377.         
  378.         // delete the database
  379.         drop: function() {
  380.             drop();
  381.         },
  382.         
  383.         // serialize the database
  384.         serialize: function() {
  385.             return serialize();
  386.         },
  387.         
  388.         // check whether a table exists
  389.         tableExists: function(table_name) {
  390.             return tableExists(table_name);
  391.         },
  392.  
  393.         // list of keys in a table
  394.         tableFields: function(table_name) {
  395.             return tableFields(table_name);
  396.         },
  397.         
  398.         // number of tables in the database
  399.         tableCount: function() {
  400.             return tableCount();
  401.         },
  402.  
  403.         columnExists: function(table_name, field_name){
  404.             return columnExists(table_name, field_name);
  405.         },
  406.         
  407.         // create a table
  408.         createTable: function(table_name, fields) {
  409.             var result = false;
  410.             if(!validateName(table_name)) {
  411.                 error("The database name '" + table_name + "'" + " contains invalid characters.");
  412.             } else if(this.tableExists(table_name)) {
  413.                 error("The table name '" + table_name + "' already exists.");
  414.             } else {
  415.                 // make sure field names are valid
  416.                 var is_valid = true;
  417.                 for(var i=0; i<fields.length; i++) {
  418.                     if(!validateName(fields[i])) {
  419.                         is_valid = false;
  420.                         break;
  421.                     }
  422.                 }
  423.                 
  424.                 if(is_valid) {
  425.                     // cannot use indexOf due to <IE9 incompatibility
  426.                     // de-duplicate the field list
  427.                     var fields_literal = {};
  428.                     for(var i=0; i<fields.length; i++) {
  429.                         fields_literal[ fields[i] ] = true;
  430.                     }
  431.                     delete fields_literal['ID']; // ID is a reserved field name
  432.  
  433.                     fields = ['ID'];
  434.                     for(var field in fields_literal) {
  435.                         if( fields_literal.hasOwnProperty(field) ) {
  436.                             fields.push(field);
  437.                         }
  438.                     }
  439.  
  440.                     createTable(table_name, fields);
  441.                     result = true;
  442.                 } else {
  443.                     error("One or more field names in the table definition contains invalid characters.");
  444.                 }
  445.             }
  446.  
  447.             return result;
  448.         },
  449.         
  450.         // Create a table using array of Objects @ [{k:v,k:v},{k:v,k:v},etc]
  451.         createTableWithData: function(table_name, data) {
  452.             if(typeof data !== 'object' || !data.length || data.length < 1) {
  453.                 error("Data supplied isn't in object form. Example: [{k:v,k:v},{k:v,k:v} ..]");
  454.             }
  455.  
  456.             var fields = Object.keys(data[0]);
  457.             
  458.             // create the table
  459.             if( this.createTable(table_name, fields) ) {
  460.                 this.commit();
  461.  
  462.                 // populate
  463.                 for (var i=0; i<data.length; i++) {
  464.                     if( !insert(table_name, data[i]) ) {
  465.                         error("Failed to insert record: ["+JSON.stringify(data[i])+"]");
  466.                     }
  467.                 }
  468.                 this.commit();
  469.             }
  470.             return true;
  471.         },
  472.         
  473.         // drop a table
  474.         dropTable: function(table_name) {
  475.             tableExistsWarn(table_name);
  476.             dropTable(table_name);
  477.         },
  478.         
  479.         // empty a table
  480.         truncate: function(table_name) {
  481.             tableExistsWarn(table_name);
  482.             truncate(table_name);
  483.         },
  484.  
  485.         // alter a table        
  486.         alterTable: function(table_name, new_fields, default_values)
  487.         {
  488.             var result = false;
  489.             if(!validateName(table_name)) {
  490.                 error("The database name '" + table_name + "'" + " contains invalid characters.");
  491.             } else {
  492.                 if(typeof new_fields == "object")
  493.                 {
  494.                 
  495.                     // make sure field names are valid
  496.                     var is_valid = true;
  497.                     for(var i=0; i<new_fields.length; i++) {
  498.                         if(!validateName(new_fields[i])) {
  499.                             is_valid = false;
  500.                             break;
  501.                         }
  502.                     }
  503.                     
  504.                     if(is_valid) {
  505.                         // cannot use indexOf due to <IE9 incompatibility
  506.                         // de-duplicate the field list
  507.                         var fields_literal = {};
  508.                         for(var i=0; i<new_fields.length; i++) {
  509.                             fields_literal[ new_fields[i] ] = true;
  510.                         }
  511.                         delete fields_literal['ID']; // ID is a reserved field name
  512.     
  513.                         new_fields = [];
  514.                         for(var field in fields_literal) {
  515.                             if( fields_literal.hasOwnProperty(field) ) {
  516.                                 new_fields.push(field);
  517.                             }
  518.                         }
  519.     
  520.                         alterTable(table_name, new_fields, default_values);
  521.                         result = true;
  522.                     } else {
  523.                         error("One or more field names in the table definition contains invalid characters.");
  524.                     }
  525.                 }
  526.                 else if(typeof new_fields == "string")
  527.                 {
  528.                     if(validateName(new_fields)) {
  529.                         var new_fields_array = [];
  530.                         new_fields_array.push(new_fields);
  531.                         alterTable(table_name, new_fields_array, default_values);
  532.                         result = true;
  533.                     }
  534.                     else{
  535.                         error("One or more field names in the table definition contains invalid characters.");
  536.                     }
  537.                 }
  538.             }
  539.  
  540.             return result;
  541.         },
  542.         
  543.         // number of rows in a table
  544.         rowCount: function(table_name) {
  545.             tableExistsWarn(table_name);
  546.             return rowCount(table_name);
  547.         },
  548.         
  549.         // insert a row
  550.         insert: function(table_name, data) {
  551.             tableExistsWarn(table_name);
  552.             return insert(table_name, validateData(table_name, data) );
  553.         },
  554.  
  555.         // insert or update based on a given condition
  556.         insertOrUpdate: function(table_name, query, data) {
  557.             tableExistsWarn(table_name);
  558.  
  559.             var result_ids = [];
  560.             if(!query) {
  561.                 result_ids = getIDs(table_name);                // there is no query. applies to all records
  562.             } else if(typeof query == 'object') {                // the query has key-value pairs provided
  563.                 result_ids = queryByValues(table_name, validFields(table_name, query));
  564.             } else if(typeof query == 'function') {                // the query has a conditional map function provided
  565.                 result_ids = queryByFunction(table_name, query);
  566.             }
  567.  
  568.             // no existing records matched, so insert a new row
  569.             if(result_ids.length == 0) {
  570.                 return insert(table_name, validateData(table_name, data) );
  571.             } else {
  572.                 var ids = [];
  573.                 for(var n=0; n<result_ids.length; n++) {
  574.                     update(table_name, result_ids, function(o) {
  575.                         ids.push(o.ID);
  576.                         return data;
  577.                     });
  578.                 }
  579.  
  580.                 return ids;
  581.             }
  582.         },
  583.         
  584.         // update rows
  585.         update: function(table_name, query, update_function) {
  586.             tableExistsWarn(table_name);
  587.  
  588.             var result_ids = [];
  589.             if(!query) {
  590.                 result_ids = getIDs(table_name);                // there is no query. applies to all records
  591.             } else if(typeof query == 'object') {                // the query has key-value pairs provided
  592.                 result_ids = queryByValues(table_name, validFields(table_name, query));
  593.             } else if(typeof query == 'function') {                // the query has a conditional map function provided
  594.                 result_ids = queryByFunction(table_name, query);
  595.             }
  596.             return update(table_name, result_ids, update_function);
  597.         },
  598.  
  599.         // select rows
  600.         query: function(table_name, query, limit, start) {
  601.             tableExistsWarn(table_name);
  602.             
  603.             var result_ids = [];
  604.             if(!query) {
  605.                 result_ids = getIDs(table_name, limit, start); // no conditions given, return all records
  606.             } else if(typeof query == 'object') {            // the query has key-value pairs provided
  607.                 result_ids = queryByValues(table_name, validFields(table_name, query), limit, start);
  608.             } else if(typeof query == 'function') {        // the query has a conditional map function provided
  609.                 result_ids = queryByFunction(table_name, query, limit, start);
  610.             }
  611.             return select(table_name, result_ids, limit);
  612.         },
  613.  
  614.         // delete rows
  615.         deleteRows: function(table_name, query) {
  616.             tableExistsWarn(table_name);
  617.  
  618.             var result_ids = [];
  619.             if(!query) {
  620.                 result_ids = getIDs(table_name);
  621.             } else if(typeof query == 'object') {
  622.                 result_ids = queryByValues(table_name, validFields(table_name, query));
  623.             } else if(typeof query == 'function') {
  624.                 result_ids = queryByFunction(table_name, query);
  625.             }
  626.             return deleteRows(table_name, result_ids);
  627.         }
  628.     }
  629. }
  630.