diff --git a/README.md b/README.md index 9ba27d7..75ce2c5 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,8 @@ the structured data is stored as serialized JSON in localStorage or sessionStora # Installation `bower install localstoragedb` +`jspm install github:uelarn/localstoragedb` + # Run Test Cases ```shell diff --git a/dist/amd/localStorageDB.d.ts b/dist/amd/localStorageDB.d.ts new file mode 100644 index 0000000..3667b36 --- /dev/null +++ b/dist/amd/localStorageDB.d.ts @@ -0,0 +1,103 @@ +/** + * Created by [BombSquad Inc](http://www.bmbsqd.com) + * User: Andy Hawkins + * Date: 6/29/15 + * Time: 11:19 AM + */ +// uelarn 11.10.15: Modified to support amd with jspm package manager, + params in queryAll optional + +declare module 'localStorageDB' { + + export type localStorageDB_callback = (object: localStorageDB_fields) => localStorageDB_dynamicFields; + export type localStorageDB_callbackFilter = (object: localStorageDB_fields) => boolean; + + export class localStorageDB { + constructor(database_name: string, storage_engine?: string); // Constructor: storage_engine can either be localStorage (default) or sessionStorage + isNew(): boolean; // Returns true if a database was created at the time of initialisation with the constructor + drop(): void; // Deletes a database, and purges it from localStorage + tableCount(): number; // Returns the number of tables in a database + commit(): boolean; // Commits the database to localStorage. Returns true if successful, and false otherwise (highly unlikely) + serialize(): string; // Returns the entire database as serialized JSON + tableExists(table: string): boolean; // Checks whether a table exists in the database + tableFields(table: string): string[]; // Returns the list of fields of a table + createTable(table: string, fields: string[]); // Creates a table - fields is an array of string fieldnames. 'ID' is a reserved fieldname. + createTableWithData(table: string, rows: { [T: string]: any }[]); + /* + Creates a table and populates it + rows is an array of object literals where each object represents a record + [{field1: val, field2: val}, {field1: val, field2: val}] + */ + alterTable(table: string, new_fields: string[] | string, default_values: localStorageDB_dynamicFields | string); + /* + Alter a table + - new_fields can be a array of columns OR a string of single column. + - default_values (optional) can be a object of column's default values OR a default value string for single column for existing rows. + */ + dropTable(table: string): void; // Deletes a table from the database + truncate(table: string): void; // Empties all records in a table and resets the internal auto increment ID to 0 + columnExists(table: string, field: string): boolean; // Checks whether a column exists in database table. + rowCount(table: string): number; // Returns the number of rows in a table + insert(table: string, data: { [T: string]: any }): number; + /* + Inserts a row into a table and returns its numerical ID + - data is an object literal with field-values + every row is assigned an auto-incremented numerical ID automatically + */ + query(table: string, query?: { [T: string]: any }, limit?: number, start?: number, sort?: any): localStorageDB_fields[]; + /* DEPRECATED + Returns an array of rows (object literals) from a table matching the query. + - query is either an object literal or null. If query is not supplied, all rows are returned + - limit is the maximum number of rows to be returned + - start is the number of rows to be skipped from the beginning (offset) + - sort is an array of sort conditions, each one of which is an array in itself with two values + - distinct is an array of fields whose values have to be unique in the returned rows + Every returned row will have it's internal auto-incremented id assigned to the variable ID + */ + queryAll(table: string, params?: localStorageDB_queryParams): localStorageDB_fields[]; + /* + Returns an array of rows (object literals) from a table matching the query. + - query is either an object literal or null. If query is not supplied, all rows are returned + - limit is the maximum number of rows to be returned + - start is the number of rows to be skipped from the beginning (offset) + - sort is an array of sort conditions, each one of which is an array in itself with two values + - distinct is an array of fields whose values have to be unique in the returned rows + Every returned row will have it's internal auto-incremented id assigned to the variable ID + */ + update(table: string, query: localStorageDB_dynamicFields | localStorageDB_callbackFilter, update?: localStorageDB_callback): number; + /* + Updates existing records in a table matching query, and returns the number of rows affected + - query is an object literal or a function. If query is not supplied, all rows are updated + - update_function is a function that returns an object literal with the updated values + */ + insertOrUpdate(table: string, query: localStorageDB_dynamicFields | localStorageDB_callbackFilter, data: localStorageDB_fields): number; + /* + Inserts a row into a table if the given query matches no results, or updates the rows matching the query. + - query is either an object literal, function, or null. + - data is an object literal with field-values + Returns the numerical ID if a new row was inserted, or an array of IDs if rows were updated + */ + deleteRows(table: string, query: localStorageDB_dynamicFields | localStorageDB_callbackFilter): number; + /* + Deletes rows from a table matching query, and returns the number of rows deleted + - query is either an object literal or a function. If query is not supplied, all rows are deleted + */ +} + + export interface localStorageDB_fields extends localStorageDB_dynamicFields { + ID: number; +} + + export interface localStorageDB_dynamicFields { + [T: string]: any; + } + + export interface localStorageDB_queryParams { + query?: { [T: string]: any }; // - query is either an object literal or null. If query is not supplied, all rows are returned + limit?: number; // - limit is the maximum number of rows to be returned + start?: number; // - start is the number of rows to be skipped from the beginning (offset) + sort?: { [T: string]: any }[]; // - sort is an array of sort conditions, each one of which is an array in itself with two values + distinct?: string[]; // - distinct is an array of fields whose values have to be unique in the returned rows + } + + //export = localStorageDB; +} \ No newline at end of file diff --git a/dist/amd/localstoragedb.js b/dist/amd/localstoragedb.js new file mode 100644 index 0000000..c75aec9 --- /dev/null +++ b/dist/amd/localstoragedb.js @@ -0,0 +1,702 @@ +/* + Kailash Nadh (http://nadh.in) + + localStorageDB v 2.3.1 + A simple database layer for localStorage + + v 2.3.1 Mar 2015 + v 2.3 Feb 2014 Contribution: Christian Kellner (http://orange-coding.net) + v 2.2 Jan 2014 Contribution: Andy Hawkins (http://a904guy.com) + v 2.1 Nov 2013 + v 2.0 June 2013 + v 1.9 Nov 2012 + + License : MIT License + + uelarn 11.10.15: Modified to support amd with jspm package manager + +*/ +define(['exports'], function (exports) { + 'use strict'; + + exports.__esModule = true; + + //!(function (_global, undefined) { + var _lsDB = + + function localStorageDB(db_name, engine) { + var db_prefix = 'db_', + db_id = db_prefix + db_name, + db_new = false, // this flag determines whether a new database was created during an object initialisation + db = null; + + try { + var storage = (engine == sessionStorage ? sessionStorage : localStorage); + } catch (e) { // ie8 hack + var storage = engine; + } + + // if the database doesn't exist, create it + db = storage[db_id]; + if (!(db && (db = JSON.parse(db)) && db.tables && db.data)) { + if (!validateName(db_name)) { + error("The name '" + db_name + "' contains invalid characters"); + } else { + db = { tables: {}, data: {} }; + commit(); + db_new = true; + } + } + + + // ______________________ private methods + + // _________ database functions + // drop the database + function drop() { + if (storage.hasOwnProperty(db_id)) { + delete storage[db_id]; + } + db = null; + } + + // number of tables in the database + function tableCount() { + var count = 0; + for (var table in db.tables) { + if (db.tables.hasOwnProperty(table)) { + count++; + } + } + return count; + } + + // _________ table functions + + // returns all fields in a table. + function tableFields(table_name) { + return db.tables[table_name].fields; + } + + // check whether a table exists + function tableExists(table_name) { + return db.tables[table_name] ? true : false; + } + + // check whether a table exists, and if not, throw an error + function tableExistsWarn(table_name) { + if (!tableExists(table_name)) { + error("The table '" + table_name + "' does not exist"); + } + } + + // check whether a table column exists + function columnExists(table_name, field_name) { + var exists = false; + var table_fields = db.tables[table_name].fields; + for (var field in table_fields) { + if (table_fields[field] == field_name) { + exists = true; + break; + } + } + return exists; + } + + // create a table + function createTable(table_name, fields) { + db.tables[table_name] = { fields: fields, auto_increment: 1 }; + db.data[table_name] = {}; + } + + // drop a table + function dropTable(table_name) { + delete db.tables[table_name]; + delete db.data[table_name]; + } + + // empty a table + function truncate(table_name) { + db.tables[table_name].auto_increment = 1; + db.data[table_name] = {}; + } + + //alter a table + function alterTable(table_name, new_fields, default_values) { + db.tables[table_name].fields = db.tables[table_name].fields.concat(new_fields); + + // insert default values in existing table + if (typeof default_values != "undefined") { + // loop through all the records in the table + for (var ID in db.data[table_name]) { + if (!db.data[table_name].hasOwnProperty(ID)) { + continue; + } + for (var field in new_fields) { + if (typeof default_values == "object") { + db.data[table_name][ID][new_fields[field]] = default_values[new_fields[field]]; + } else { + db.data[table_name][ID][new_fields[field]] = default_values; + } + } + } + } + } + + // number of rows in a table + function rowCount(table_name) { + var count = 0; + for (var ID in db.data[table_name]) { + if (db.data[table_name].hasOwnProperty(ID)) { + count++; + } + } + return count; + } + + // insert a new row + function insert(table_name, data) { + data.ID = db.tables[table_name].auto_increment; + db.data[table_name][db.tables[table_name].auto_increment] = data; + db.tables[table_name].auto_increment++; + return data.ID; + } + + // select rows, given a list of IDs of rows in a table + function select(table_name, ids, start, limit, sort, distinct) { + var ID = null, results = [], row = null; + + for (var i = 0; i < ids.length; i++) { + ID = ids[i]; + row = db.data[table_name][ID]; + results.push(clone(row)); + } + + // there are sorting params + if (sort && sort instanceof Array) { + for (var i = 0; i < sort.length; i++) { + results.sort(sort_results(sort[i][0], sort[i].length > 1 ? sort[i][1] : null)); + } + } + + // distinct params + if (distinct && distinct instanceof Array) { + for (var j = 0; j < distinct.length; j++) { + var seen = {}, d = distinct[j]; + + for (var i = 0; i < results.length; i++) { + if (results[i] === undefined) { + continue; + } + + if (results[i].hasOwnProperty(d) && seen.hasOwnProperty(results[i][d])) { + delete (results[i]); + } else { + seen[results[i][d]] = 1; + } + } + } + + // can't use .filter(ie8) + var new_results = []; + for (var i = 0; i < results.length; i++) { + if (results[i] !== undefined) { + new_results.push(results[i]); + } + } + + results = new_results; + } + + // limit and offset + start = start && typeof start === "number" ? start : null; + limit = limit && typeof limit === "number" ? limit : null; + + if (start && limit) { + results = results.slice(start, start + limit); + } else if (start) { + results = results.slice(start); + } else if (limit) { + results = results.slice(start, limit); + } + + return results; + } + + // sort a result set + function sort_results(field, order) { + return function (x, y) { + // case insensitive comparison for string values + var v1 = typeof (x[field]) === "string" ? x[field].toLowerCase() : x[field], + v2 = typeof (y[field]) === "string" ? y[field].toLowerCase() : y[field]; + + if (order === "DESC") { + return v1 == v2 ? 0 : (v1 < v2 ? 1 : -1); + } else { + return v1 == v2 ? 0 : (v1 > v2 ? 1 : -1); + } + }; + } + + // select rows in a table by field-value pairs, returns the IDs of matches + function queryByValues(table_name, data) { + var result_ids = [], + exists = false, + row = null; + + // loop through all the records in the table, looking for matches + for (var ID in db.data[table_name]) { + if (!db.data[table_name].hasOwnProperty(ID)) { + continue; + } + + row = db.data[table_name][ID]; + exists = true; + + for (var field in data) { + if (!data.hasOwnProperty(field)) { + continue; + } + + if (typeof data[field] == 'string') { // if the field is a string, do a case insensitive comparison + if (row[field].toString().toLowerCase() != data[field].toString().toLowerCase()) { + exists = false; + break; + } + } else { + if (row[field] != data[field]) { + exists = false; + break; + } + } + } + if (exists) { + result_ids.push(ID); + } + } + + return result_ids; + } + + // select rows in a table by a function, returns the IDs of matches + function queryByFunction(table_name, query_function) { + var result_ids = [], + exists = false, + row = null; + + // loop through all the records in the table, looking for matches + for (var ID in db.data[table_name]) { + if (!db.data[table_name].hasOwnProperty(ID)) { + continue; + } + + row = db.data[table_name][ID]; + + if (query_function(clone(row)) == true) { // it's a match if the supplied conditional function is satisfied + result_ids.push(ID); + } + } + + return result_ids; + } + + // return all the IDs in a table + function getIDs(table_name) { + var result_ids = []; + + for (var ID in db.data[table_name]) { + if (db.data[table_name].hasOwnProperty(ID)) { + result_ids.push(ID); + } + } + return result_ids; + } + + // delete rows, given a list of their IDs in a table + function deleteRows(table_name, ids) { + for (var i = 0; i < ids.length; i++) { + if (db.data[table_name].hasOwnProperty(ids[i])) { + delete db.data[table_name][ids[i]]; + } + } + return ids.length; + } + + // update rows + function update(table_name, ids, update_function) { + var ID = '', num = 0; + + for (var i = 0; i < ids.length; i++) { + ID = ids[i]; + + var updated_data = update_function(clone(db.data[table_name][ID])); + + if (updated_data) { + delete updated_data['ID']; // no updates possible to ID + + var new_data = db.data[table_name][ID]; + // merge updated data with existing data + for (var field in updated_data) { + if (updated_data.hasOwnProperty(field)) { + new_data[field] = updated_data[field]; + } + } + + db.data[table_name][ID] = validFields(table_name, new_data); + num++; + } + } + return num; + } + + // commit the database to localStorage + function commit() { + try { + storage.setItem(db_id, JSON.stringify(db)); + return true; + } catch (e) { + return false; + } + } + + // serialize the database + function serialize() { + return JSON.stringify(db); + } + + // throw an error + function error(msg) { + throw new Error(msg); + } + + // clone an object + function clone(obj) { + var new_obj = {}; + for (var key in obj) { + if (obj.hasOwnProperty(key)) { + new_obj[key] = obj[key]; + } + } + return new_obj; + } + + // validate db, table, field names (alpha-numeric only) + function validateName(name) { + return name.toString().match(/[^a-z_0-9]/ig) ? false : true; + } + + // given a data list, only retain valid fields in a table + function validFields(table_name, data) { + var field = '', new_data = {}; + + for (var i = 0; i < db.tables[table_name].fields.length; i++) { + field = db.tables[table_name].fields[i]; + + if (data[field] !== undefined) { + new_data[field] = data[field]; + } + } + return new_data; + } + + // given a data list, populate with valid field names of a table + function validateData(table_name, data) { + var field = '', new_data = {}; + for (var i = 0; i < db.tables[table_name].fields.length; i++) { + field = db.tables[table_name].fields[i]; + new_data[field] = (data[field] === null || data[field] === undefined) ? null : data[field]; + } + return new_data; + } + + // ______________________ public methods + + return { + // commit the database to localStorage + commit: function () { + return commit(); + }, + + // is this instance a newly created database? + isNew: function () { + return db_new; + }, + + // delete the database + drop: function () { + drop(); + }, + + // serialize the database + serialize: function () { + return serialize(); + }, + + // check whether a table exists + tableExists: function (table_name) { + return tableExists(table_name); + }, + + // list of keys in a table + tableFields: function (table_name) { + return tableFields(table_name); + }, + + // number of tables in the database + tableCount: function () { + return tableCount(); + }, + + columnExists: function (table_name, field_name) { + return columnExists(table_name, field_name); + }, + + // create a table + createTable: function (table_name, fields) { + var result = false; + if (!validateName(table_name)) { + error("The database name '" + table_name + "' contains invalid characters."); + } else if (this.tableExists(table_name)) { + error("The table name '" + table_name + "' already exists."); + } else { + // make sure field names are valid + var is_valid = true; + for (var i = 0; i < fields.length; i++) { + if (!validateName(fields[i])) { + is_valid = false; + break; + } + } + + if (is_valid) { + // cannot use indexOf due to ", + "main": "localstoragedb.js", + "repository": { + "type": "git", + "url": "https://github.com/knadh/localStorageDB" + }, + "jspm": { + "main": "localStorageDB", + "format": "amd", + "directories": { + "lib": "dist/amd" + } + } +}